SlideShare a Scribd company logo
1 of 89
Download to read offline
Peter Friese | Developer Advocate, Firebase
Marina Coelho | Developer Relations Engineer, Firebase
 +
Swi!UI & Firebase Workshop
@coelho_dev @pete!riese
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
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
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
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

Firebase Auth Tutorial
Firebase Auth TutorialFirebase Auth Tutorial
Firebase Auth TutorialBukhori Aqid
 
Secure Your Code Implement DevSecOps in Azure
Secure Your Code Implement DevSecOps in AzureSecure Your Code Implement DevSecOps in Azure
Secure Your Code Implement DevSecOps in Azurekloia
 
DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...
DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...
DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...Simplilearn
 
Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...
Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...
Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...Edureka!
 
Docker Compose by Aanand Prasad
Docker Compose by Aanand Prasad Docker Compose by Aanand Prasad
Docker Compose by Aanand Prasad Docker, Inc.
 
Introduction to Swagger
Introduction to SwaggerIntroduction to Swagger
Introduction to SwaggerKnoldus Inc.
 
Videostream compression in iOS
Videostream compression in iOSVideostream compression in iOS
Videostream compression in iOS*instinctools
 
Continuous Integration (Jenkins/Hudson)
Continuous Integration (Jenkins/Hudson)Continuous Integration (Jenkins/Hudson)
Continuous Integration (Jenkins/Hudson)Dennys Hsieh
 
Hands On with Maven
Hands On with MavenHands On with Maven
Hands On with MavenSid Anand
 
Docker and Kubernetes 101 workshop
Docker and Kubernetes 101 workshopDocker and Kubernetes 101 workshop
Docker and Kubernetes 101 workshopSathish VJ
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해beom kyun choi
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
Introduction to Docker - IndiaOpsUG
Introduction to Docker - IndiaOpsUGIntroduction to Docker - IndiaOpsUG
Introduction to Docker - IndiaOpsUGAjeet Singh Raina
 
introduction to Vue.js 3
introduction to Vue.js 3 introduction to Vue.js 3
introduction to Vue.js 3 ArezooKmn
 
Terraform: An Overview & Introduction
Terraform: An Overview & IntroductionTerraform: An Overview & Introduction
Terraform: An Overview & IntroductionLee Trout
 

What's hot (20)

Firebase Auth Tutorial
Firebase Auth TutorialFirebase Auth Tutorial
Firebase Auth Tutorial
 
Secure Your Code Implement DevSecOps in Azure
Secure Your Code Implement DevSecOps in AzureSecure Your Code Implement DevSecOps in Azure
Secure Your Code Implement DevSecOps in Azure
 
DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...
DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...
DevOps Training | DevOps Training Video | DevOps Tools | DevOps Tutorial For ...
 
Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...
Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...
Introduction to DevOps Tools | DevOps Training | DevOps Tutorial for Beginner...
 
Docker Compose by Aanand Prasad
Docker Compose by Aanand Prasad Docker Compose by Aanand Prasad
Docker Compose by Aanand Prasad
 
Introduction to Swagger
Introduction to SwaggerIntroduction to Swagger
Introduction to Swagger
 
Videostream compression in iOS
Videostream compression in iOSVideostream compression in iOS
Videostream compression in iOS
 
Continuous Integration (Jenkins/Hudson)
Continuous Integration (Jenkins/Hudson)Continuous Integration (Jenkins/Hudson)
Continuous Integration (Jenkins/Hudson)
 
Hands On with Maven
Hands On with MavenHands On with Maven
Hands On with Maven
 
Docker and Kubernetes 101 workshop
Docker and Kubernetes 101 workshopDocker and Kubernetes 101 workshop
Docker and Kubernetes 101 workshop
 
Jenkins with SonarQube
Jenkins with SonarQubeJenkins with SonarQube
Jenkins with SonarQube
 
스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해스프링 시큐리티 구조 이해
스프링 시큐리티 구조 이해
 
Terraform on Azure
Terraform on AzureTerraform on Azure
Terraform on Azure
 
Jenkins
JenkinsJenkins
Jenkins
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Maven Introduction
Maven IntroductionMaven Introduction
Maven Introduction
 
