SlideShare a Scribd company logo
Peter Friese | Developer Advocate, Firebase
Marina Coelho | Developer Relations Engineer, Firebase
 +
Swi!UI & Firebase Workshop
@coelho_dev @pete!riese
Firebase & SwiftUI Workshop
Firebase & SwiftUI Workshop
What we’re going to
build
Make It So
✨ Simple to-do list app
✨ Store to-dos in Cloud Firestore
✨ Real-time updates across devices
✨ Use without user account (“try before you buy”)
✨ Sign in with Email/Password
✨ Sign in with Apple
✨ Feature flags using Remote Config
✨ There is an Android version as well!
(bit.ly/makeitso-android-4)
Demo
Firebase & SwiftUI Workshop
Run with confidence Engage users
Develop apps faster
Run with confidence
Crashlytics
Performance
Monitoring
Test Lab
App Distribution
Engage users
Analytics
Predictions
Cloud
Messaging
Remote
Config
A/B Testing
Dynamic
Links
In-app
Messaging
Develop apps faster
Auth
Cloud
Functions
Cloud
Firestore
Hosting
ML Kit
Realtime
Database
Cloud
Storage
bit.ly/what-is-firebase
Extensions
Machine
Learning
Adding Firebase to a
Swi!UI app
Exercise
Adding Firebase to a Swi!UI app
Exercise
1. Create a new Firebase project
2. Add your app to the new Firebase project
3. Download GoogleServices-Info.plist to your app
Application Lifecycle
SwiftUI 2
Photo by Thor Alvis on Unsplash
SwiftUI 2: No more AppDelegate!
import SwiftUI
@main
struct MakeItSoApp: App {
var body: some Scene {
WindowGroup {
TodosListView()
}
}
}
SwiftUI 2: No more AppDelegate!
import SwiftUI
@main
struct MakeItSoApp: App {
var body: some Scene {
WindowGroup {
TodosListView()
}
}
}
Solution 1: use initialiser
import SwiftUI
@main
struct MakeItSoApp: App {
var body: some Scene {
WindowGroup {
TodosListView()
}
}
}
Solution 1: use initialiser
import SwiftUI
@main
struct MakeItSoApp: App {
var body: some Scene {
WindowGroup {
TodosListView()
}
}
}
init() {
FirebaseApp.configure()
}
Solution 2: use UIApplicationDelegateAdaptor
import SwiftUI
import Firebase
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool {
FirebaseApp.configure()
return true
}
}
@main
struct MakeItSoApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
Solution 2: use UIApplicationDelegateAdaptor
import SwiftUI
import Firebase
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool {
FirebaseApp.configure()
return true
}
}
@main
struct MakeItSoApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
Solution 2: use UIApplicationDelegateAdaptor
import SwiftUI
import Firebase
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool {
FirebaseApp.configure()
return true
}
}
@main
struct MakeItSoApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
Solution 2: use UIApplicationDelegateAdaptor
import SwiftUI
import Firebase
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions:
[UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool {
FirebaseApp.configure()
return true
}
}
@main
struct MakeItSoApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
Watch the scene phase
Handle deep links
Continue user activities
Do more with the new life cycle
Watch the scene phase
Handle deep links
Continue user activities
Learn more
pete!riese.dev/ultimate-guide-to-swi"ui2-application-lifecycle/
Firestore
Firebase & SwiftUI Workshop
Firebase & SwiftUI Workshop
One-Time Fetches
Offline Mode
Effortless Syncing
bird_type:
airspeed:
coconut_capacity:
isNative:
icon:
vector:
distances_traveled:
"swallow"
42.733
0.62
false
<binary data>
{ x: 36.4255,
y: 25.1442,
z: 18.8816 }
[42, 39, 12, 42]
Document
Collection
Sub-Collection
Top level
collections
“todos” collection a single todo document
struct Todo {
@DocumentID var docId: String?
var id: String? = UUID().uuidString
var title: String
var completed: Bool = false
var userId: String?
}
Data Model
TodosListView
TodoDetailsView
TodoListRowView
Source of Truth
todos: [Todo]
@StateObject
@Binding
Todo
Todo
Review: Architecture
TodosListView
TodoDetailsView
TodoListRowView
Source of Truth
todos: [Todo]
@StateObject
@Binding
Todo
Todo
Review: Architecture
Snapshot Listener
class TodosRepository: ObservableObject {
@Injected var db: Firestore
private var listenerRegistration: ListenerRegistration?
@Published var todos = [Todo]()
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
Real-time Sync w/ Snapshot Listeners
class TodosRepository: ObservableObject {
@Injected var db: Firestore
private var listenerRegistration: ListenerRegistration?
@Published var todos = [Todo]()
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
Real-time Sync w/ Snapshot Listeners
class TodosRepository: ObservableObject {
@Injected var db: Firestore
private var listenerRegistration: ListenerRegistration?
@Published var todos = [Todo]()
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
Real-time Sync w/ Snapshot Listeners
class TodosRepository: ObservableObject {
@Injected var db: Firestore
private var listenerRegistration: ListenerRegistration?
@Published var todos = [Todo]()
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
Real-time Sync w/ Snapshot Listeners
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot["id"] as? String !% ""
let title = data["title"] as? String !% ""
let completed = data["completed"] as? Bool !% false
let userId = data["userId"] as? String !% ""
return Todo(docId: docId, id: id,
title: title, completed: completed, userId: userId)
}
}
Real-time Sync w/ Snapshot Listeners
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot["id"] as? String !% ""
let title = data["title"] as? String !% ""
let completed = data["completed"] as? Bool !% false
let userId = data["userId"] as? String !% ""
return Todo(docId: docId, id: id,
title: title, completed: completed, userId: userId)
}
}
Real-time Sync w/ Snapshot Listeners
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot["id"] as? String !% ""
let title = data["title"] as? String !% ""
let completed = data["completed"] as? Bool !% false
let userId = data["userId"] as? String !% ""
return Todo(docId: docId, id: id,
title: title, completed: completed, userId: userId)
}
}
Real-time Sync w/ Snapshot Listeners
Can we do better?
(Yes, we can)
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let docId = queryDocumentSnapshot.documentID
let data = queryDocumentSnapshot.data()
let id = queryDocumentSnapshot["id"] as? String !% ""
let title = data["title"] as? String !% ""
let completed = data["completed"] as? Bool !% false
let userId = data["userId"] as? String !% ""
return Todo(docId: docId, id: id,
title: title, completed: completed, userId: userId)
}
}
Real-time Sync w/ Snapshot Listeners
func subscribe() {
if listenerRegistration !# nil {
unsubscribe()
}
listenerRegistration = db.collection("todos")
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot!$documents else {
return
}
self!$todos = documents.compactMap { queryDocumentSnapshot in
let result = Result { try queryDocumentSnapshot.data(as: Todo.self) }
switch result {
case .success(let todo):
return todo
case .failure(let error):
!& handle error
return nil
}
}
}
Real-time Sync w/ Snapshot Listeners
Learn more
h#ps://pete!riese.dev/$restore-codable-the-comprehensive-guide/
Sync Data
with Firestore
Exercise
Sync Data with Firestore
1. Add Snapshot listeners and display data in the UI
2. Add new todo items via the app
3. Update todo items via the app
4.Delete todo items via the app
Exercise
struct BookShelfView: View {
@FirestoreQuery(
collectionPath: "todos",
predicates: [
.where("userId", isEqualTo: userId),
]
) var todos: Result<[Todo], Error>
@State var userId = "F18EBA5E"
var body: some View {
List(todos) { todo in
Text(todo.title)
}
}
}
Firestore Property Wrapper Firebase 8.9.0
@FloWritesCode
@mo%enditlevsen
Thanks to
Authentication
Photo by Conve"Kit on Unsplash
Authentication
Photo by Eduardo Soares on Unsplash
Authentication
Photo by Victor Freitas on Unsplash

