Skip to content

Commit fb77255

Browse files
committed
Refactor
1 parent 99c1362 commit fb77255

File tree

4 files changed

+98
-89
lines changed

4 files changed

+98
-89
lines changed

firebaseai/ChatExample/Views/WebView.swift renamed to firebaseai/ChatExample/Views/Grounding/GoogleSearchSuggestionView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import SwiftUI
1616
import WebKit
1717

18-
struct WebView: UIViewRepresentable {
18+
struct GoogleSearchSuggestionView: UIViewRepresentable {
1919
let htmlString: String
2020

2121
// This Coordinator class will act as the web view's navigation delegate.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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 FirebaseAI
16+
import SwiftUI
17+
18+
/// A view that displays a chat message that is grounded in Google Search.
19+
struct GroundedResponseView: View {
20+
var message: ChatMessage
21+
var groundingMetadata: GroundingMetadata
22+
23+
var body: some View {
24+
// We can only display a response grounded in Google Search if the searchEntrypoint is non-nil.
25+
let isCompliant = (groundingMetadata.groundingChunks.isEmpty || groundingMetadata
26+
.searchEntryPoint != nil)
27+
if isCompliant {
28+
HStack(alignment: .top, spacing: 8) {
29+
VStack(alignment: .leading, spacing: 8) {
30+
// Message text
31+
ResponseTextView(message: message)
32+
33+
if !groundingMetadata.groundingChunks.isEmpty {
34+
Divider()
35+
// Source links
36+
ForEach(0 ..< groundingMetadata.groundingChunks.count, id: \.self) { index in
37+
if let webChunk = groundingMetadata.groundingChunks[index].web {
38+
SourceLinkView(
39+
title: webChunk.title ?? "Untitled Source",
40+
uri: webChunk.uri
41+
)
42+
}
43+
}
44+
}
45+
// Search suggestions
46+
if let searchEntryPoint = groundingMetadata.searchEntryPoint {
47+
Divider()
48+
GoogleSearchSuggestionView(htmlString: searchEntryPoint.renderedContent)
49+
.frame(height: 44)
50+
.clipShape(RoundedRectangle(cornerRadius: 22))
51+
}
52+
}
53+
}
54+
.frame(maxWidth: UIScreen.main.bounds.width * 0.8, alignment: .leading)
55+
}
56+
}
57+
}
58+
59+
/// A view for a single, clickable source link.
60+
struct SourceLinkView: View {
61+
let title: String
62+
let uri: String?
63+
64+
var body: some View {
65+
if let uri, let url = URL(string: uri) {
66+
Link(destination: url) {
67+
HStack(spacing: 4) {
68+
Image(systemName: "link")
69+
.font(.caption)
70+
.foregroundColor(.secondary)
71+
Text(title)
72+
.font(.footnote)
73+
.underline()
74+
.lineLimit(1)
75+
.multilineTextAlignment(.leading)
76+
}
77+
}
78+
.buttonStyle(.plain)
79+
}
80+
}
81+
}

firebaseai/ChatExample/Views/MessageView.swift

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -80,49 +80,6 @@ struct ResponseTextView: View {
8080
}
8181
}
8282

83-
struct GroundedResponseView: View {
84-
var message: ChatMessage
85-
var groundingMetadata: GroundingMetadata
86-
87-
var body: some View {
88-
// We can only display a grounded response if the searchEntrypoint is non-nil.
89-
// If the searchEntrypoint is nil, we can only display the response if it's not grounded.
90-
let isNonCompliant = (!groundingMetadata.groundingChunks.isEmpty && groundingMetadata
91-
.searchEntryPoint == nil)
92-
if isNonCompliant {
93-
ComplianceErrorView()
94-
} else {
95-
HStack(alignment: .top, spacing: 8) {
96-
VStack(alignment: .leading, spacing: 8) {
97-
// Message text
98-
ResponseTextView(message: message)
99-
100-
if !groundingMetadata.groundingChunks.isEmpty {
101-
Divider()
102-
// Source links
103-
ForEach(0 ..< groundingMetadata.groundingChunks.count, id: \.self) { index in
104-
if let webChunk = groundingMetadata.groundingChunks[index].web {
105-
SourceLinkView(
106-
title: webChunk.title ?? "Untitled Source",
107-
uri: webChunk.uri
108-
)
109-
}
110-
}
111-
}
112-
// Search suggestions
113-
if let searchEntryPoint = groundingMetadata.searchEntryPoint {
114-
Divider()
115-
WebView(htmlString: searchEntryPoint.renderedContent)
116-
.frame(height: 44)
117-
.clipShape(RoundedRectangle(cornerRadius: 22))
118-
}
119-
}
120-
}
121-
.frame(maxWidth: UIScreen.main.bounds.width * 0.8, alignment: .leading)
122-
}
123-
}
124-
}
125-
12683
struct MessageView: View {
12784
var message: ChatMessage
12885

@@ -164,44 +121,3 @@ struct MessageView_Previews: PreviewProvider {
164121
}
165122
}
166123
}
167-
168-
/// A simplified view for a single, clickable source link.
169-
struct SourceLinkView: View {
170-
let title: String
171-
let uri: String?
172-
173-
var body: some View {
174-
if let uri, let url = URL(string: uri) {
175-
Link(destination: url) {
176-
HStack(spacing: 4) {
177-
Image(systemName: "link")
178-
.font(.caption)
179-
.foregroundColor(.secondary)
180-
Text(title)
181-
.font(.footnote)
182-
.underline()
183-
.lineLimit(1)
184-
.multilineTextAlignment(.leading)
185-
}
186-
}
187-
.buttonStyle(.plain)
188-
}
189-
}
190-
}
191-
192-
/// A view to show when a response cannot be displayed due to compliance or other errors.
193-
struct ComplianceErrorView: View {
194-
var message =
195-
"Could not display the response because it was missing required attribution components."
196-
197-
var body: some View {
198-
HStack {
199-
Image(systemName: "exclamationmark.triangle.fill")
200-
.foregroundColor(.orange)
201-
Text(message)
202-
}
203-
.padding()
204-
.background(Color(.secondarySystemBackground))
205-
.clipShape(RoundedRectangle(cornerRadius: 12))
206-
}
207-
}