Introduction to Docker - IndiaOpsUG
Introduction to Docker - IndiaOpsUGIntroduction to Docker - IndiaOpsUG
Introduction to Docker - IndiaOpsUG
 
introduction to Vue.js 3
introduction to Vue.js 3 introduction to Vue.js 3
introduction to Vue.js 3
 
CI/CD on AWS
CI/CD on AWSCI/CD on AWS
CI/CD on AWS
 
Terraform: An Overview & Introduction
Terraform: An Overview & IntroductionTerraform: An Overview & Introduction
Terraform: An Overview & Introduction
 

Similar to Firebase & SwiftUI Workshop

WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015Fernando Daciuk
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkDaniel Spector
 
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift LeedsPeter Friese
 
Asynchronous Programming at Netflix
Asynchronous Programming at NetflixAsynchronous Programming at Netflix
Asynchronous Programming at NetflixC4Media
 
Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011Puppet
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5Darren Craig
 
Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3Katy Slemon
 
Building an Android app with Jetpack Compose and Firebase
Building an Android app with Jetpack Compose and FirebaseBuilding an Android app with Jetpack Compose and Firebase
Building an Android app with Jetpack Compose and FirebaseMarina Coelho
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your appsJuan C Catalan
 
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)Amazon Web Services
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
A resource oriented framework using the DI/AOP/REST triangle
A resource oriented framework using the DI/AOP/REST triangleA resource oriented framework using the DI/AOP/REST triangle
A resource oriented framework using the DI/AOP/REST triangleAkihito Koriyama
 
Introduction to Spring Boot.pdf
Introduction to Spring Boot.pdfIntroduction to Spring Boot.pdf
Introduction to Spring Boot.pdfShaiAlmog1
 
Tasks: you gotta know how to run them
Tasks: you gotta know how to run themTasks: you gotta know how to run them
Tasks: you gotta know how to run themFilipe Ximenes
 
Flask patterns
Flask patternsFlask patterns
Flask patternsit-people
 
Lecture 11 Firebase overview
Lecture 11 Firebase overviewLecture 11 Firebase overview
Lecture 11 Firebase overviewMaksym Davydov
 
The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]Nilhcem
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsLoiane Groner
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud CastlesBen Scofield
 

Similar to Firebase & SwiftUI Workshop (20)

WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015WordPress Realtime - WordCamp São Paulo 2015
WordPress Realtime - WordCamp São Paulo 2015
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end Framework
 
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
 
Asynchronous Programming at Netflix
Asynchronous Programming at NetflixAsynchronous Programming at Netflix
Asynchronous Programming at Netflix
 
Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011Oliver hookins puppetcamp2011
Oliver hookins puppetcamp2011
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5
 
Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3Crud operations using aws dynamo db with flask ap is and boto3
Crud operations using aws dynamo db with flask ap is and boto3
 
Building an Android app with Jetpack Compose and Firebase
Building an Android app with Jetpack Compose and FirebaseBuilding an Android app with Jetpack Compose and Firebase
Building an Android app with Jetpack Compose and Firebase
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
AWS re:Invent 2016: Chalice: A Serverless Microframework for Python (DEV308)
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
A resource oriented framework using the DI/AOP/REST triangle
A resource oriented framework using the DI/AOP/REST triangleA resource oriented framework using the DI/AOP/REST triangle
A resource oriented framework using the DI/AOP/REST triangle
 
Introduction to Spring Boot.pdf
Introduction to Spring Boot.pdfIntroduction to Spring Boot.pdf
Introduction to Spring Boot.pdf
 
Tasks: you gotta know how to run them
Tasks: you gotta know how to run themTasks: you gotta know how to run them
Tasks: you gotta know how to run them
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 
Lecture 11 Firebase overview
Lecture 11 Firebase overviewLecture 11 Firebase overview
Lecture 11 Firebase overview
 
The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]The 2016 Android Developer Toolbox [MOBILIZATION]
The 2016 Android Developer Toolbox [MOBILIZATION]
 