Sign the user in
Update the data model
Secure users’ data
How to implement
Firebase Authentication?
Anonymous Authentication
“Guest” accounts, rather
func signIn() {
registerStateListener()
if Auth.auth().currentUser !' nil {
Auth.auth().signInAnonymously()
}
}
Anonymous Authentication
Firebase & SwiftUI Workshop
SignInWithAppleButton(
onRequest: { !!( },
onCompletion: { result in
!!(
let appleIDToken = appleIDCredential.identityToken
let idTokenString = String(data: appleIDToken, encoding: .utf8)
let credential = OAuthProvider.credential(withProviderID: “apple.com",
idToken: idTokenString,
rawNonce: nonce)
do {
try await Auth.auth().signIn(with: credential)
}
catch {
!& handle error
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
SignInWithAppleButton(
onRequest: { !!( },
onCompletion: { result in
!!(
let appleIDToken = appleIDCredential.identityToken
let idTokenString = String(data: appleIDToken, encoding: .utf8)
let credential = OAuthProvider.credential(withProviderID: “apple.com",
idToken: idTokenString,
rawNonce: nonce)
do {
try await Auth.auth().signIn(with: credential)
}
catch {
!& handle error
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
SignInWithAppleButton(
onRequest: { !!( },
onCompletion: { result in
!!(
let appleIDToken = appleIDCredential.identityToken
let idTokenString = String(data: appleIDToken, encoding: .utf8)
let credential = OAuthProvider.credential(withProviderID: “apple.com",
idToken: idTokenString,
rawNonce: nonce)
do {
try await Auth.auth().signIn(with: credential)
}
catch {
!& handle error
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
SignInWithAppleButton(
onRequest: { !!( },
onCompletion: { result in
!!(
let appleIDToken = appleIDCredential.identityToken
let idTokenString = String(data: appleIDToken, encoding: .utf8)
let credential = OAuthProvider.credential(withProviderID: “apple.com",
idToken: idTokenString,
rawNonce: nonce)
do {
try await Auth.auth().signIn(with: credential)
}
catch {
!& handle error
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
SignInWithAppleButton(
onRequest: { !!( },
onCompletion: { result in
!!(
let appleIDToken = appleIDCredential.identityToken
let idTokenString = String(data: appleIDToken, encoding: .utf8)
let credential = OAuthProvider.credential(withProviderID: “apple.com",
idToken: idTokenString,
rawNonce: nonce)
do {
try await Auth.auth().signIn(with: credential)
}
catch {
!& handle error
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
SignInWithAppleButton(
onRequest: { !!( },
onCompletion: { result in
!!(
let appleIDToken = appleIDCredential.identityToken
let idTokenString = String(data: appleIDToken, encoding: .utf8)
let credential = OAuthProvider.credential(withProviderID: “apple.com",
idToken: idTokenString,
rawNonce: nonce)
do {
try await Auth.auth().signIn(with: credential)
}
catch {
!& handle error
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
Firebase SDK
All todos are stored in one single collection
Which user do
they belong to?
let query = db.collection(“todos")
.whereField("userId",
isEqualTo: self.userId)
query
.addSnapshotListener { [weak self] (querySnapsho
guard let documents = querySnapshot!$documents els
Signed in user
Implementing User
Authentication
Exercise
Implementing User Authentication
1. Add Anonymous Authentication to the app
2. Add Email and Password Authentication to
the app
3. Add Sign in with Apple
Exercise
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if
request.time < timestamp.date(2022, 8, 26);
}
}
}
Security Rules Time-based security == no
security!
Don’t do this at home!
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow create: if request.auth !# null;
      allow read, update, delete: if request.auth !# null 
        !* resource.data.userId !' request.auth.uid;
    }
  }
}
Security Rules
Only signed-in users can
create new documents
Only owners may read and
modify a document
Securing Your Users’ Data
with Security Rules
Exercise
Securing Your Users’ Data with Security Rules
1. Update the Security Rule so only
authenticated users can create todo items
2. Update the Security Rule so only
authenticated users can create todo items
Exercise
Remote Con"g
Photo by Ash Edmonds on Unsplash
Deploy Feature Flags
with Progressive Rollouts
Make changes to your
app without publishing
an App Update
Target speci#c user
segments
Popular use cases
• Activate banner for New Year’s resolution promo at midnight

• Drive iOS adoption by offering a 20% discount to iOS users
(and 10% to Android users)

• Use staged rollout (10% > 25% > 75% > 100%) for the new
search feature

• Show different home screen content to users in USA vs the
UK

• Show customised feed based on user’s preferences for
particular topic / category

• Engage users who are predicted to churn

• Show less ads to users who are predicted to spend in your
app

• Slowly roll out migration to a new API (API URL defined as
a RC key)

• Define entire game levels as JSON/XML config values.
Great for fixing gameplay!

• Host static assets on Firebase Hosting and reference them
dynamically in your game using Remote Config keys

• Disable features at runtime that might be causing high
number of crashes
Set up Remote Config
import FirebaseRemoteConfig
private var remoteConfig: RemoteConfig
func setupRemoteConfig() {
let remoteConfig = RemoteConfig.remoteConfig()
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")
}
Set up Remote Config
import FirebaseRemoteConfig
private var remoteConfig: RemoteConfig
func setupRemoteConfig() {
let remoteConfig = RemoteConfig.remoteConfig()
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")
}
Amount of time before you can
fetch again after a successful fetch
Don’t do this at home!
Set up Remote Config
import FirebaseRemoteConfig
private var remoteConfig: RemoteConfig
func setupRemoteConfig() {
let remoteConfig = RemoteConfig.remoteConfig()
#if DEBUG
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
#endif
remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")
}
Amount of time before you can
fetch again after a successful fetch
Do THIS at home!
Set up Remote Config
import FirebaseRemoteConfig
private var remoteConfig: RemoteConfig
func setupRemoteConfig() {
let remoteConfig = RemoteConfig.remoteConfig()
#if DEBUG
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
#endif
remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")
}
Fetch and apply a value
func fetchConfigutation() {
remoteConfig.fetch { (status, error) !" Void in
if status !' .success {
print("Configuration fetched!")
self.remoteConfig.activate { changed, error in
let value = remoteConfig.configValue(forKey: "key")
!& apply configuration
}
}
else {
print("Configuration not fetched")
print("Error: (error!$localizedDescription !% "No error available.")")
}
}
}
Fetch and apply a value
func fetchConfigutation() {
remoteConfig.fetch { (status, error) !" Void in
if status !' .success {
print("Configuration fetched!")
self.remoteConfig.activate { changed, error in
let value = remoteConfig.configValue(forKey: "key")
!& apply configuration
}
}
else {
print("Configuration not fetched")
print("Error: (error!$localizedDescription !% "No error available.")")
}
}
}
Fetch and apply a value
func fetchConfigutation() {
remoteConfig.fetch { (status, error) !" Void in
if status !' .success {
print("Configuration fetched!")
self.remoteConfig.activate { changed, error in
let value = remoteConfig.configValue(forKey: "key")
!& apply configuration
}
}
else {
print("Configuration not fetched")
print("Error: (error!$localizedDescription !% "No error available.")")
}
}
}
Fetch and apply a value w/ async/await
func fetchConfigutation() async {
do {
let status = try await remoteConfig.fetch()
if status !' .success {
print("Configuration fetched!")
try await remoteConfig.activate()
let value = remoteConfig.configValue(forKey: "")
}
}
catch {
!& handle error
}
}
Fetch and apply a value w/ async/await
func fetchConfigutation() async {
do {
let status = try await remoteConfig.fetch()
if status !' .success {
print("Configuration fetched!")
try await remoteConfig.activate()
let value = remoteConfig.configValue(forKey: "")
}
}
catch {
!& handle error
}
}
Fetch and apply a value w/ async/await
func fetchConfigutation() async {
do {
let status = try await remoteConfig.fetch()
if status !' .success {
print("Configuration fetched!")
try await remoteConfig.activate()
let value = remoteConfig.configValue(forKey: "")
}
}
catch {
!& handle error
}
}
Implementing
Remote Con"g
Exercise
Implementing Remote Con"g
1. Create a con#guration to hide/show the
details bu$on for each todo item
2. Fetch the con#guration when your app
sta", and apply to the UI
3. Launch this con#guration to 10% of your
users only
Exercise
The End
Photo by Paul Hudson (@twostraws) on Twi$er
The End
The End
)
(for real
The End
(for real now - why don’t you go and grab a drink )

More Related Content

What's hot (10)

PPTX
JDBC - JPA - Spring Data
Arturs Drozdovs
 
PDF
MongoDB and Ruby on Rails
rfischer20
 
PPT
Spring AOP
AnushaNaidu
 
PDF
JSON Array Indexes in MySQL
Norvald Ryeng
 
PDF
Spring Data JPA
Knoldus Inc.
 
PDF
ADRという考えを取り入れてみて
infinite_loop
 
PDF
Spring Framework - Spring Security
Dzmitry Naskou
 
PDF
Redux Saga - Under the hood
Waqqas Jabbar
 
PPTX
Modern JS with ES6
Kevin Langley Jr.
 
DOCX
Organizational Behaviour
varun23oct
 
JDBC - JPA - Spring Data
Arturs Drozdovs
 
MongoDB and Ruby on Rails
rfischer20
 
Spring AOP
AnushaNaidu
 
JSON Array Indexes in MySQL
Norvald Ryeng
 
Spring Data JPA
Knoldus Inc.
 
ADRという考えを取り入れてみて
infinite_loop
 
Spring Framework - Spring Security
Dzmitry Naskou
 
Redux Saga - Under the hood
Waqqas Jabbar
 
Modern JS with ES6
Kevin Langley Jr.
 
Organizational Behaviour
varun23oct
 

Similar to Firebase & SwiftUI Workshop (20)

PDF
Firebase for Apple Developers - SwiftHeroes
Peter Friese
 
PDF
Firebase for Apple Developers
Peter Friese
 
PDF
Kitura Todolist tutorial
Robert F. Dickerson
 
PDF
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
Peter Friese
 
PDF
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Chris Adamson
 
PDF
Firebase: Totally Not Parse All Over Again (Unless It Is)
Chris Adamson
 
PDF
Building Apps with SwiftUI and Firebase
Peter Friese
 
PDF
Building an Android app with Jetpack Compose and Firebase
Marina Coelho
 
PDF
Rethinking Syncing at AltConf 2019
Joe Keeley
 
PDF
Três conceitos que farão a diferença nos seus apps
Guilherme Rambo
 
PDF
Prescribing RX Responsibly
Nareg Khoshafian
 
PDF
Rapid Application Development with SwiftUI and Firebase
Peter Friese
 
PDF
Firestore MENA digital days : GDG Abu dhabi
Shashank Kakroo
 
PPTX
IndexedDB and Push Notifications in Progressive Web Apps
Adégòkè Obasá
 
PPTX
Advance Mobile Application Development class 07
Dr. Mazin Mohamed alkathiri
 
PDF
Rapid Application Development with SwiftUI and Firebase
Peter Friese
 
PDF
Elements for an iOS Backend
Laurent Cerveau
 
PDF
The combine triad
Donny Wals
 
PDF
Android architecture components with cloud firestore
Pankaj Rai
 
PDF
Optimize CollectionView Scrolling
Andrea Prearo
 
Firebase for Apple Developers - SwiftHeroes
Peter Friese
 
Firebase for Apple Developers
Peter Friese
 
Kitura Todolist tutorial
Robert F. Dickerson
 
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
Peter Friese
 
Firebase: Totally Not Parse All Over Again (Unless It Is) (CocoaConf San Jose...
Chris Adamson
 
Firebase: Totally Not Parse All Over Again (Unless It Is)
Chris Adamson
 
Building Apps with SwiftUI and Firebase
Peter Friese
 
Building an Android app with Jetpack Compose and Firebase
Marina Coelho
 
Rethinking Syncing at AltConf 2019
Joe Keeley
 
Três conceitos que farão a diferença nos seus apps
Guilherme Rambo
 
Prescribing RX Responsibly
Nareg Khoshafian
 
Rapid Application Development with SwiftUI and Firebase
Peter Friese
 
Firestore MENA digital days : GDG Abu dhabi
Shashank Kakroo
 
IndexedDB and Push Notifications in Progressive Web Apps
Adégòkè Obasá
 
Advance Mobile Application Development class 07
Dr. Mazin Mohamed alkathiri
 
Rapid Application Development with SwiftUI and Firebase
Peter Friese
 
Elements for an iOS Backend
Laurent Cerveau
 
The combine triad
Donny Wals
 
Android architecture components with cloud firestore
Pankaj Rai
 
Optimize CollectionView Scrolling
Andrea Prearo
 
Ad

More from Peter Friese (20)

PDF
Building Reusable SwiftUI Components
Peter Friese
 
PDF
Building Reusable SwiftUI Components
Peter Friese
 
PDF
async/await in Swift
Peter Friese
 
PDF
6 Things You Didn't Know About Firebase Auth
Peter Friese
 
PDF
Five Things You Didn't Know About Firebase Auth
Peter Friese
 
PDF
Building High-Quality Apps for Google Assistant
Peter Friese
 
PDF
Building Conversational Experiences with Actions on Google
Peter Friese
 
PDF
Building Conversational Experiences with Actions on Google
Peter Friese
 
PDF
What's new in Android Wear 2.0
Peter Friese
 
PDF
Google Fit, Android Wear & Xamarin
Peter Friese
 
PDF
Introduction to Android Wear
Peter Friese
 
PDF
Google Play Services Rock
Peter Friese
 
PDF
Introduction to Android Wear
Peter Friese
 
PDF
Google+ for Mobile Apps on iOS and Android
Peter Friese
 
PDF
Cross-Platform Authentication with Google+ Sign-In
Peter Friese
 
PDF
Bring Back the Fun to Testing Android Apps with Robolectric
Peter Friese
 
PDF
Do Androids Dream of Electric Sheep
Peter Friese
 
PDF
Java based Cross-Platform Mobile Development
Peter Friese
 
PDF
CouchDB Mobile - From Couch to 5K in 1 Hour
Peter Friese
 
PDF
RestKit - From Zero to Hero
Peter Friese
 
Building Reusable SwiftUI Components
Peter Friese
 
Building Reusable SwiftUI Components
Peter Friese
 
async/await in Swift
Peter Friese
 
6 Things You Didn't Know About Firebase Auth
Peter Friese
 
Five Things You Didn't Know About Firebase Auth
Peter Friese
 
Building High-Quality Apps for Google Assistant
Peter Friese
 
Building Conversational Experiences with Actions on Google
Peter Friese
 
Building Conversational Experiences with Actions on Google
Peter Friese
 
What's new in Android Wear 2.0
Peter Friese
 
Google Fit, Android Wear & Xamarin
Peter Friese
 
Introduction to Android Wear
Peter Friese
 
Google Play Services Rock
Peter Friese
 
Introduction to Android Wear
Peter Friese
 
Google+ for Mobile Apps on iOS and Android
Peter Friese
 
Cross-Platform Authentication with Google+ Sign-In
Peter Friese
 
Bring Back the Fun to Testing Android Apps with Robolectric
Peter Friese
 
Do Androids Dream of Electric Sheep
Peter Friese
 
Java based Cross-Platform Mobile Development
Peter Friese
 
CouchDB Mobile - From Couch to 5K in 1 Hour
Peter Friese
 
RestKit - From Zero to Hero
Peter Friese
 
Ad

Recently uploaded (20)

PPTX
Library_Management_System_PPT111111.pptx
nmtnissancrm
 
PDF
SciPy 2025 - Packaging a Scientific Python Project
Henry Schreiner
 
PDF
Technical-Careers-Roadmap-in-Software-Market.pdf
Hussein Ali
 
PPTX
Comprehensive Risk Assessment Module for Smarter Risk Management
EHA Soft Solutions
 
PPTX
Get Started with Maestro: Agent, Robot, and Human in Action – Session 5 of 5
klpathrudu
 
PDF
Wondershare PDFelement Pro Crack for MacOS New Version Latest 2025
bashirkhan333g
 
PDF
AI Prompts Cheat Code prompt engineering
Avijit Kumar Roy
 
PDF
Is Framer the Future of AI Powered No-Code Development?
Isla Pandora
 
PDF
Meet in the Middle: Solving the Low-Latency Challenge for Agentic AI
Alluxio, Inc.
 
PDF
AI + DevOps = Smart Automation with devseccops.ai.pdf
Devseccops.ai
 
PPTX
prodad heroglyph crack 2.0.214.2 Full Free Download
cracked shares
 
PDF
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
PDF
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
PDF
Salesforce Experience Cloud Consultant.pdf
VALiNTRY360
 
PDF
Dipole Tech Innovations – Global IT Solutions for Business Growth
dipoletechi3
 
PPTX
Build a Custom Agent for Agentic Testing.pptx
klpathrudu
 
PDF
Empower Your Tech Vision- Why Businesses Prefer to Hire Remote Developers fro...
logixshapers59
 
PPTX
UI5con_2025_Accessibility_Ever_Evolving_
gerganakremenska1
 
PPTX
Foundations of Marketo Engage - Powering Campaigns with Marketo Personalization
bbedford2
 
PDF
Download Canva Pro 2025 PC Crack Full Latest Version
bashirkhan333g
 
Library_Management_System_PPT111111.pptx
nmtnissancrm
 
SciPy 2025 - Packaging a Scientific Python Project
Henry Schreiner
 
Technical-Careers-Roadmap-in-Software-Market.pdf
Hussein Ali
 
Comprehensive Risk Assessment Module for Smarter Risk Management
EHA Soft Solutions
 
Get Started with Maestro: Agent, Robot, and Human in Action – Session 5 of 5
klpathrudu
 
Wondershare PDFelement Pro Crack for MacOS New Version Latest 2025
bashirkhan333g
 
AI Prompts Cheat Code prompt engineering
Avijit Kumar Roy
 
Is Framer the Future of AI Powered No-Code Development?
Isla Pandora
 
Meet in the Middle: Solving the Low-Latency Challenge for Agentic AI
Alluxio, Inc.
 
AI + DevOps = Smart Automation with devseccops.ai.pdf
Devseccops.ai
 
prodad heroglyph crack 2.0.214.2 Full Free Download
cracked shares
 
MiniTool Partition Wizard Free Crack + Full Free Download 2025
bashirkhan333g
 
SAP Firmaya İade ABAB Kodları - ABAB ile yazılmıl hazır kod örneği
Salih Küçük
 
Salesforce Experience Cloud Consultant.pdf
VALiNTRY360
 
Dipole Tech Innovations – Global IT Solutions for Business Growth
dipoletechi3
 
Build a Custom Agent for Agentic Testing.pptx
klpathrudu
 
Empower Your Tech Vision- Why Businesses Prefer to Hire Remote Developers fro...
logixshapers59
 
UI5con_2025_Accessibility_Ever_Evolving_
gerganakremenska1
 
Foundations of Marketo Engage - Powering Campaigns with Marketo Personalization
bbedford2
 
Download Canva Pro 2025 PC Crack Full Latest Version
bashirkhan333g
 

Firebase & SwiftUI Workshop

  • 1. Peter Friese | Developer Advocate, Firebase Marina Coelho | Developer Relations Engineer, Firebase  + Swi!UI & Firebase Workshop @coelho_dev @pete!riese
  • 5. Make It So ✨ Simple to-do list app ✨ Store to-dos in Cloud Firestore ✨ Real-time updates across devices ✨ Use without user account (“try before you buy”) ✨ Sign in with Email/Password ✨ Sign in with Apple ✨ Feature flags using Remote Config ✨ There is an Android version as well! (bit.ly/makeitso-android-4)
  • 8. Run with confidence Engage users Develop apps faster
  • 9. Run with confidence Crashlytics Performance Monitoring Test Lab App Distribution Engage users Analytics Predictions Cloud Messaging Remote Config A/B Testing Dynamic Links In-app Messaging Develop apps faster Auth Cloud Functions Cloud Firestore Hosting ML Kit Realtime Database Cloud Storage bit.ly/what-is-firebase Extensions Machine Learning
  • 10. Adding Firebase to a Swi!UI app Exercise
  • 11. Adding Firebase to a Swi!UI app Exercise 1. Create a new Firebase project 2. Add your app to the new Firebase project 3. Download GoogleServices-Info.plist to your app
  • 12. Application Lifecycle SwiftUI 2 Photo by Thor Alvis on Unsplash
  • 13. SwiftUI 2: No more AppDelegate! import SwiftUI @main struct MakeItSoApp: App { var body: some Scene { WindowGroup { TodosListView() } } }
  • 14. SwiftUI 2: No more AppDelegate! import SwiftUI @main struct MakeItSoApp: App { var body: some Scene { WindowGroup { TodosListView() } } }
  • 15. Solution 1: use initialiser import SwiftUI @main struct MakeItSoApp: App { var body: some Scene { WindowGroup { TodosListView() } } }
  • 16. Solution 1: use initialiser import SwiftUI @main struct MakeItSoApp: App { var body: some Scene { WindowGroup { TodosListView() } } } init() { FirebaseApp.configure() }
  • 17. Solution 2: use UIApplicationDelegateAdaptor import SwiftUI import Firebase class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool { FirebaseApp.configure() return true } } @main struct MakeItSoApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 18. Solution 2: use UIApplicationDelegateAdaptor import SwiftUI import Firebase class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool { FirebaseApp.configure() return true } } @main struct MakeItSoApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 19. Solution 2: use UIApplicationDelegateAdaptor import SwiftUI import Firebase class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool { FirebaseApp.configure() return true } } @main struct MakeItSoApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 20. Solution 2: use UIApplicationDelegateAdaptor import SwiftUI import Firebase class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) !" Bool { FirebaseApp.configure() return true } } @main struct MakeItSoApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 21. Watch the scene phase Handle deep links Continue user activities Do more with the new life cycle
  • 22. Watch the scene phase Handle deep links Continue user activities Learn more pete!riese.dev/ultimate-guide-to-swi"ui2-application-lifecycle/
  • 30. struct Todo { @DocumentID var docId: String? var id: String? = UUID().uuidString var title: String var completed: Bool = false var userId: String? } Data Model
  • 31. TodosListView TodoDetailsView TodoListRowView Source of Truth todos: [Todo] @StateObject @Binding Todo Todo Review: Architecture
  • 32. TodosListView TodoDetailsView TodoListRowView Source of Truth todos: [Todo] @StateObject @Binding Todo Todo Review: Architecture Snapshot Listener
  • 33. class TodosRepository: ObservableObject { @Injected var db: Firestore private var listenerRegistration: ListenerRegistration? @Published var todos = [Todo]() func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID Real-time Sync w/ Snapshot Listeners
  • 34. class TodosRepository: ObservableObject { @Injected var db: Firestore private var listenerRegistration: ListenerRegistration? @Published var todos = [Todo]() func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID Real-time Sync w/ Snapshot Listeners
  • 35. class TodosRepository: ObservableObject { @Injected var db: Firestore private var listenerRegistration: ListenerRegistration? @Published var todos = [Todo]() func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID Real-time Sync w/ Snapshot Listeners
  • 36. class TodosRepository: ObservableObject { @Injected var db: Firestore private var listenerRegistration: ListenerRegistration? @Published var todos = [Todo]() func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID Real-time Sync w/ Snapshot Listeners
  • 37. func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID let data = queryDocumentSnapshot.data() let id = queryDocumentSnapshot["id"] as? String !% "" let title = data["title"] as? String !% "" let completed = data["completed"] as? Bool !% false let userId = data["userId"] as? String !% "" return Todo(docId: docId, id: id, title: title, completed: completed, userId: userId) } } Real-time Sync w/ Snapshot Listeners
  • 38. func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID let data = queryDocumentSnapshot.data() let id = queryDocumentSnapshot["id"] as? String !% "" let title = data["title"] as? String !% "" let completed = data["completed"] as? Bool !% false let userId = data["userId"] as? String !% "" return Todo(docId: docId, id: id, title: title, completed: completed, userId: userId) } } Real-time Sync w/ Snapshot Listeners
  • 39. func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID let data = queryDocumentSnapshot.data() let id = queryDocumentSnapshot["id"] as? String !% "" let title = data["title"] as? String !% "" let completed = data["completed"] as? Bool !% false let userId = data["userId"] as? String !% "" return Todo(docId: docId, id: id, title: title, completed: completed, userId: userId) } } Real-time Sync w/ Snapshot Listeners Can we do better?
  • 41. func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let docId = queryDocumentSnapshot.documentID let data = queryDocumentSnapshot.data() let id = queryDocumentSnapshot["id"] as? String !% "" let title = data["title"] as? String !% "" let completed = data["completed"] as? Bool !% false let userId = data["userId"] as? String !% "" return Todo(docId: docId, id: id, title: title, completed: completed, userId: userId) } } Real-time Sync w/ Snapshot Listeners
  • 42. func subscribe() { if listenerRegistration !# nil { unsubscribe() } listenerRegistration = db.collection("todos") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot!$documents else { return } self!$todos = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Todo.self) } switch result { case .success(let todo): return todo case .failure(let error): !& handle error return nil } } } Real-time Sync w/ Snapshot Listeners
  • 45. Sync Data with Firestore 1. Add Snapshot listeners and display data in the UI 2. Add new todo items via the app 3. Update todo items via the app 4.Delete todo items via the app Exercise
  • 46. struct BookShelfView: View { @FirestoreQuery( collectionPath: "todos", predicates: [ .where("userId", isEqualTo: userId), ] ) var todos: Result<[Todo], Error> @State var userId = "F18EBA5E" var body: some View { List(todos) { todo in Text(todo.title) } } } Firestore Property Wrapper Firebase 8.9.0 @FloWritesCode @mo%enditlevsen Thanks to
  • 48. Authentication Photo by Eduardo Soares on Unsplash
  • 49. Authentication Photo by Victor Freitas on Unsplash
  • 50.
  • 51. Sign the user in Update the data model Secure users’ data How to implement Firebase Authentication?
  • 53. func signIn() { registerStateListener() if Auth.auth().currentUser !' nil { Auth.auth().signInAnonymously() } } Anonymous Authentication
  • 55. SignInWithAppleButton( onRequest: { !!( }, onCompletion: { result in !!( let appleIDToken = appleIDCredential.identityToken let idTokenString = String(data: appleIDToken, encoding: .utf8) let credential = OAuthProvider.credential(withProviderID: “apple.com", idToken: idTokenString, rawNonce: nonce) do { try await Auth.auth().signIn(with: credential) } catch { !& handle error } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 56. SignInWithAppleButton( onRequest: { !!( }, onCompletion: { result in !!( let appleIDToken = appleIDCredential.identityToken let idTokenString = String(data: appleIDToken, encoding: .utf8) let credential = OAuthProvider.credential(withProviderID: “apple.com", idToken: idTokenString, rawNonce: nonce) do { try await Auth.auth().signIn(with: credential) } catch { !& handle error } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 57. SignInWithAppleButton( onRequest: { !!( }, onCompletion: { result in !!( let appleIDToken = appleIDCredential.identityToken let idTokenString = String(data: appleIDToken, encoding: .utf8) let credential = OAuthProvider.credential(withProviderID: “apple.com", idToken: idTokenString, rawNonce: nonce) do { try await Auth.auth().signIn(with: credential) } catch { !& handle error } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 58. SignInWithAppleButton( onRequest: { !!( }, onCompletion: { result in !!( let appleIDToken = appleIDCredential.identityToken let idTokenString = String(data: appleIDToken, encoding: .utf8) let credential = OAuthProvider.credential(withProviderID: “apple.com", idToken: idTokenString, rawNonce: nonce) do { try await Auth.auth().signIn(with: credential) } catch { !& handle error } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 59. SignInWithAppleButton( onRequest: { !!( }, onCompletion: { result in !!( let appleIDToken = appleIDCredential.identityToken let idTokenString = String(data: appleIDToken, encoding: .utf8) let credential = OAuthProvider.credential(withProviderID: “apple.com", idToken: idTokenString, rawNonce: nonce) do { try await Auth.auth().signIn(with: credential) } catch { !& handle error } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 60. SignInWithAppleButton( onRequest: { !!( }, onCompletion: { result in !!( let appleIDToken = appleIDCredential.identityToken let idTokenString = String(data: appleIDToken, encoding: .utf8) let credential = OAuthProvider.credential(withProviderID: “apple.com", idToken: idTokenString, rawNonce: nonce) do { try await Auth.auth().signIn(with: credential) } catch { !& handle error } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple Firebase SDK
  • 61. All todos are stored in one single collection Which user do they belong to?
  • 62. let query = db.collection(“todos") .whereField("userId", isEqualTo: self.userId) query .addSnapshotListener { [weak self] (querySnapsho guard let documents = querySnapshot!$documents els Signed in user
  • 64. Implementing User Authentication 1. Add Anonymous Authentication to the app 2. Add Email and Password Authentication to the app 3. Add Sign in with Apple Exercise
  • 65. rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.time < timestamp.date(2022, 8, 26); } } } Security Rules Time-based security == no security! Don’t do this at home!
  • 66. rules_version = '2'; service cloud.firestore {   match /databases/{database}/documents {     match /{document=**} {       allow create: if request.auth !# null;       allow read, update, delete: if request.auth !# null          !* resource.data.userId !' request.auth.uid;     }   } } Security Rules Only signed-in users can create new documents Only owners may read and modify a document
  • 67. Securing Your Users’ Data with Security Rules Exercise
  • 68. Securing Your Users’ Data with Security Rules 1. Update the Security Rule so only authenticated users can create todo items 2. Update the Security Rule so only authenticated users can create todo items Exercise
  • 69. Remote Con"g Photo by Ash Edmonds on Unsplash
  • 70. Deploy Feature Flags with Progressive Rollouts
  • 71. Make changes to your app without publishing an App Update
  • 73. Popular use cases • Activate banner for New Year’s resolution promo at midnight • Drive iOS adoption by offering a 20% discount to iOS users (and 10% to Android users) • Use staged rollout (10% > 25% > 75% > 100%) for the new search feature • Show different home screen content to users in USA vs the UK • Show customised feed based on user’s preferences for particular topic / category • Engage users who are predicted to churn • Show less ads to users who are predicted to spend in your app • Slowly roll out migration to a new API (API URL defined as a RC key) • Define entire game levels as JSON/XML config values. Great for fixing gameplay! • Host static assets on Firebase Hosting and reference them dynamically in your game using Remote Config keys • Disable features at runtime that might be causing high number of crashes
  • 74. Set up Remote Config import FirebaseRemoteConfig private var remoteConfig: RemoteConfig func setupRemoteConfig() { let remoteConfig = RemoteConfig.remoteConfig() let settings = RemoteConfigSettings() settings.minimumFetchInterval = 0 remoteConfig.configSettings = settings remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults") }
  • 75. Set up Remote Config import FirebaseRemoteConfig private var remoteConfig: RemoteConfig func setupRemoteConfig() { let remoteConfig = RemoteConfig.remoteConfig() let settings = RemoteConfigSettings() settings.minimumFetchInterval = 0 remoteConfig.configSettings = settings remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults") } Amount of time before you can fetch again after a successful fetch Don’t do this at home!
  • 76. Set up Remote Config import FirebaseRemoteConfig private var remoteConfig: RemoteConfig func setupRemoteConfig() { let remoteConfig = RemoteConfig.remoteConfig() #if DEBUG let settings = RemoteConfigSettings() settings.minimumFetchInterval = 0 remoteConfig.configSettings = settings #endif remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults") } Amount of time before you can fetch again after a successful fetch Do THIS at home!
  • 77. Set up Remote Config import FirebaseRemoteConfig private var remoteConfig: RemoteConfig func setupRemoteConfig() { let remoteConfig = RemoteConfig.remoteConfig() #if DEBUG let settings = RemoteConfigSettings() settings.minimumFetchInterval = 0 remoteConfig.configSettings = settings #endif remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults") }
  • 78. Fetch and apply a value func fetchConfigutation() { remoteConfig.fetch { (status, error) !" Void in if status !' .success { print("Configuration fetched!") self.remoteConfig.activate { changed, error in let value = remoteConfig.configValue(forKey: "key") !& apply configuration } } else { print("Configuration not fetched") print("Error: (error!$localizedDescription !% "No error available.")") } } }
  • 79. Fetch and apply a value func fetchConfigutation() { remoteConfig.fetch { (status, error) !" Void in if status !' .success { print("Configuration fetched!") self.remoteConfig.activate { changed, error in let value = remoteConfig.configValue(forKey: "key") !& apply configuration } } else { print("Configuration not fetched") print("Error: (error!$localizedDescription !% "No error available.")") } } }
  • 80. Fetch and apply a value func fetchConfigutation() { remoteConfig.fetch { (status, error) !" Void in if status !' .success { print("Configuration fetched!") self.remoteConfig.activate { changed, error in let value = remoteConfig.configValue(forKey: "key") !& apply configuration } } else { print("Configuration not fetched") print("Error: (error!$localizedDescription !% "No error available.")") } } }
  • 81. Fetch and apply a value w/ async/await func fetchConfigutation() async { do { let status = try await remoteConfig.fetch() if status !' .success { print("Configuration fetched!") try await remoteConfig.activate() let value = remoteConfig.configValue(forKey: "") } } catch { !& handle error } }
  • 82. Fetch and apply a value w/ async/await func fetchConfigutation() async { do { let status = try await remoteConfig.fetch() if status !' .success { print("Configuration fetched!") try await remoteConfig.activate() let value = remoteConfig.configValue(forKey: "") } } catch { !& handle error } }
  • 83. Fetch and apply a value w/ async/await func fetchConfigutation() async { do { let status = try await remoteConfig.fetch() if status !' .success { print("Configuration fetched!") try await remoteConfig.activate() let value = remoteConfig.configValue(forKey: "") } } catch { !& handle error } }
  • 85. Implementing Remote Con"g 1. Create a con#guration to hide/show the details bu$on for each todo item 2. Fetch the con#guration when your app sta", and apply to the UI 3. Launch this con#guration to 10% of your users only Exercise
  • 86. The End Photo by Paul Hudson (@twostraws) on Twi$er
  • 89. The End (for real now - why don’t you go and grab a drink )