Skip to content

Add Create Image example to Demo app #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Demo/App/APIProvidedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import SwiftUI
struct APIProvidedView: View {
@Binding var apiKey: String
@StateObject var chatStore: ChatStore
@StateObject var imageStore: ImageStore
@StateObject var miscStore: MiscStore
@State var isShowingAPIConfigModal: Bool = true

Expand All @@ -29,6 +30,11 @@ struct APIProvidedView: View {
idProvider: idProvider
)
)
self._imageStore = StateObject(
wrappedValue: ImageStore(
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
)
)
self._miscStore = StateObject(
wrappedValue: MiscStore(
openAIClient: OpenAI(apiToken: apiKey.wrappedValue)
Expand All @@ -39,6 +45,7 @@ struct APIProvidedView: View {
var body: some View {
ContentView(
chatStore: chatStore,
imageStore: imageStore,
miscStore: miscStore
)
.onChange(of: apiKey) { newApiKey in
Expand Down
9 changes: 2 additions & 7 deletions Demo/App/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SwiftUI

struct ContentView: View {
@ObservedObject var chatStore: ChatStore
@ObservedObject var imageStore: ImageStore
@ObservedObject var miscStore: MiscStore
@State private var selectedTab = 0
@Environment(\.idProviderValue) var idProvider
Expand All @@ -33,6 +34,7 @@ struct ContentView: View {
.tag(1)

ImageView(
store: imageStore
)
.tabItem {
Label("Image", systemImage: "photo")
Expand Down Expand Up @@ -63,10 +65,3 @@ struct TranscribeView: View {
.font(.largeTitle)
}
}

struct ImageView: View {
var body: some View {
Text("Image: TBD")
.font(.largeTitle)
}
}
33 changes: 33 additions & 0 deletions Demo/DemoChat/Sources/ImageStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// ImageStore.swift
// DemoChat
//
// Created by Aled Samuel on 24/04/2023.
//

import Foundation
import OpenAI

public final class ImageStore: ObservableObject {
public var openAIClient: OpenAIProtocol

@Published var images: [ImagesResult.URLResult] = []

public init(
openAIClient: OpenAIProtocol
) {
self.openAIClient = openAIClient
}

@MainActor
func images(query: ImagesQuery) async {
images.removeAll()
do {
let response = try await openAIClient.images(query: query)
images = response.data
} catch {
// TODO: Better error handling
print(error.localizedDescription)
}
}
}
75 changes: 75 additions & 0 deletions Demo/DemoChat/Sources/UI/Images/ImageCreationView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// ImageCreationView.swift
// DemoChat
//
// Created by Aled Samuel on 24/04/2023.
//

import SwiftUI
import OpenAI

public struct ImageCreationView: View {
@ObservedObject var store: ImageStore

@State private var prompt: String = ""
@State private var n: Int = 1
@State private var size: String

private var sizes = ["256x256", "512x512", "1024x1024"]

public init(store: ImageStore) {
self.store = store
size = sizes[0]
}

public var body: some View {
List {
Section {
HStack {
Text("Prompt")
Spacer()
TextEditor(text: $prompt)
.multilineTextAlignment(.trailing)
}
HStack {
Stepper("Amount: \(n)", value: $n, in: 1...10)
}
HStack {
Picker("Size", selection: $size) {
ForEach(sizes, id: \.self) {
Text($0)
}
}
}
}
Section {
HStack {
Button("Create Image" + (n == 1 ? "" : "s")) {
Task {
let query = ImagesQuery(prompt: prompt, n: n, size: size)
await store.images(query: query)
}
}
.foregroundColor(.accentColor)
Spacer()
}
}
if !$store.images.isEmpty {
Section("Images") {
ForEach($store.images, id: \.self) { image in
let urlString = image.wrappedValue.url
if let imageURL = URL(string: urlString), UIApplication.shared.canOpenURL(imageURL) {
LinkPreview(previewURL: imageURL)
.aspectRatio(contentMode: .fit)
} else {
Text(urlString)
.foregroundStyle(.secondary)
}
}
}
}
}
.listStyle(.insetGrouped)
.navigationTitle("Create Image")
}
}
63 changes: 63 additions & 0 deletions Demo/DemoChat/Sources/UI/Images/ImageView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// ImageView.swift
// DemoChat
//
// Created by Aled Samuel on 24/04/2023.
//

import SwiftUI

public struct ImageView: View {
@ObservedObject var store: ImageStore

public init(store: ImageStore) {
self.store = store
}

public var body: some View {
NavigationStack {
List {
NavigationLink("Create Image", destination: ImageCreationView(store: store))
NavigationLink("Create Image Edit", destination: ImageEditView(store: store))
.disabled(true)
NavigationLink("Create Image Variation", destination: ImageVariationView(store: store))
.disabled(true)

}
.listStyle(.insetGrouped)
.navigationTitle("Image")
}
}
}

public struct ImageEditView: View {
@ObservedObject var store: ImageStore

public init(store: ImageStore) {
self.store = store
}

public var body: some View {
List {

}
.listStyle(.insetGrouped)
.navigationTitle("Create Image Edit")
}
}

public struct ImageVariationView: View {
@ObservedObject var store: ImageStore

public init(store: ImageStore) {
self.store = store
}

public var body: some View {
List {

}
.listStyle(.insetGrouped)
.navigationTitle("Create Image Variation")
}
}
33 changes: 33 additions & 0 deletions Demo/DemoChat/Sources/UI/Images/LinkPreview.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// LinkPreview.swift
// DemoChat
//
// Created by Aled Samuel on 25/04/2023.
//

import SwiftUI
import LinkPresentation

struct LinkPreview: UIViewRepresentable {
typealias UIViewType = LPLinkView

var previewURL: URL

func makeUIView(context: Context) -> LPLinkView {
LPLinkView(url: previewURL)
}

func updateUIView(_ uiView: UIViewType, context: Context) {
LPMetadataProvider().startFetchingMetadata(for: previewURL) { metadata, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let metadata = metadata else {
print("Metadata missing for \(previewURL.absoluteString)")
return
}
uiView.metadata = metadata
}
}
}
2 changes: 2 additions & 0 deletions Sources/OpenAI/Public/Models/ImagesResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ public struct ImagesResult: Codable, Equatable {
public let created: TimeInterval
public let data: [URLResult]
}

extension ImagesResult.URLResult: Hashable { }