firebaseai/FirebaseAIExample.xcodeproj/project.pbxproj

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
8611DB362E21AA1600132740 /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8611DB352E21AA1600132740 /* WebView.swift */; };
1110
869200B32B879C4F00482873 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 869200B22B879C4F00482873 /* GoogleService-Info.plist */; };
1211
86C1F4832BC726150026816F /* FunctionCallingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86C1F47E2BC726150026816F /* FunctionCallingScreen.swift */; };
1312
86C1F4842BC726150026816F /* FunctionCallingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86C1F4802BC726150026816F /* FunctionCallingViewModel.swift */; };
@@ -28,13 +27,14 @@
2827
886F95E02B17D5010036F07A /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E10F562B1112F600C08E95 /* ConversationViewModel.swift */; };
2928
886F95E12B17D5010036F07A /* ConversationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E10F542B1112CA00C08E95 /* ConversationScreen.swift */; };
3029
886F95E32B17D6630036F07A /* GenerativeAIUIComponents in Frameworks */ = {isa = PBXBuildFile; productRef = 886F95E22B17D6630036F07A /* GenerativeAIUIComponents */; };
30+
AEE793DF2E256D3900708F02 /* GoogleSearchSuggestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE793DC2E256D3900708F02 /* GoogleSearchSuggestionView.swift */; };
31+
AEE793E02E256D3900708F02 /* GroundedResponseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE793DD2E256D3900708F02 /* GroundedResponseView.swift */; };
3132
DE26D95F2DBB3E9F007E6668 /* FirebaseAI in Frameworks */ = {isa = PBXBuildFile; productRef = DE26D95E2DBB3E9F007E6668 /* FirebaseAI */; };
3233
DEFECAA92D7B4CCD00EF9621 /* ImagenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFECAA72D7B4CCD00EF9621 /* ImagenViewModel.swift */; };
3334
DEFECAAA2D7B4CCD00EF9621 /* ImagenScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEFECAA62D7B4CCD00EF9621 /* ImagenScreen.swift */; };
3435
/* End PBXBuildFile section */
3536

