├── GeminiMultiturnChat
├── GeminiMultiturnChat
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── gemini-logo.imageset
│ │ │ ├── gemini-logo.png
│ │ │ └── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── Config
│ │ ├── GenerativeAI-Info.plist
│ │ └── APIKey.swift
│ ├── GeminiMultiturnChatApp.swift
│ ├── ChatBubble.swift
│ ├── ChatService.swift
│ ├── MultiturnChatView.swift
│ └── ChatBubbleShape.swift
└── GeminiMultiturnChat.xcodeproj
│ ├── project.xcworkspace
│ └── xcshareddata
│ │ └── swiftpm
│ │ └── Package.resolved
│ └── project.pbxproj
├── .gitignore
├── LICENSE
└── README.md
/GeminiMultiturnChat/GeminiMultiturnChat/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Assets.xcassets/gemini-logo.imageset/gemini-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anupdsouza/ios-gemini-chat/HEAD/GeminiMultiturnChat/GeminiMultiturnChat/Assets.xcassets/gemini-logo.imageset/gemini-logo.png
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Assets.xcassets/gemini-logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "gemini-logo.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Config/GenerativeAI-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | API_KEY
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/GeminiMultiturnChatApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GeminiMultiturnChatApp.swift
3 | // GeminiMultiturnChat
4 | //
5 | // Created by Anup D'Souza
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct GeminiMultiturnChatApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | MultiturnChatView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | GeminiMultiturnChat/GeminiMultiturnChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata
3 | GeminiMultiturnChat/GeminiMultiturnChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
4 | GeminiMultiturnChat/GeminiMultiturnChat.xcodeproj/project.xcworkspace/xcuserdata/adso0001.xcuserdatad/UserInterfaceState.xcuserstate
5 | GeminiMultiturnChat/GeminiMultiturnChat.xcodeproj/xcuserdata/adso0001.xcuserdatad/xcschemes/xcschememanagement.plist
6 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "generative-ai-swift",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/google/generative-ai-swift",
7 | "state" : {
8 | "revision" : "fead3038a70047a72ea2cf39e96d3add71c372de",
9 | "version" : "0.4.4"
10 | }
11 | }
12 | ],
13 | "version" : 2
14 | }
15 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/Config/APIKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APIKey.swift
3 | // GeminiChat
4 | //
5 | // Created by Anup D'Souza
6 | //
7 |
8 | import Foundation
9 |
10 | enum APIKey {
11 | // Fetch the API key from `GenerativeAI-Info.plist`
12 | static var `default`: String {
13 | guard let filePath = Bundle.main.path(forResource: "GenerativeAI-Info", ofType: "plist")
14 | else {
15 | fatalError("Couldn't find file 'GenerativeAI-Info.plist'.")
16 | }
17 | let plist = NSDictionary(contentsOfFile: filePath)
18 | guard let value = plist?.object(forKey: "API_KEY") as? String else {
19 | fatalError("Couldn't find key 'API_KEY' in 'GenerativeAI-Info.plist'.")
20 | }
21 | if value.starts(with: "_") || value.isEmpty {
22 | fatalError(
23 | "Follow the instructions at https://ai.google.dev/tutorials/setup to get an API key."
24 | )
25 | }
26 | return value
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/ChatBubble.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatBubble.swift
3 | // GeminiChat
4 | //
5 | // Created by Anup D'Souza
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct ChatBubble: View where Content: View {
12 | let direction: ChatBubbleShape.Direction
13 | let content: () -> Content
14 | init(direction: ChatBubbleShape.Direction, @ViewBuilder content: @escaping () -> Content) {
15 | self.content = content
16 | self.direction = direction
17 | }
18 |
19 | var body: some View {
20 | HStack {
21 | if direction == .right {
22 | Spacer()
23 | }
24 | content()
25 | .clipShape(ChatBubbleShape(direction: direction))
26 | if direction == .left {
27 | Spacer()
28 | }
29 | }.padding([(direction == .left) ? .leading : .trailing, .top, .bottom], 20)
30 | .padding((direction == .right) ? .leading : .trailing, 50)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Anup D'Souza
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/ChatService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatService.swift
3 | // GeminiChat
4 | //
5 | // Created by Anup D'Souza
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 | import GoogleGenerativeAI
11 |
12 | enum ChatRole {
13 | case user
14 | case model
15 | }
16 |
17 | struct ChatMessage: Identifiable, Equatable {
18 | let id = UUID().uuidString
19 | var role: ChatRole
20 | var message: String
21 | }
22 |
23 | @Observable
24 | class ChatService {
25 | private var chat: Chat?
26 | private(set) var messages = [ChatMessage]()
27 | private(set) var loadingResponse = false
28 |
29 | func sendMessage(_ message: String) {
30 | loadingResponse = true
31 |
32 | if (chat == nil) {
33 | let history: [ModelContent] = messages.map { ModelContent(role: $0.role == .user ? "user" : "model", parts: $0.message)}
34 | chat = GenerativeModel(name: "gemini-pro", apiKey: APIKey.default).startChat(history: history)
35 | }
36 |
37 | // MARK: Add user's message to the list
38 | messages.append(.init(role: .user, message: message))
39 |
40 | Task {
41 | do {
42 | let response = try await chat?.sendMessage(message)
43 |
44 | loadingResponse = false
45 |
46 | guard let text = response?.text else {
47 | messages.append(.init(role: .model, message: "Something went wrong, please try again."))
48 | return
49 | }
50 |
51 | messages.append(.init(role: .model, message: text))
52 | }
53 | catch {
54 | loadingResponse = false
55 | messages.append(.init(role: .model, message: "Something went wrong, please try again."))
56 | }
57 | }
58 | }
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gemini AI chat with SwiftUI
2 | * Multi-turn chat demo application using the `gemini-pro` model.
3 | * For text & image (multimodal) chat with streaming responses, checkout the [`multimodal`](https://github.com/anupdsouza/ios-gemini-chat/tree/multimodal) branch.
4 |
5 | Star the Repo if you found this useful: 
6 | Support Me:
7 |
8 | ## Screenshots
9 |
10 |
11 |
12 |
13 | ## Stay Connected 🤙🏼
14 | -
Star the Repo
15 | -
Subscribe on YouTube
16 | -
Buy Me a Coffee
17 | -
Become a Patron
18 | -
Follow me on X
19 | -
Follow me on GitHub
20 |
21 | Your support makes projects like this possible!
22 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/MultiturnChatView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiturnChatView.swift
3 | // GeminiMultiturnChat
4 | //
5 | // Created by Anup D'Souza
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct MultiturnChatView: View {
11 | @State var textInput = ""
12 | @State var logoAnimating = false
13 | @State var timer: Timer?
14 | @State var chatService = ChatService()
15 |
16 | var body: some View {
17 | VStack {
18 | // MARK: Animating logo
19 | Image(.geminiLogo)
20 | .resizable()
21 | .scaledToFit()
22 | .frame(width: 200)
23 | .opacity(logoAnimating ? 0.5 : 1)
24 | .animation(.easeInOut, value: logoAnimating)
25 |
26 | // MARK: Chat message list
27 | ScrollViewReader(content: { proxy in
28 | ScrollView {
29 | ForEach(chatService.messages) { chatMessage in
30 | // MARK: Chat message view
31 | chatMessageView(chatMessage)
32 | }
33 | }
34 | .onChange(of: chatService.messages) { _, _ in
35 | guard let recentMessage = chatService.messages.last else { return }
36 | DispatchQueue.main.async {
37 | withAnimation {
38 | proxy.scrollTo(recentMessage.id, anchor: .bottom)
39 | }
40 | }
41 | }
42 | .onChange(of: chatService.loadingResponse) { _, newValue in
43 | if newValue {
44 | startLoadingAnimation()
45 | } else {
46 | stopLoadingAnimation()
47 | }
48 | }
49 | })
50 |
51 | // MARK: Input fields
52 | HStack {
53 | TextField("Enter a message...", text: $textInput)
54 | .textFieldStyle(.roundedBorder)
55 | .foregroundColor(.black)
56 | Button(action: sendMessage) {
57 | Image(systemName: "paperplane.fill")
58 | }
59 | }
60 | }
61 | .foregroundStyle(.white)
62 | .padding()
63 | .background {
64 | // MARK: Background
65 | ZStack {
66 | Color.black
67 | }
68 | .ignoresSafeArea()
69 | }
70 | }
71 |
72 | // MARK: Chat message view
73 | @ViewBuilder func chatMessageView(_ message: ChatMessage) -> some View {
74 | ChatBubble(direction: message.role == .model ? .left : .right) {
75 | Text(message.message)
76 | .font(.title3)
77 | .padding(.all, 20)
78 | .foregroundStyle(.white)
79 | .background(message.role == .model ? Color.blue : Color.green)
80 | }
81 | }
82 |
83 | // MARK: Fetch response
84 | func sendMessage() {
85 | chatService.sendMessage(textInput)
86 | textInput = ""
87 | }
88 | // MARK: Response loading animation
89 | func startLoadingAnimation() {
90 | timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { timer in
91 | logoAnimating.toggle()
92 | })
93 | }
94 |
95 | func stopLoadingAnimation() {
96 | logoAnimating = false
97 | timer?.invalidate()
98 | timer = nil
99 | }
100 | }
101 |
102 | #Preview {
103 | MultiturnChatView()
104 | }
105 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat/ChatBubbleShape.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ChatBubbleShape.swift
3 | // GeminiChat
4 | //
5 | // Created by Anup D'Souza
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | struct ChatBubbleShape: Shape {
12 | enum Direction {
13 | case left
14 | case right
15 | }
16 |
17 | let direction: Direction
18 |
19 | func path(in rect: CGRect) -> Path {
20 | return (direction == .left) ? leftBubble(in: rect) : rightBubble(in: rect)
21 | }
22 |
23 | private func leftBubble(in rect: CGRect) -> Path {
24 | let width = rect.width
25 | let height = rect.height
26 | let path = Path { p in
27 | p.move(to: CGPoint(x: 25, y: height))
28 | p.addLine(to: CGPoint(x: width - 20, y: height))
29 | p.addCurve(to: CGPoint(x: width, y: height - 20),
30 | control1: CGPoint(x: width - 8, y: height),
31 | control2: CGPoint(x: width, y: height - 8))
32 | p.addLine(to: CGPoint(x: width, y: 20))
33 | p.addCurve(to: CGPoint(x: width - 20, y: 0),
34 | control1: CGPoint(x: width, y: 8),
35 | control2: CGPoint(x: width - 8, y: 0))
36 | p.addLine(to: CGPoint(x: 21, y: 0))
37 | p.addCurve(to: CGPoint(x: 4, y: 20),
38 | control1: CGPoint(x: 12, y: 0),
39 | control2: CGPoint(x: 4, y: 8))
40 | p.addLine(to: CGPoint(x: 4, y: height - 11))
41 | p.addCurve(to: CGPoint(x: 0, y: height),
42 | control1: CGPoint(x: 4, y: height - 1),
43 | control2: CGPoint(x: 0, y: height))
44 | p.addLine(to: CGPoint(x: -0.05, y: height - 0.01))
45 | p.addCurve(to: CGPoint(x: 11.0, y: height - 4.0),
46 | control1: CGPoint(x: 4.0, y: height + 0.5),
47 | control2: CGPoint(x: 8, y: height - 1))
48 | p.addCurve(to: CGPoint(x: 25, y: height),
49 | control1: CGPoint(x: 16, y: height),
50 | control2: CGPoint(x: 20, y: height))
51 |
52 | }
53 | return path
54 | }
55 |
56 | private func rightBubble(in rect: CGRect) -> Path {
57 | let width = rect.width
58 | let height = rect.height
59 | let path = Path { p in
60 | p.move(to: CGPoint(x: 25, y: height))
61 | p.addLine(to: CGPoint(x: 20, y: height))
62 | p.addCurve(to: CGPoint(x: 0, y: height - 20),
63 | control1: CGPoint(x: 8, y: height),
64 | control2: CGPoint(x: 0, y: height - 8))
65 | p.addLine(to: CGPoint(x: 0, y: 20))
66 | p.addCurve(to: CGPoint(x: 20, y: 0),
67 | control1: CGPoint(x: 0, y: 8),
68 | control2: CGPoint(x: 8, y: 0))
69 | p.addLine(to: CGPoint(x: width - 21, y: 0))
70 | p.addCurve(to: CGPoint(x: width - 4, y: 20),
71 | control1: CGPoint(x: width - 12, y: 0),
72 | control2: CGPoint(x: width - 4, y: 8))
73 | p.addLine(to: CGPoint(x: width - 4, y: height - 11))
74 | p.addCurve(to: CGPoint(x: width, y: height),
75 | control1: CGPoint(x: width - 4, y: height - 1),
76 | control2: CGPoint(x: width, y: height))
77 | p.addLine(to: CGPoint(x: width + 0.05, y: height - 0.01))
78 | p.addCurve(to: CGPoint(x: width - 11, y: height - 4),
79 | control1: CGPoint(x: width - 4, y: height + 0.5),
80 | control2: CGPoint(x: width - 8, y: height - 1))
81 | p.addCurve(to: CGPoint(x: width - 25, y: height),
82 | control1: CGPoint(x: width - 16, y: height),
83 | control2: CGPoint(x: width - 20, y: height))
84 |
85 | }
86 | return path
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/GeminiMultiturnChat/GeminiMultiturnChat.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E42126412B306A8C00B5D1E6 /* GeminiMultiturnChatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42126402B306A8C00B5D1E6 /* GeminiMultiturnChatApp.swift */; };
11 | E42126432B306A8C00B5D1E6 /* MultiturnChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42126422B306A8C00B5D1E6 /* MultiturnChatView.swift */; };
12 | E42126452B306A8E00B5D1E6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E42126442B306A8E00B5D1E6 /* Assets.xcassets */; };
13 | E42126482B306A8E00B5D1E6 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E42126472B306A8E00B5D1E6 /* Preview Assets.xcassets */; };
14 | E42126512B306AA200B5D1E6 /* GenerativeAI-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E421264F2B306AA100B5D1E6 /* GenerativeAI-Info.plist */; };
15 | E42126522B306AA200B5D1E6 /* APIKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42126502B306AA100B5D1E6 /* APIKey.swift */; };
16 | E42126552B306B2300B5D1E6 /* ChatBubbleShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42126532B306B2300B5D1E6 /* ChatBubbleShape.swift */; };
17 | E42126562B306B2300B5D1E6 /* ChatBubble.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42126542B306B2300B5D1E6 /* ChatBubble.swift */; };
18 | E42126582B306B3C00B5D1E6 /* ChatService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42126572B306B3C00B5D1E6 /* ChatService.swift */; };
19 | E421265B2B306E7000B5D1E6 /* GoogleGenerativeAI in Frameworks */ = {isa = PBXBuildFile; productRef = E421265A2B306E7000B5D1E6 /* GoogleGenerativeAI */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | E421263D2B306A8C00B5D1E6 /* GeminiMultiturnChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GeminiMultiturnChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
24 | E42126402B306A8C00B5D1E6 /* GeminiMultiturnChatApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiMultiturnChatApp.swift; sourceTree = ""; };
25 | E42126422B306A8C00B5D1E6 /* MultiturnChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiturnChatView.swift; sourceTree = ""; };
26 | E42126442B306A8E00B5D1E6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | E42126472B306A8E00B5D1E6 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
28 | E421264F2B306AA100B5D1E6 /* GenerativeAI-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GenerativeAI-Info.plist"; sourceTree = ""; };
29 | E42126502B306AA100B5D1E6 /* APIKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIKey.swift; sourceTree = ""; };
30 | E42126532B306B2300B5D1E6 /* ChatBubbleShape.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatBubbleShape.swift; sourceTree = ""; };
31 | E42126542B306B2300B5D1E6 /* ChatBubble.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatBubble.swift; sourceTree = ""; };
32 | E42126572B306B3C00B5D1E6 /* ChatService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatService.swift; sourceTree = ""; };
33 | /* End PBXFileReference section */
34 |
35 | /* Begin PBXFrameworksBuildPhase section */
36 | E421263A2B306A8C00B5D1E6 /* Frameworks */ = {
37 | isa = PBXFrameworksBuildPhase;
38 | buildActionMask = 2147483647;
39 | files = (
40 | E421265B2B306E7000B5D1E6 /* GoogleGenerativeAI in Frameworks */,
41 | );
42 | runOnlyForDeploymentPostprocessing = 0;
43 | };
44 | /* End PBXFrameworksBuildPhase section */
45 |
46 | /* Begin PBXGroup section */
47 | E42126342B306A8B00B5D1E6 = {
48 | isa = PBXGroup;
49 | children = (
50 | E421263F2B306A8C00B5D1E6 /* GeminiMultiturnChat */,
51 | E421263E2B306A8C00B5D1E6 /* Products */,
52 | );
53 | sourceTree = "";
54 | };
55 | E421263E2B306A8C00B5D1E6 /* Products */ = {
56 | isa = PBXGroup;
57 | children = (
58 | E421263D2B306A8C00B5D1E6 /* GeminiMultiturnChat.app */,
59 | );
60 | name = Products;
61 | sourceTree = "";
62 | };
63 | E421263F2B306A8C00B5D1E6 /* GeminiMultiturnChat */ = {
64 | isa = PBXGroup;
65 | children = (
66 | E42126402B306A8C00B5D1E6 /* GeminiMultiturnChatApp.swift */,
67 | E42126422B306A8C00B5D1E6 /* MultiturnChatView.swift */,
68 | E42126572B306B3C00B5D1E6 /* ChatService.swift */,
69 | E42126542B306B2300B5D1E6 /* ChatBubble.swift */,
70 | E42126532B306B2300B5D1E6 /* ChatBubbleShape.swift */,
71 | E421264E2B306AA100B5D1E6 /* Config */,
72 | E42126442B306A8E00B5D1E6 /* Assets.xcassets */,
73 | E42126462B306A8E00B5D1E6 /* Preview Content */,
74 | );
75 | path = GeminiMultiturnChat;
76 | sourceTree = "";
77 | };
78 | E42126462B306A8E00B5D1E6 /* Preview Content */ = {
79 | isa = PBXGroup;
80 | children = (
81 | E42126472B306A8E00B5D1E6 /* Preview Assets.xcassets */,
82 | );
83 | path = "Preview Content";
84 | sourceTree = "";
85 | };
86 | E421264E2B306AA100B5D1E6 /* Config */ = {
87 | isa = PBXGroup;
88 | children = (
89 | E421264F2B306AA100B5D1E6 /* GenerativeAI-Info.plist */,
90 | E42126502B306AA100B5D1E6 /* APIKey.swift */,
91 | );
92 | path = Config;
93 | sourceTree = "";
94 | };
95 | /* End PBXGroup section */
96 |
97 | /* Begin PBXNativeTarget section */
98 | E421263C2B306A8C00B5D1E6 /* GeminiMultiturnChat */ = {
99 | isa = PBXNativeTarget;
100 | buildConfigurationList = E421264B2B306A8E00B5D1E6 /* Build configuration list for PBXNativeTarget "GeminiMultiturnChat" */;
101 | buildPhases = (
102 | E42126392B306A8C00B5D1E6 /* Sources */,
103 | E421263A2B306A8C00B5D1E6 /* Frameworks */,
104 | E421263B2B306A8C00B5D1E6 /* Resources */,
105 | );
106 | buildRules = (
107 | );
108 | dependencies = (
109 | );
110 | name = GeminiMultiturnChat;
111 | packageProductDependencies = (
112 | E421265A2B306E7000B5D1E6 /* GoogleGenerativeAI */,
113 | );
114 | productName = GeminiMultiturnChat;
115 | productReference = E421263D2B306A8C00B5D1E6 /* GeminiMultiturnChat.app */;
116 | productType = "com.apple.product-type.application";
117 | };
118 | /* End PBXNativeTarget section */
119 |
120 | /* Begin PBXProject section */
121 | E42126352B306A8B00B5D1E6 /* Project object */ = {
122 | isa = PBXProject;
123 | attributes = {
124 | BuildIndependentTargetsInParallel = 1;
125 | LastSwiftUpdateCheck = 1500;
126 | LastUpgradeCheck = 1500;
127 | TargetAttributes = {
128 | E421263C2B306A8C00B5D1E6 = {
129 | CreatedOnToolsVersion = 15.0;
130 | };
131 | };
132 | };
133 | buildConfigurationList = E42126382B306A8B00B5D1E6 /* Build configuration list for PBXProject "GeminiMultiturnChat" */;
134 | compatibilityVersion = "Xcode 14.0";
135 | developmentRegion = en;
136 | hasScannedForEncodings = 0;
137 | knownRegions = (
138 | en,
139 | Base,
140 | );
141 | mainGroup = E42126342B306A8B00B5D1E6;
142 | packageReferences = (
143 | E42126592B306E7000B5D1E6 /* XCRemoteSwiftPackageReference "generative-ai-swift" */,
144 | );
145 | productRefGroup = E421263E2B306A8C00B5D1E6 /* Products */;
146 | projectDirPath = "";
147 | projectRoot = "";
148 | targets = (
149 | E421263C2B306A8C00B5D1E6 /* GeminiMultiturnChat */,
150 | );
151 | };
152 | /* End PBXProject section */
153 |
154 | /* Begin PBXResourcesBuildPhase section */
155 | E421263B2B306A8C00B5D1E6 /* Resources */ = {
156 | isa = PBXResourcesBuildPhase;
157 | buildActionMask = 2147483647;
158 | files = (
159 | E42126482B306A8E00B5D1E6 /* Preview Assets.xcassets in Resources */,
160 | E42126452B306A8E00B5D1E6 /* Assets.xcassets in Resources */,
161 | E42126512B306AA200B5D1E6 /* GenerativeAI-Info.plist in Resources */,
162 | );
163 | runOnlyForDeploymentPostprocessing = 0;
164 | };
165 | /* End PBXResourcesBuildPhase section */
166 |
167 | /* Begin PBXSourcesBuildPhase section */
168 | E42126392B306A8C00B5D1E6 /* Sources */ = {
169 | isa = PBXSourcesBuildPhase;
170 | buildActionMask = 2147483647;
171 | files = (
172 | E42126432B306A8C00B5D1E6 /* MultiturnChatView.swift in Sources */,
173 | E42126562B306B2300B5D1E6 /* ChatBubble.swift in Sources */,
174 | E42126552B306B2300B5D1E6 /* ChatBubbleShape.swift in Sources */,
175 | E42126522B306AA200B5D1E6 /* APIKey.swift in Sources */,
176 | E42126412B306A8C00B5D1E6 /* GeminiMultiturnChatApp.swift in Sources */,
177 | E42126582B306B3C00B5D1E6 /* ChatService.swift in Sources */,
178 | );
179 | runOnlyForDeploymentPostprocessing = 0;
180 | };
181 | /* End PBXSourcesBuildPhase section */
182 |
183 | /* Begin XCBuildConfiguration section */
184 | E42126492B306A8E00B5D1E6 /* Debug */ = {
185 | isa = XCBuildConfiguration;
186 | buildSettings = {
187 | ALWAYS_SEARCH_USER_PATHS = NO;
188 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
189 | CLANG_ANALYZER_NONNULL = YES;
190 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
191 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
192 | CLANG_ENABLE_MODULES = YES;
193 | CLANG_ENABLE_OBJC_ARC = YES;
194 | CLANG_ENABLE_OBJC_WEAK = YES;
195 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
196 | CLANG_WARN_BOOL_CONVERSION = YES;
197 | CLANG_WARN_COMMA = YES;
198 | CLANG_WARN_CONSTANT_CONVERSION = YES;
199 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
202 | CLANG_WARN_EMPTY_BODY = YES;
203 | CLANG_WARN_ENUM_CONVERSION = YES;
204 | CLANG_WARN_INFINITE_RECURSION = YES;
205 | CLANG_WARN_INT_CONVERSION = YES;
206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
207 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
210 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
211 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
212 | CLANG_WARN_STRICT_PROTOTYPES = YES;
213 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
214 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
215 | CLANG_WARN_UNREACHABLE_CODE = YES;
216 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
217 | COPY_PHASE_STRIP = NO;
218 | DEBUG_INFORMATION_FORMAT = dwarf;
219 | ENABLE_STRICT_OBJC_MSGSEND = YES;
220 | ENABLE_TESTABILITY = YES;
221 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
222 | GCC_C_LANGUAGE_STANDARD = gnu17;
223 | GCC_DYNAMIC_NO_PIC = NO;
224 | GCC_NO_COMMON_BLOCKS = YES;
225 | GCC_OPTIMIZATION_LEVEL = 0;
226 | GCC_PREPROCESSOR_DEFINITIONS = (
227 | "DEBUG=1",
228 | "$(inherited)",
229 | );
230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
232 | GCC_WARN_UNDECLARED_SELECTOR = YES;
233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
234 | GCC_WARN_UNUSED_FUNCTION = YES;
235 | GCC_WARN_UNUSED_VARIABLE = YES;
236 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
237 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
238 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
239 | MTL_FAST_MATH = YES;
240 | ONLY_ACTIVE_ARCH = YES;
241 | SDKROOT = iphoneos;
242 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
243 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
244 | };
245 | name = Debug;
246 | };
247 | E421264A2B306A8E00B5D1E6 /* Release */ = {
248 | isa = XCBuildConfiguration;
249 | buildSettings = {
250 | ALWAYS_SEARCH_USER_PATHS = NO;
251 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
252 | CLANG_ANALYZER_NONNULL = YES;
253 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
255 | CLANG_ENABLE_MODULES = YES;
256 | CLANG_ENABLE_OBJC_ARC = YES;
257 | CLANG_ENABLE_OBJC_WEAK = YES;
258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
259 | CLANG_WARN_BOOL_CONVERSION = YES;
260 | CLANG_WARN_COMMA = YES;
261 | CLANG_WARN_CONSTANT_CONVERSION = YES;
262 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
264 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
265 | CLANG_WARN_EMPTY_BODY = YES;
266 | CLANG_WARN_ENUM_CONVERSION = YES;
267 | CLANG_WARN_INFINITE_RECURSION = YES;
268 | CLANG_WARN_INT_CONVERSION = YES;
269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
270 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
271 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
273 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
274 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
275 | CLANG_WARN_STRICT_PROTOTYPES = YES;
276 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
277 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
278 | CLANG_WARN_UNREACHABLE_CODE = YES;
279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
280 | COPY_PHASE_STRIP = NO;
281 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
282 | ENABLE_NS_ASSERTIONS = NO;
283 | ENABLE_STRICT_OBJC_MSGSEND = YES;
284 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
285 | GCC_C_LANGUAGE_STANDARD = gnu17;
286 | GCC_NO_COMMON_BLOCKS = YES;
287 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
289 | GCC_WARN_UNDECLARED_SELECTOR = YES;
290 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
291 | GCC_WARN_UNUSED_FUNCTION = YES;
292 | GCC_WARN_UNUSED_VARIABLE = YES;
293 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
294 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
295 | MTL_ENABLE_DEBUG_INFO = NO;
296 | MTL_FAST_MATH = YES;
297 | SDKROOT = iphoneos;
298 | SWIFT_COMPILATION_MODE = wholemodule;
299 | VALIDATE_PRODUCT = YES;
300 | };
301 | name = Release;
302 | };
303 | E421264C2B306A8E00B5D1E6 /* Debug */ = {
304 | isa = XCBuildConfiguration;
305 | buildSettings = {
306 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
307 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
308 | CODE_SIGN_STYLE = Automatic;
309 | CURRENT_PROJECT_VERSION = 1;
310 | DEVELOPMENT_ASSET_PATHS = "\"GeminiMultiturnChat/Preview Content\"";
311 | ENABLE_PREVIEWS = YES;
312 | GENERATE_INFOPLIST_FILE = YES;
313 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
314 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
315 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
316 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
317 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
318 | LD_RUNPATH_SEARCH_PATHS = (
319 | "$(inherited)",
320 | "@executable_path/Frameworks",
321 | );
322 | MARKETING_VERSION = 1.0;
323 | PRODUCT_BUNDLE_IDENTIFIER = com.anupdsouza.GeminiMultiturnChat;
324 | PRODUCT_NAME = "$(TARGET_NAME)";
325 | SWIFT_EMIT_LOC_STRINGS = YES;
326 | SWIFT_VERSION = 5.0;
327 | TARGETED_DEVICE_FAMILY = "1,2";
328 | };
329 | name = Debug;
330 | };
331 | E421264D2B306A8E00B5D1E6 /* Release */ = {
332 | isa = XCBuildConfiguration;
333 | buildSettings = {
334 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
335 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
336 | CODE_SIGN_STYLE = Automatic;
337 | CURRENT_PROJECT_VERSION = 1;
338 | DEVELOPMENT_ASSET_PATHS = "\"GeminiMultiturnChat/Preview Content\"";
339 | ENABLE_PREVIEWS = YES;
340 | GENERATE_INFOPLIST_FILE = YES;
341 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
342 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
343 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
344 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
345 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
346 | LD_RUNPATH_SEARCH_PATHS = (
347 | "$(inherited)",
348 | "@executable_path/Frameworks",
349 | );
350 | MARKETING_VERSION = 1.0;
351 | PRODUCT_BUNDLE_IDENTIFIER = com.anupdsouza.GeminiMultiturnChat;
352 | PRODUCT_NAME = "$(TARGET_NAME)";
353 | SWIFT_EMIT_LOC_STRINGS = YES;
354 | SWIFT_VERSION = 5.0;
355 | TARGETED_DEVICE_FAMILY = "1,2";
356 | };
357 | name = Release;
358 | };
359 | /* End XCBuildConfiguration section */
360 |
361 | /* Begin XCConfigurationList section */
362 | E42126382B306A8B00B5D1E6 /* Build configuration list for PBXProject "GeminiMultiturnChat" */ = {
363 | isa = XCConfigurationList;
364 | buildConfigurations = (
365 | E42126492B306A8E00B5D1E6 /* Debug */,
366 | E421264A2B306A8E00B5D1E6 /* Release */,
367 | );
368 | defaultConfigurationIsVisible = 0;
369 | defaultConfigurationName = Release;
370 | };
371 | E421264B2B306A8E00B5D1E6 /* Build configuration list for PBXNativeTarget "GeminiMultiturnChat" */ = {
372 | isa = XCConfigurationList;
373 | buildConfigurations = (
374 | E421264C2B306A8E00B5D1E6 /* Debug */,
375 | E421264D2B306A8E00B5D1E6 /* Release */,
376 | );
377 | defaultConfigurationIsVisible = 0;
378 | defaultConfigurationName = Release;
379 | };
380 | /* End XCConfigurationList section */
381 |
382 | /* Begin XCRemoteSwiftPackageReference section */
383 | E42126592B306E7000B5D1E6 /* XCRemoteSwiftPackageReference "generative-ai-swift" */ = {
384 | isa = XCRemoteSwiftPackageReference;
385 | repositoryURL = "https://github.com/google/generative-ai-swift";
386 | requirement = {
387 | kind = upToNextMajorVersion;
388 | minimumVersion = 0.4.4;
389 | };
390 | };
391 | /* End XCRemoteSwiftPackageReference section */
392 |
393 | /* Begin XCSwiftPackageProductDependency section */
394 | E421265A2B306E7000B5D1E6 /* GoogleGenerativeAI */ = {
395 | isa = XCSwiftPackageProductDependency;
396 | package = E42126592B306E7000B5D1E6 /* XCRemoteSwiftPackageReference "generative-ai-swift" */;
397 | productName = GoogleGenerativeAI;
398 | };
399 | /* End XCSwiftPackageProductDependency section */
400 | };
401 | rootObject = E42126352B306A8B00B5D1E6 /* Project object */;
402 | }
403 |
--------------------------------------------------------------------------------