├── 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: startherepo
6 | Support Me: buymeacoffee youtube 7 | 8 | ## Screenshots 9 | 10 | 11 | 12 | 13 | ## Stay Connected 🤙🏼 14 | - star the repoStar the Repo 15 | - youtubeSubscribe on YouTube 16 | - buymeacoffeeBuy Me a Coffee 17 | - patreonBecome a Patron 18 | - xFollow me on X 19 | - githubFollow 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 | --------------------------------------------------------------------------------