3637
/* Begin PBXFileReference section */
37-
8611DB352E21AA1600132740 /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = "<group>"; };
3838
869200B22B879C4F00482873 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
3939
86C1F47E2BC726150026816F /* FunctionCallingScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FunctionCallingScreen.swift; sourceTree = "<group>"; };
4040
86C1F4802BC726150026816F /* FunctionCallingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FunctionCallingViewModel.swift; sourceTree = "<group>"; };
@@ -61,6 +61,8 @@
6161
88E10F582B11131900C08E95 /* ChatMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMessage.swift; sourceTree = "<group>"; };
6262
88E10F5A2B11133E00C08E95 /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = "<group>"; };
6363
88E10F5C2B11135000C08E95 /* BouncingDots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BouncingDots.swift; sourceTree = "<group>"; };
64+
AEE793DC2E256D3900708F02 /* GoogleSearchSuggestionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSearchSuggestionView.swift; sourceTree = "<group>"; };
65+
AEE793DD2E256D3900708F02 /* GroundedResponseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroundedResponseView.swift; sourceTree = "<group>"; };
6466
DEFECAA62D7B4CCD00EF9621 /* ImagenScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagenScreen.swift; sourceTree = "<group>"; };
6567
DEFECAA72D7B4CCD00EF9621 /* ImagenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagenViewModel.swift; sourceTree = "<group>"; };
6668
/* End PBXFileReference section */
@@ -256,11 +258,11 @@
256258
88E10F512B11124100C08E95 /* Views */ = {
257259
isa = PBXGroup;
258260
children = (
261+
AEE793DE2E256D3900708F02 /* Grounding */,
259262
88263BEE2B239BFE008AB09B /* ErrorView.swift */,
260263
88E10F5A2B11133E00C08E95 /* MessageView.swift */,
261264
88E10F5C2B11135000C08E95 /* BouncingDots.swift */,
262265
889873842B208563005B4896 /* ErrorDetailsView.swift */,
263-
8611DB352E21AA1600132740 /* WebView.swift */,
264266
);
265267
path = Views;
266268
sourceTree = "<group>";
@@ -281,6 +283,15 @@
281283
path = Screens;
282284
sourceTree = "<group>";
283285
};
286+
AEE793DE2E256D3900708F02 /* Grounding */ = {
287+
isa = PBXGroup;
288+
children = (
289+
AEE793DC2E256D3900708F02 /* GoogleSearchSuggestionView.swift */,
290+
AEE793DD2E256D3900708F02 /* GroundedResponseView.swift */,
291+
);
292+
path = Grounding;
293+
sourceTree = "<group>";
294+
};
284295
DEFECAA82D7B4CCD00EF9621 /* ImagenScreen */ = {
285296
isa = PBXGroup;
286297
children = (
@@ -378,7 +389,6 @@
378389
88263BF12B239C11008AB09B /* ErrorDetailsView.swift in Sources */,
379390
8848C8352B0D04BC007B434F /* ContentView.swift in Sources */,
380391
886F95D52B17BA010036F07A /* GenerateContentScreen.swift in Sources */,
381-
8611DB362E21AA1600132740 /* WebView.swift in Sources */,
382392
8848C8332B0D04BC007B434F /* FirebaseAIExampleApp.swift in Sources */,
383393
886F95E02B17D5010036F07A /* ConversationViewModel.swift in Sources */,
384394
886F95DD2B17D5010036F07A /* MessageView.swift in Sources */,
@@ -389,6 +399,8 @@
389399
886F95E12B17D5010036F07A /* ConversationScreen.swift in Sources */,
390400
88263BF02B239C09008AB09B /* ErrorView.swift in Sources */,
391401
886F95D62B17BA010036F07A /* GenerateContentViewModel.swift in Sources */,
402+
AEE793DF2E256D3900708F02 /* GoogleSearchSuggestionView.swift in Sources */,
403+
AEE793E02E256D3900708F02 /* GroundedResponseView.swift in Sources */,
392404
);
393405
runOnlyForDeploymentPostprocessing = 0;
394406
};

0 commit comments

Comments
 (0)