Serverless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applicationsServerless Angular, Material, Firebase and Google Cloud applications
Serverless Angular, Material, Firebase and Google Cloud applications
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud Castles
 

More from Peter Friese

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
Firebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroesFirebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroesPeter Friese
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in SwiftPeter Friese
 
Firebase for Apple Developers
Firebase for Apple DevelopersFirebase for Apple Developers
Firebase for Apple DevelopersPeter Friese
 
Building Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebaseBuilding Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebasePeter Friese
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebasePeter Friese
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebasePeter Friese
 
6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase Auth6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase AuthPeter Friese
 
Five Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase AuthFive Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase AuthPeter Friese
 
Building High-Quality Apps for Google Assistant
Building High-Quality Apps for Google AssistantBuilding High-Quality Apps for Google Assistant
Building High-Quality Apps for Google AssistantPeter Friese
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google Peter Friese
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on GoogleBuilding Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on GooglePeter Friese
 
What's new in Android Wear 2.0
What's new in Android Wear 2.0What's new in Android Wear 2.0
What's new in Android Wear 2.0Peter Friese
 
Google Fit, Android Wear & Xamarin
Google Fit, Android Wear & XamarinGoogle Fit, Android Wear & Xamarin
Google Fit, Android Wear & XamarinPeter Friese
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android WearPeter Friese
 
Google Play Services Rock
Google Play Services RockGoogle Play Services Rock
Google Play Services RockPeter Friese
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android WearPeter Friese
 
Google+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and AndroidGoogle+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and AndroidPeter Friese
 
Cross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-InCross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-InPeter Friese
 
Bring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with RobolectricBring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with RobolectricPeter Friese
 

More from Peter Friese (20)

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Firebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroesFirebase for Apple Developers - SwiftHeroes
Firebase for Apple Developers - SwiftHeroes
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
 
Firebase for Apple Developers
Firebase for Apple DevelopersFirebase for Apple Developers
Firebase for Apple Developers
 
Building Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebaseBuilding Apps with SwiftUI and Firebase
Building Apps with SwiftUI and Firebase
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and Firebase
 
Rapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and FirebaseRapid Application Development with SwiftUI and Firebase
Rapid Application Development with SwiftUI and Firebase
 
6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase Auth6 Things You Didn't Know About Firebase Auth
6 Things You Didn't Know About Firebase Auth
 
Five Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase AuthFive Things You Didn't Know About Firebase Auth
Five Things You Didn't Know About Firebase Auth
 
Building High-Quality Apps for Google Assistant
Building High-Quality Apps for Google AssistantBuilding High-Quality Apps for Google Assistant
Building High-Quality Apps for Google Assistant
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google
 
Building Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on GoogleBuilding Conversational Experiences with Actions on Google
Building Conversational Experiences with Actions on Google
 
What's new in Android Wear 2.0
What's new in Android Wear 2.0What's new in Android Wear 2.0
What's new in Android Wear 2.0
 
Google Fit, Android Wear & Xamarin
Google Fit, Android Wear & XamarinGoogle Fit, Android Wear & Xamarin
Google Fit, Android Wear & Xamarin
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android Wear
 
Google Play Services Rock
Google Play Services RockGoogle Play Services Rock
Google Play Services Rock
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android Wear
 
Google+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and AndroidGoogle+ for Mobile Apps on iOS and Android
Google+ for Mobile Apps on iOS and Android
 
Cross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-InCross-Platform Authentication with Google+ Sign-In
Cross-Platform Authentication with Google+ Sign-In
 
Bring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with RobolectricBring Back the Fun to Testing Android Apps with Robolectric
Bring Back the Fun to Testing Android Apps with Robolectric
 

Recently uploaded

Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 

Recently uploaded (20)

Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 

Firebase & SwiftUI Workshop

  • 1. Peter Friese | Developer Advocate, Firebase Marina Coelho | Developer Relations Engineer, Firebase  + Swi!UI & Firebase Workshop @coelho_dev @pete!riese
  • 2.
  • 3.
  • 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)
  • 7.
  • 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/
  • 24.
  • 25.
  • 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
  • 54.
  • 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 )