Skip to content

Commit 4c8b01a

Browse files
committed
✨ Connect to FDC
Signed-off-by: Peter Friese <[email protected]>
1 parent 794ed5c commit 4c8b01a

File tree

20 files changed

+3300
-1813
lines changed

20 files changed

+3300
-1813
lines changed

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix.xcodeproj/project.pbxproj

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
8854EC512CC6AC2700A09F27 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8854EC502CC6AC2700A09F27 /* FirebaseAuth */; };
10+
88896F392CCA5AD80089A19C /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 88896F382CCA5AD80089A19C /* NukeUI */; };
11+
88BA433B2CC937E10063E309 /* FirebaseDataConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 88BA433A2CC937E10063E309 /* FirebaseDataConnect */; };
12+
88BA43402CC938250063E309 /* FriendlyFlixSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 88BA433F2CC938250063E309 /* FriendlyFlixSDK */; };
1113
/* End PBXBuildFile section */
1214

1315
/* Begin PBXFileReference section */
@@ -27,7 +29,9 @@
2729
isa = PBXFrameworksBuildPhase;
2830
buildActionMask = 2147483647;
2931
files = (
30-
8854EC512CC6AC2700A09F27 /* FirebaseAuth in Frameworks */,
32+
88896F392CCA5AD80089A19C /* NukeUI in Frameworks */,
33+
88BA433B2CC937E10063E309 /* FirebaseDataConnect in Frameworks */,
34+
88BA43402CC938250063E309 /* FriendlyFlixSDK in Frameworks */,
3135
);
3236
runOnlyForDeploymentPostprocessing = 0;
3337
};
@@ -70,7 +74,9 @@
7074
);
7175
name = FriendlyFlix;
7276
packageProductDependencies = (
73-
8854EC502CC6AC2700A09F27 /* FirebaseAuth */,
77+
88BA433A2CC937E10063E309 /* FirebaseDataConnect */,
78+
88BA433F2CC938250063E309 /* FriendlyFlixSDK */,
79+
88896F382CCA5AD80089A19C /* NukeUI */,
7480
);
7581
productName = FriendlyFlix;
7682
productReference = 88A9E6342CA834C600B3C4EF /* FriendlyFlix.app */;
@@ -101,7 +107,9 @@
101107
mainGroup = 88A9E62B2CA834C600B3C4EF;
102108
minimizedProjectReferenceProxies = 1;
103109
packageReferences = (
104-
8854EC4F2CC6AC2700A09F27 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
110+
88BA43392CC937E10063E309 /* XCRemoteSwiftPackageReference "data-connect-ios-sdk" */,
111+
88BA433E2CC938250063E309 /* XCLocalSwiftPackageReference "../FriendlyFlixSDK" */,
112+
88896F372CCA5AD80089A19C /* XCRemoteSwiftPackageReference "Nuke" */,
105113
);
106114
preferredProjectObjectVersion = 77;
107115
productRefGroup = 88A9E6352CA834C600B3C4EF /* Products */;
@@ -334,22 +342,46 @@
334342
};
335343
/* End XCConfigurationList section */
336344

345+
/* Begin XCLocalSwiftPackageReference section */
346+
88BA433E2CC938250063E309 /* XCLocalSwiftPackageReference "../FriendlyFlixSDK" */ = {
347+
isa = XCLocalSwiftPackageReference;
348+
relativePath = ../FriendlyFlixSDK;
349+
};
350+
/* End XCLocalSwiftPackageReference section */
351+
337352
/* Begin XCRemoteSwiftPackageReference section */
338-
8854EC4F2CC6AC2700A09F27 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
353+
88896F372CCA5AD80089A19C /* XCRemoteSwiftPackageReference "Nuke" */ = {
339354
isa = XCRemoteSwiftPackageReference;
340-
repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git";
355+
repositoryURL = "https://github.com/kean/Nuke?tab=readme-ov-file";
341356
requirement = {
342357
kind = upToNextMajorVersion;
343-
minimumVersion = 11.0.0;
358+
minimumVersion = 12.8.0;
359+
};
360+
};
361+
88BA43392CC937E10063E309 /* XCRemoteSwiftPackageReference "data-connect-ios-sdk" */ = {
362+
isa = XCRemoteSwiftPackageReference;
363+
repositoryURL = "https://github.com/firebase/data-connect-ios-sdk";
364+
requirement = {
365+
branch = main;
366+
kind = branch;
344367
};
345368
};
346369
/* End XCRemoteSwiftPackageReference section */
347370

