Skip to content

Commit 92c3619

Browse files
committed
Build UI for template generate content
1 parent 6ed7fad commit 92c3619

File tree

4 files changed

+179
-2
lines changed

4 files changed

+179
-2
lines changed

firebaseai/FirebaseAIExample.xcodeproj/project.pbxproj

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
AEE793DF2E256D3900708F02 /* GoogleSearchSuggestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE793DC2E256D3900708F02 /* GoogleSearchSuggestionView.swift */; };
6161
AEE793E02E256D3900708F02 /* GroundedResponseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE793DD2E256D3900708F02 /* GroundedResponseView.swift */; };
6262
DE26D95F2DBB3E9F007E6668 /* FirebaseAI in Frameworks */ = {isa = PBXBuildFile; productRef = DE26D95E2DBB3E9F007E6668 /* FirebaseAI */; };
63+
DE907A812EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE907A802EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift */; };
64+
DE907A822EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE907A802EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift */; };
65+
DE907A842EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE907A832EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift */; };
66+
DE907A852EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE907A832EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift */; };
6367
DEFECAA92D7B4CCD00EF9621 /* ImagenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFECAA72D7B4CCD00EF9621 /* ImagenViewModel.swift */; };
6468
DEFECAAA2D7B4CCD00EF9621 /* ImagenScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFECAA62D7B4CCD00EF9621 /* ImagenScreen.swift */; };
6569
/* End PBXBuildFile section */
@@ -94,6 +98,8 @@
9498
88E10F5C2B11135000C08E95 /* BouncingDots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BouncingDots.swift; sourceTree = "<group>"; };
9599
AEE793DC2E256D3900708F02 /* GoogleSearchSuggestionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchSuggestionView.swift; sourceTree = "<group>"; };
96100
AEE793DD2E256D3900708F02 /* GroundedResponseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroundedResponseView.swift; sourceTree = "<group>"; };
101+
DE907A802EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateContentFromTemplateScreen.swift; sourceTree = "<group>"; };
102+
DE907A832EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateContentFromTemplateViewModel.swift; sourceTree = "<group>"; };
97103
DEFECAA62D7B4CCD00EF9621 /* ImagenScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagenScreen.swift; sourceTree = "<group>"; };
98104
DEFECAA72D7B4CCD00EF9621 /* ImagenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagenViewModel.swift; sourceTree = "<group>"; };
99105
/* End PBXFileReference section */
@@ -171,6 +177,7 @@
171177
88209C1A2B0FBDC300F64795 /* Screens */ = {
172178
isa = PBXGroup;
173179
children = (
180+
DE907A802EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift */,
174181
88209C1B2B0FBDC300F64795 /* GenerateContentScreen.swift */,
175182
);
176183
path = Screens;
@@ -179,6 +186,7 @@
179186
88209C1C2B0FBDC300F64795 /* ViewModels */ = {
180187
isa = PBXGroup;
181188
children = (
189+
DE907A832EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift */,
182190
88209C1D2B0FBDC300F64795 /* GenerateContentViewModel.swift */,
183191
);
184192
path = ViewModels;
@@ -468,6 +476,8 @@
468476
isa = PBXSourcesBuildPhase;
469477
buildActionMask = 2147483647;
470478
files = (
479+
DE907A852EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift in Sources */,
480+
DE907A812EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift in Sources */,
471481
86BB55EA2E8B2D6D0054B8B5 /* FunctionCallingScreen.swift in Sources */,
472482
86BB55EB2E8B2D6D0054B8B5 /* BouncingDots.swift in Sources */,
473483
86BB55EC2E8B2D6D0054B8B5 /* FunctionCallingViewModel.swift in Sources */,
@@ -494,6 +504,8 @@
494504
isa = PBXSourcesBuildPhase;
495505
buildActionMask = 2147483647;
496506
files = (
507+
DE907A842EAAE55600AE56CE /* GenerateContentFromTemplateViewModel.swift in Sources */,
508+
DE907A822EAAE53E00AE56CE /* GenerateContentFromTemplateScreen.swift in Sources */,
497509
86C1F4832BC726150026816F /* FunctionCallingScreen.swift in Sources */,
498510
886F95DF2B17D5010036F07A /* BouncingDots.swift in Sources */,
499511
86C1F4842BC726150026816F /* FunctionCallingViewModel.swift in Sources */,
@@ -827,8 +839,8 @@
827839
isa = XCRemoteSwiftPackageReference;
828840
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
829841
requirement = {
830-
kind = upToNextMajorVersion;
831-
minimumVersion = 12.0.0;
842+
branch = "pb-spt";
843+
kind = branch;
832844
};
833845
};
834846
/* End XCRemoteSwiftPackageReference section */

firebaseai/FirebaseAIExample/ContentView.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ struct ContentView: View {
5555
} label: {
5656
Label("Generate Content", systemImage: "doc.text")
5757
}
58+
NavigationLink {
59+
GenerateContentFromTemplateScreen(firebaseService: firebaseService)
60+
} label: {
61+
Label("Generate Content from Template", systemImage: "doc.text.fill")
62+
}
5863
NavigationLink {
5964
PhotoReasoningScreen(firebaseService: firebaseService)
6065
} label: {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import MarkdownUI
16+
import SwiftUI
17+
#if canImport(FirebaseAILogic)
18+
import FirebaseAILogic
19+
#else
20+
import FirebaseAI
21+
#endif
22+
import GenerativeAIUIComponents
23+
24+
struct GenerateContentFromTemplateScreen: View {
25+
let firebaseService: FirebaseAI
26+
@StateObject var viewModel: GenerateContentFromTemplateViewModel
27+
@State var userInput = ""
28+
29+
init(firebaseService: FirebaseAI) {
30+
self.firebaseService = firebaseService
31+
_viewModel =
32+
StateObject(wrappedValue: GenerateContentFromTemplateViewModel(firebaseService: firebaseService))
33+
}
34+
35+
enum FocusedField: Hashable {
36+
case message
37+
}
38+
39+
@FocusState
40+
var focusedField: FocusedField?
41+
42+
var body: some View {
43+
VStack {
44+
VStack(alignment: .leading) {
45+
Text("Enter your name, then tap on _Go_ to run generateContent from template on it.")
46+
.padding(.horizontal, 6)
47+
InputField("Enter your name", text: $userInput) {
48+
Text("Go")
49+
}
50+
.focused($focusedField, equals: .message)
51+
.onSubmit { onGenerateContentTapped() }
52+
}
53+
.padding(.horizontal, 16)
54+
55+
List {
56+
HStack(alignment: .top) {
57+
if viewModel.inProgress {
58+
ProgressView()
59+
} else {
60+
Image(systemName: "cloud.circle.fill")
61+
.font(.title2)
62+
}
63+
64+
Markdown("\(viewModel.outputText)")
65+
}
66+
.listRowSeparator(.hidden)
67+
}
68+
.listStyle(.plain)
69+
}
70+
.onTapGesture {
71+
focusedField = nil
72+
}
73+
.navigationTitle("Template Text example")
74+
}
75+
76+
private func onGenerateContentTapped() {
77+
focusedField = nil
78+
79+
Task {
80+
await viewModel.generateContentFromTemplate(name: userInput)
81+
}
82+
}
83+
}
84+
85+
#Preview {
86+
NavigationStack {
87+
GenerateContentFromTemplateScreen(firebaseService: FirebaseAI.firebaseAI())
88+
}
89+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2023 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#if canImport(FirebaseAILogic)
16+
import FirebaseAILogic
17+
#else
18+
import FirebaseAI
19+
#endif
20+
import Foundation
21+
import OSLog
22+
23+
@MainActor
24+
class GenerateContentFromTemplateViewModel: ObservableObject {
25+
private var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "generative-ai")
26+
27+
@Published
28+
var outputText = ""
29+
30+
@Published
31+
var errorMessage: String?
32+
33+
@Published
34+
var inProgress = false
35+
36+
private var model: TemplateGenerativeModel?
37+
38+
init(firebaseService: FirebaseAI) {
39+
model = firebaseService.templateGenerativeModel()
40+
// model = firebaseService.generativeModel(modelName: "gemini-2.0-flash-001")
41+
}
42+
43+
func generateContentFromTemplate(name: String) async {
44+
defer {
45+
inProgress = false
46+
}
47+
guard let model else {
48+
return
49+
}
50+
51+
do {
52+
inProgress = true
53+
errorMessage = nil
54+
outputText = ""
55+
56+
let response = try await model.generateContent(
57+
templateID: "new-greeting",
58+
inputs: [
59+
"name": name,
60+
"language": "English",
61+
]
62+
)
63+
if let text = response.text {
64+
outputText = text
65+
}
66+
} catch {
67+
logger.error("\(error.localizedDescription)")
68+
errorMessage = error.localizedDescription
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)