Skip to content

Commit 64849ee

Browse files
authored
Merge pull request MacPaw#63 from SunburstEnzo/demoapp_create_image
Add Create Image example to Demo app
2 parents c55e598 + 97c6101 commit 64849ee

File tree

7 files changed

+215
-7
lines changed

7 files changed

+215
-7
lines changed

Demo/App/APIProvidedView.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import SwiftUI
1212
struct APIProvidedView: View {
1313
@Binding var apiKey: String
1414
@StateObject var chatStore: ChatStore
15+
@StateObject var imageStore: ImageStore
1516
@StateObject var miscStore: MiscStore
1617
@State var isShowingAPIConfigModal: Bool = true
1718

@@ -29,6 +30,11 @@ struct APIProvidedView: View {
2930
idProvider: idProvider
3031
)
3132
)
33+
self._imageStore = StateObject(
34+
wrappedValue: ImageStore(
35+
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
36+
)
37+
)
3238
self._miscStore = StateObject(
3339
wrappedValue: MiscStore(
3440
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
@@ -39,6 +45,7 @@ struct APIProvidedView: View {
3945
var body: some View {
4046
ContentView(
4147
chatStore: chatStore,
48+
imageStore: imageStore,
4249
miscStore: miscStore
4350
)
4451
.onChange(of: apiKey) { newApiKey in

Demo/App/ContentView.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import SwiftUI
1111

1212
struct ContentView: View {
1313
@ObservedObject var chatStore: ChatStore
14+
@ObservedObject var imageStore: ImageStore
1415
@ObservedObject var miscStore: MiscStore
1516
@State private var selectedTab = 0
1617
@Environment(\.idProviderValue) var idProvider
@@ -33,6 +34,7 @@ struct ContentView: View {
3334
.tag(1)
3435

3536
ImageView(
37+
store: imageStore
3638
)
3739
.tabItem {
3840
Label("Image", systemImage: "photo")
@@ -63,10 +65,3 @@ struct TranscribeView: View {
6365
.font(.largeTitle)
6466
}
6567
}
66-
67-
struct ImageView: View {
68-
var body: some View {
69-
Text("Image: TBD")
70-
.font(.largeTitle)
71-
}
72-
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// ImageStore.swift
3+
// DemoChat
4+
//
5+
// Created by Aled Samuel on 24/04/2023.
6+
//
7+
8+
import Foundation
9+
import OpenAI
10+
11+
public final class ImageStore: ObservableObject {
12+
public var openAIClient: OpenAIProtocol
13+
14+
@Published var images: [ImagesResult.URLResult] = []
15+
16+
public init(
17+
openAIClient: OpenAIProtocol
18+
) {
19+
self.openAIClient = openAIClient
20+
}
21+
22+
@MainActor
23+
func images(query: ImagesQuery) async {
24+
images.removeAll()
25+
do {
26+
let response = try await openAIClient.images(query: query)
27+
images = response.data
28+
} catch {
29+
// TODO: Better error handling
30+
print(error.localizedDescription)
31+
}
32+
}
33+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// ImageCreationView.swift
3+
// DemoChat
4+
//
5+
// Created by Aled Samuel on 24/04/2023.
6+
//
7+
8+
import SwiftUI
9+
import OpenAI
10+
11+
public struct ImageCreationView: View {
12+
@ObservedObject var store: ImageStore
13+
14+
@State private var prompt: String = ""
15+
@State private var n: Int = 1
16+
@State private var size: String
17+
18+
private var sizes = ["256x256", "512x512", "1024x1024"]
19+
20+
public init(store: ImageStore) {
21+
self.store = store
22+
size = sizes[0]
23+
}
24+
25+
public var body: some View {
26+
List {
27+
Section {
28+
HStack {
29+
Text("Prompt")
30+
Spacer()
31+
TextEditor(text: $prompt)
32+
.multilineTextAlignment(.trailing)
33+
}
34+
HStack {
35+
Stepper("Amount: \(n)", value: $n, in: 1...10)
36+
}
37+
HStack {
38+
Picker("Size", selection: $size) {
39+
ForEach(sizes, id: \.self) {
40+
Text($0)
41+
}
42+
}
43+
}
44+
}
45+
Section {
46+
HStack {
47+
Button("Create Image" + (n == 1 ? "" : "s")) {
48+
Task {
49+
let query = ImagesQuery(prompt: prompt, n: n, size: size)
50+
await store.images(query: query)
51+
}
52+
}
53+
.foregroundColor(.accentColor)
54+
Spacer()
55+
}
56+
}
57+
if !$store.images.isEmpty {
58+
Section("Images") {
59+
ForEach($store.images, id: \.self) { image in
60+
let urlString = image.wrappedValue.url
61+
if let imageURL = URL(string: urlString), UIApplication.shared.canOpenURL(imageURL) {
62+
LinkPreview(previewURL: imageURL)
63+
.aspectRatio(contentMode: .fit)
64+
} else {
65+
Text(urlString)
66+
.foregroundStyle(.secondary)
67+
}
68+
}
69+
}
70+
}
71+
}
72+
.listStyle(.insetGrouped)
73+
.navigationTitle("Create Image")
74+
}
75+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// ImageView.swift
3+
// DemoChat
4+
//
5+
// Created by Aled Samuel on 24/04/2023.
6+
//
7+
8+
import SwiftUI
9+
10+
public struct ImageView: View {
11+
@ObservedObject var store: ImageStore
12+
13+
public init(store: ImageStore) {
14+
self.store = store
15+
}
16+
17+
public var body: some View {
18+
NavigationStack {
19+
List {
20+
NavigationLink("Create Image", destination: ImageCreationView(store: store))
21+
NavigationLink("Create Image Edit", destination: ImageEditView(store: store))
22+
.disabled(true)
23+
NavigationLink("Create Image Variation", destination: ImageVariationView(store: store))
24+
.disabled(true)
25+
26+
}
27+
.listStyle(.insetGrouped)
28+
.navigationTitle("Image")
29+
}
30+
}
31+
}
32+
33+
public struct ImageEditView: View {
34+
@ObservedObject var store: ImageStore
35+
36+
public init(store: ImageStore) {
37+
self.store = store
38+
}
39+
40+
public var body: some View {
41+
List {
42+
43+
}
44+
.listStyle(.insetGrouped)
45+
.navigationTitle("Create Image Edit")
46+
}
47+
}
48+
49+
public struct ImageVariationView: View {
50+
@ObservedObject var store: ImageStore
51+
52+
public init(store: ImageStore) {
53+
self.store = store
54+
}
55+
56+
public var body: some View {
57+
List {
58+
59+
}
60+
.listStyle(.insetGrouped)
61+
.navigationTitle("Create Image Variation")
62+
}
63+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// LinkPreview.swift
3+
// DemoChat
4+
//
5+
// Created by Aled Samuel on 25/04/2023.
6+
//
7+
8+
import SwiftUI
9+
import LinkPresentation
10+
11+
struct LinkPreview: UIViewRepresentable {
12+
typealias UIViewType = LPLinkView
13+
14+
var previewURL: URL
15+
16+
func makeUIView(context: Context) -> LPLinkView {
17+
LPLinkView(url: previewURL)
18+
}
19+
20+
func updateUIView(_ uiView: UIViewType, context: Context) {
21+
LPMetadataProvider().startFetchingMetadata(for: previewURL) { metadata, error in
22+
if let error = error {
23+
print(error.localizedDescription)
24+
return
25+
}
26+
guard let metadata = metadata else {
27+
print("Metadata missing for \(previewURL.absoluteString)")
28+
return
29+
}
30+
uiView.metadata = metadata
31+
}
32+
}
33+
}

Sources/OpenAI/Public/Models/ImagesResult.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ public struct ImagesResult: Codable, Equatable {
1515
public let created: TimeInterval
1616
public let data: [URLResult]
1717
}
18+
19+
extension ImagesResult.URLResult: Hashable { }

0 commit comments

Comments
 (0)