348371
/* Begin XCSwiftPackageProductDependency section */
349-
8854EC502CC6AC2700A09F27 /* FirebaseAuth */ = {
372+
88896F382CCA5AD80089A19C /* NukeUI */ = {
373+
isa = XCSwiftPackageProductDependency;
374+
package = 88896F372CCA5AD80089A19C /* XCRemoteSwiftPackageReference "Nuke" */;
375+
productName = NukeUI;
376+
};
377+
88BA433A2CC937E10063E309 /* FirebaseDataConnect */ = {
378+
isa = XCSwiftPackageProductDependency;
379+
package = 88BA43392CC937E10063E309 /* XCRemoteSwiftPackageReference "data-connect-ios-sdk" */;
380+
productName = FirebaseDataConnect;
381+
};
382+
88BA433F2CC938250063E309 /* FriendlyFlixSDK */ = {
350383
isa = XCSwiftPackageProductDependency;
351-
package = 8854EC4F2CC6AC2700A09F27 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
352-
productName = FirebaseAuth;
384+
productName = FriendlyFlixSDK;
353385
};
354386
/* End XCSwiftPackageProductDependency section */
355387
};

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Details/MovieCardView.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919

2020
import SwiftUI
21+
import NukeUI
2122

2223
struct MovieCardView: View {
2324
var showDetails: Bool = false
@@ -27,23 +28,28 @@ struct MovieCardView: View {
2728
var body: some View {
2829
CardView(showDetails: showDetails) {
2930
if let imageUrl = URL(string: movie.imageUrl) {
30-
AsyncImage(url: imageUrl) { phase in
31-
if let image = phase.image {
31+
LazyImage(url: imageUrl) { state in
32+
if let image = state.image {
3233
image
3334
.resizable()
34-
.aspectRatio(contentMode: .fill)
35-
.frame(height: 450)
36-
} else if phase.error != nil {
35+
.scaledToFill()
36+
.frame(maxWidth: .infinity)
37+
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
38+
} else if state.error != nil {
3739
Color.red
40+
.frame(maxWidth: .infinity)
41+
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
3842
.redacted(if: true)
3943
} else {
4044
Image(systemName: "photo.artframe")
4145
.resizable()
42-
.aspectRatio(contentMode: .fit)
43-
.frame(height: 450)
46+
.aspectRatio(contentMode: .fill)
47+
.frame(maxWidth: .infinity)
48+
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
4449
.redacted(reason: .placeholder)
4550
}
4651
}
52+
.frame(maxWidth: .infinity)
4753
}
4854
} heroTitle: {
4955
VStack(alignment: .leading) {

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Home/HomeScreen.swift

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,83 @@
1717
// limitations under the License.
1818

1919
import SwiftUI
20+
import FirebaseDataConnect
21+
import FriendlyFlixSDK
2022

2123
struct HomeScreen: View {
2224
@Namespace private var namespace
25+
@Environment(AuthenticationService.self) var authenticationService
2326

27+
private var connector = DataConnect.friendlyFlixConnector
28+
29+
private func mapMovie(_ listMovie: ListMoviesQuery.Data.Movie) -> Movie {
30+
.init(
31+
title: listMovie.title,
32+
description: listMovie.description ?? "",
33+
releaseYear: listMovie.releaseYear,
34+
rating: listMovie.rating,
35+
imageUrl: listMovie.imageUrl
36+
)
37+
}
38+
39+
private var heroMovies: [Movie] {
40+
connector.listMoviesQuery
41+
.ref({ optionalVars in
42+
optionalVars.limit = 3
43+
optionalVars.orderByReleaseYear = .DESC
44+
})
45+
.data?.movies.map(Movie.init) ?? []
46+
}
47+
48+
private var topMovies: [Movie] {
49+
connector.listMoviesQuery
50+
.ref({ optionalVars in
51+
optionalVars.limit = 5
52+
optionalVars.orderByRating = .DESC
53+
})
54+
.data?.movies.map(Movie.init) ?? []
55+
}
56+
57+
private var watchList: [Movie] {
58+
/// TODO: build a query that retrieves the user's watch list
59+
connector.listMoviesQuery.ref().data?.movies.map(Movie.init) ?? []
60+
}
61+
62+
private var featuredMovies: [Movie] {
63+
/// TODO: build query that retrieves movies that are marked as "featured"
64+
connector.listMoviesQuery
65+
.ref({ optionalVars in
66+
optionalVars.limit = 5
67+
optionalVars.orderByRating = .DESC
68+
})
69+
.data?.movies.map(Movie.init) ?? []
70+
}
71+
}
72+
73+
extension HomeScreen {
2474
var body: some View {
2575
NavigationStack {
2676
ScrollView {
2777
TabView {
28-
ForEach(Movie.featured) { movie in
78+
ForEach(heroMovies) { movie in
2979
NavigationLink(value: movie) {
30-
MovieTeaserView(title: movie.title, subtitle: movie.subtitle, imageUrl: movie.imageUrl)
80+
MovieTeaserView(title: movie.title, subtitle: movie.description, imageUrl: movie.imageUrl)
3181
.matchedTransitionSource(id: movie.id, in: namespace)
3282
}
33-
83+
.buttonStyle(.noHighlight)
3484
}
3585
}
86+
.frame(height: 600)
3687
.navigationDestination(for: Movie.self) { movie in
3788
MovieCardView(showDetails: true, movie: movie)
3889
.navigationTransition(.zoom(sourceID: movie.id, in: namespace))
3990
}
40-
.frame(height: 600)
41-
.tabViewStyle(.page)
4291
.tabViewStyle(.page(indexDisplayMode: .always))
4392

4493
Group {
45-
MovieListSection(namespace: namespace, title: "Top Movies", movies: Movie.topMovies)
46-
MovieListSection(namespace: namespace, title: "Watch List", movies: Movie.watchList)
47-
MovieListSection(namespace: namespace, title: "Featured", movies: Movie.featured)
94+
MovieListSection(namespace: namespace, title: "Top Movies", movies: topMovies)
95+
MovieListSection(namespace: namespace, title: "Watch List", movies: watchList)
96+
MovieListSection(namespace: namespace, title: "Featured", movies: featuredMovies)
4897
}
4998
.navigationDestination(for: [Movie].self) { movies in
5099
MovieListScreen(namespace: namespace, movies: movies)

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Home/MovieTeaserView.swift

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// See the License for the specific language governing permissions and
1717
// limitations under the License.
1818

19+
import NukeUI
1920
import SwiftUI
2021

2122
struct MovieTeaserView: View {
@@ -25,20 +26,26 @@ struct MovieTeaserView: View {
2526

2627
var body: some View {
2728
ZStack(alignment: .bottom) {
28-
if let imageUrl = URL(string: imageUrl) {
29-
AsyncImage(url: imageUrl) { phase in
30-
if let image = phase.image {
31-
image
32-
.resizable()
33-
.scaledToFill()
34-
} else if phase.error != nil {
35-
Color.red
36-
.redacted(if: true)
37-
} else {
38-
Image(systemName: "photo.artframe")
39-
.resizable()
40-
.scaledToFill()
41-
.redacted(reason: .placeholder)
29+
GeometryReader { geometry in
30+
if let imageUrl = URL(string: imageUrl) {
31+
LazyImage(url: imageUrl) { state in
32+
if let image = state.image {
33+
image
34+
.resizable()
35+
.scaledToFill()
36+
.frame(width: geometry.size.width)
37+
.clipped()
38+
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
39+
} else if state.error != nil {
40+
Color.red
41+
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
42+
.redacted(if: true)
43+
} else {
44+
Image(systemName: "photo.artframe")
45+
.resizable()
46+
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
47+
.redacted(reason: .placeholder)
48+
}
4249
}
4350
}
4451
}
@@ -67,7 +74,8 @@ struct MovieTeaserView: View {
6774
var movie = Movie.mock
6875
MovieTeaserView(
6976
title: movie.title,
70-
subtitle: movie.subtitle,
77+
subtitle: movie.description,
7178
imageUrl: movie.imageUrl
7279
)
80+
.frame(maxHeight: 400)
7381
}

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/MovieList/MovieListScreen.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct MovieListScreen: View {
2727
ForEach(movies) { movie in
2828
MovieListRowView(
2929
title: movie.title,
30-
subtitle: movie.subtitle,
30+
subtitle: movie.description,
3131
imageUrl: movie.imageUrl
3232
)
3333
.matchedTransitionSource(id: movie.id, in: namespace)

Examples/FriendlyFlix/app/FriendlyFlix/FriendlyFlix/Features/Search/SearchScreen.swift

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,34 @@
1717
// limitations under the License.
1818

1919
import SwiftUI
20+
import FirebaseDataConnect
21+
import FriendlyFlixSDK
2022

21-
private struct SearchedView: View {
23+
struct SearchedView: View {
2224
@Environment(\.isSearching) private var isSearching
2325
var namespace: Namespace.ID
24-
var filteredMovies: [Movie]
26+
var filteredMovies = [Movie]()
27+
var connector = DataConnect.friendlyFlixConnector
28+
29+
private var topMovies: [Movie] {
30+
connector.listMoviesQuery
31+
.ref({ optionalVars in
32+
optionalVars.limit = 5
33+
optionalVars.orderByRating = .DESC
34+
})
35+
.data?.movies.map(Movie.init) ?? []
36+
}
2537

2638
var body: some View {
2739
if !isSearching {
28-
MovieListSection(namespace: namespace, title: "Top Movies", movies: Movie.topMovies)
40+
MovieListSection(namespace: namespace, title: "Top Movies", movies: topMovies)
2941
CategoriesView()
3042
} else {
3143
ForEach(filteredMovies) { movie in
3244
NavigationLink(value: movie) {
3345
MovieListRowView(
3446
title: movie.title,
35-
subtitle: movie.subtitle,
47+
subtitle: movie.description,
3648
imageUrl: movie.imageUrl
3749
)
3850
.matchedTransitionSource(id: movie.id, in: namespace)
@@ -48,11 +60,17 @@ struct SearchScreen: View {
4860
@State private var isStatusBarHidden = false
4961
@Namespace private var namespace
5062

51-
var filteredMovies: [Movie] {
52-
Movie.mockList.filter { $0.title.lowercased().contains(searchText.lowercased()) }
63+
var connector = DataConnect.friendlyFlixConnector
64+
65+
private var filteredMovies: [Movie] {
66+
connector.listMoviesByPartialTitleQuery
67+
.ref(searchTerm: searchText)
68+
.data?.movies.map(Movie.init) ?? []
5369
}
5470

71+
}
5572

73+
extension SearchScreen {
5674
var body: some View {
5775
NavigationStack {
5876
ScrollView {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright © 2024 Google LLC. All rights reserved.
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 FirebaseDataConnect
16+
import FriendlyFlixSDK
17+
18+
extension Movie {
19+
init(from: ListMoviesQuery.Data.Movie) {
20+
self.title = from.title
21+
self.description = from.description ?? ""
22+
self.releaseYear = from.releaseYear
23+
self.rating = from.rating
24+
self.imageUrl = from.imageUrl
25+
}
26+
27+
init(from: ListMoviesByPartialTitleQuery.Data.Movie) {
28+
self.title = from.title
29+
self.description = from.description ?? ""
30+
self.releaseYear = from.releaseYear
31+
self.rating = from.rating
32+
self.imageUrl = from.imageUrl
33+
}
34+
}
35+

0 commit comments

Comments
 (0)