SlideShare a Scribd company logo
1 of 117
Download to read offline
Peter Friese | Firebase Developer Advocate | @pete
rf
riese
 +
Firebase for Apple Developers
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Architecture
For Data-Driven Apps
Photo by Lance Anderson on Unsplash
#one-book-a-week-challenge
Drill-down navigation


Three-column layout (iPad / Mac)


Single source of truth


Driving factors
UI always in sync
UI always in sync
BookShelfView
BookEditView
BookDetailsView
BookRowView
Source of Truth
books: [Book]
@ObservableObject
@Binding
Book
Book
Book
Challenge #1
This needs to be a binding
But this isn’t a list of bindings
struct BookShelfView: View {


@Binding var bookShelf: BookShelf




var body: some View {


List {


ForEach(Array(bookShelf.books.enumerated()), id: .element.id) { index, item in


BookRowView(book: $bookShelf.books[index])


}


.onDelete { indexSet in


bookShelf.books.remove(atOffsets: indexSet)


}


}


.navigationTitle(bookShelf.title)


}


}
Solution #1: iterate over enumerated items
🤔
struct BookShelfView: View {


@Binding var bookShelf: BookShelf




var body: some View {


List {


ForEach($bookShelf.books) { $book in


BookRowView(book: $book)


}


.onDelete { indexSet in


bookShelf.books.remove(atOffsets: indexSet)


}


}


.navigationTitle(bookShelf.title)


}


}
Solution #1: use list bindings
Everything is now bindable
Learn more
h
tt
ps://pete
rf
riese.dev/swi
ft
ui-list-item-bindings-behind-the-scenes/
How to update only
when the user commits?
Challenge #2
struct BookEditView: View {


@Binding var book: Book


@ObservedObject var bookEditViewModel: BookEditViewModel




init(book: Binding<Book>) {


self._book = book


self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue)


}




var body: some View {


TextField("Book title", text: $bookEditViewModel.book.title)


}


func save() {


self.book = bookEditViewModel.book


dismiss()


}


}


Solution 2: use inner @ObservableObject
struct BookEditView: View {


@Binding var book: Book


@ObservedObject var bookEditViewModel: BookEditViewModel




init(book: Binding<Book>) {


self._book = book


self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue)


}




var body: some View {


TextField("Book title", text: $bookEditViewModel.book.title)


}


func save() {


self.book = bookEditViewModel.book


dismiss()


}


}


Solution 2: use inner @ObservableObject
struct BookEditView: View {


@Binding var book: Book


@ObservedObject var bookEditViewModel: BookEditViewModel




init(book: Binding<Book>) {


self._book = book


self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue)


}




var body: some View {


TextField("Book title", text: $bookEditViewModel.book.title)


}


func save() {


self.book = bookEditViewModel.book


dismiss()


}


}


Solution 2: use inner @ObservableObject
struct BookEditView: View {


@Binding var book: Book


@ObservedObject var bookEditViewModel: BookEditViewModel




init(book: Binding<Book>) {


self._book = book


self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue)


}




var body: some View {


TextField("Book title", text: $bookEditViewModel.book.title)


}


func save() {


self.book = bookEditViewModel.book


dismiss()


}


}


Solution 2: use inner @ObservableObject
Solution 2
class BookEditViewModel: ObservableObject {


@Published var book: Book


@Published var isISBNValid: Bool = true




init(book: Book) {


self.book = book




self.$book


.map { checkISBN(isbn: $0.isbn) }


.assign(to: &$isISBNValid)


}


}


Solution 2: use inner @ObservableObject
Bonus: use Combine to
perform validation
Learn more about


Building SwiftUI Components
h
tt
ps://www.youtube.com/c/pete
rf
riese
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
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
#protip: Put it
beneath Asset.xcasset
Don’t forget to add
to all the targets!
Swift Package Manager
now officially supported!
h
tt
ps://github.com/
fi
rebase/
fi
rebase-ios-sdk
🤔
Application Lifecycle
SwiftUI 2
Photo by Thor Alvis on Unsplash
SwiftUI 2: No more AppDelegate!
import SwiftUI


@main


struct BookShelfApp: App {


@StateObject var store = BookShelfStore(shelves: BookShelf.samples)
var body: some Scene {


WindowGroup {


NavigationView {


BookShelvesView(store: store)


Text("Select a shelf to see its books")


Text("Select a book to see its details")


}


}


}


}
SwiftUI 2: No more AppDelegate!
import SwiftUI


@main


struct BookShelfApp: App {


@StateObject var store = BookShelfStore(shelves: BookShelf.samples)
var body: some Scene {


WindowGroup {


NavigationView {


BookShelvesView(store: store)


Text("Select a shelf to see its books")


Text("Select a book to see its details")


}


}


}


}
Solution 1: use initialiser
import SwiftUI


@main


struct BookShelfApp: App {


@StateObject var store = BookShelfStore(shelves: BookShelf.samples)
var body: some Scene {


WindowGroup {


NavigationView {


BookShelvesView(store: store)


Text("Select a shelf to see its books")


Text("Select a book to see its details")


}
Solution 1: use initialiser
import SwiftUI


@main


struct BookShelfApp: App {


@StateObject var store = BookShelfStore(shelves: BookShelf.samples)
var body: some Scene {


WindowGroup {


NavigationView {


BookShelvesView(store: store)


Text("Select a shelf to see its books")


Text("Select a book to see its details")


}




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 BookShelfApp: 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 BookShelfApp: 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 BookShelfApp: 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 BookShelfApp: 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
rf
riese.dev/ultimate-guide-to-swi
ft
ui2-application-lifecycle/
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
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
“books” collection a single book document
struct Book: Identifiable {


var id = UUID().uuidString


var shelfId: String?


var userId: String?


var title: String


var author: String


var isbn: String


var pages: Int


var isRead: Bool = false


var coverEditionKey: String?


}
Data Model
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}
Can we do better?
(Yes, we can)
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


if let document = document {


let id = document.documentID


let data = document.data()


let title = data?["title"] as? String
?
?
""


let numberOfPages = data?["numberOfPages"] as? Int
?
?
0


let author = data?["author"] as? String
?
?
""


self.book = Book(id:id, title: title,


numberOfPages: numberOfPages, author: author)


}


}


}


}
Fetching a document from Firestore
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


}


}


}
Mapping data using Codable
if let document = document {


do {


self.book = try document.data(as: Book.self)


}


catch {


print(error)


}


}
func fetchBook(documentId: String) {


let docRef = Firestore.firestore().collection("books").document(documentId)


docRef.getDocument { document, error in


if let error = error as NSError? {


print("Error getting document: (error.localizedDescription)")


} else {


}


}


}
Mapping data using Codable
if let document = document {


do {


self.book = try document.data(as: Book.self)


}


catch {


print(error)


}


}
BookShelfView
BookEditView
BookDetailsView
BookRowView
Source of Truth
books: [Book]
@ObservableObject
@Binding
Book
Book
Book
Review: Architecture
BookShelfView
BookDetailsView
BookRowView
Source of Truth
books: [Book]
@ObservableObject
@Binding
Book
Book
Review: Architecture
Snapshot Listener
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
class BookStore: ObservableObject {


var db = Firestore.firestore()


private var listenerRegistration: ListenerRegistration?


@Published var books = [Book]()




func subscribe() {


listenerRegistration = db.collection("books")


.addSnapshotListener { [weak self] (querySnapshot, error) in


guard let documents = querySnapshot
?
.
documents else { return }




self
?
.
books = documents.compactMap { queryDocumentSnapshot in


let result = Result { try queryDocumentSnapshot.data(as: Book.self) }




switch result {


case .success(let book):


if let book = book {


return book




Fetching a collection of documents
Learn more
h
tt
ps://pete
rf
riese.dev/
fi
restore-codable-the-comprehensive-guide/
import Foundation


import Combine


import Firebase


import FirebaseFirestoreSwift


import os


class BookStore: ObservableObject {


/
/
MARK: - Dependencies


var db = Firestore.firestore()




/
/
MARK: - Publishers


@Published var user: User?


@Published var books = [Book]()




/
/
MARK: - Private attributes


@Published private var userId: String = "unknown"


private var listenerRegistration: ListenerRegistration?


private var cancellables = Set<AnyCancellable>()




Fetching a collection of documents
/
/
A Book value could not be initialized from the DocumentSnapshot.


switch error {


case DecodingError.typeMismatch(_, let context):


self
?
.
logger.debug("(error.localizedDescription): 
(context.debugDescription)")


case DecodingError.valueNotFound(_, let context):


self
?
.
logger.debug("(error.localizedDescription): 
(context.debugDescription)")


case DecodingError.keyNotFound(_, let context):


self
?
.
logger.debug("(error.localizedDescription): 
(context.debugDescription)")


case DecodingError.dataCorrupted(let key):


self
?
.
logger.debug("(error.localizedDescription): 
(key.debugDescription)")


default:


self
?
.
logger.debug("Error decoding document: 
(error.localizedDescription)")


}


return nil


}


}


}


}




Fetching a collection of documents
/
/
A Book value could not be initialized from the DocumentSnapshot.


switch error {


case DecodingError.typeMismatch(_, let context):


self
?
.
logger.debug("(error.localizedDescription): 
(context.debugDescription)")


case DecodingError.valueNotFound(_, let context):


self
?
.
logger.debug("(error.localizedDescription): 
(context.debugDescription)")


case DecodingError.keyNotFound(_, let context):


self
?
.
logger.debug("(error.localizedDescription): 
(context.debugDescription)")


case DecodingError.dataCorrupted(let key):


self
?
.
logger.debug("(error.localizedDescription): 
(key.debugDescription)")


default:


self
?
.
logger.debug("Error decoding document: 
(error.localizedDescription)")


}


return nil


}


}


}


}




Fetching a collection of documents
about 100 lines of code
struct BookShelfView: View {


@FirestoreQuery(


collectionPath: "books",


predicates: [


.where("userId", isEqualTo: userId),


]


) var books: Result<[Book], Error>




@State var userId = "F18EBA5E"




var body: some View {


List(books) { book in


Text(book.title)


}


}


}
Firestore Property Wrapper Firebase 8.9.0
@FloWritesCode
@mo
rt
enditlevsen
Thanks to
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Authentication
Photo by Conve
rt
Kit on Unsplash
Authentication
Photo by Eduardo Soares on Unsplash
Authentication
Photo by Victor Freitas on Unsplash

Sign in the user


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)


Auth.auth().signIn(with: credential) { (authResult, error) in


if (error
!
=
nil) {
.
.
.
}


print(“User signed in")


dismiss()


}


}


).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)


Auth.auth().signIn(with: credential) { (authResult, error) in


if (error
!
=
nil) {
.
.
.
}


print(“User signed in")


dismiss()


}


}


).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)


Auth.auth().signIn(with: credential) { (authResult, error) in


if (error
!
=
nil) {
.
.
.
}


print(“User signed in")


dismiss()


}


}


).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)


Auth.auth().signIn(with: credential) { (authResult, error) in


if (error
!
=
nil) {
.
.
.
}


print(“User signed in")


dismiss()


}


}


).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)


Auth.auth().signIn(with: credential) { (authResult, error) in


if (error
!
=
nil) {
.
.
.
}


print(“User signed in")


dismiss()


}


}


).frame(width: 280, height: 45, alignment: .center)


Sign in with Apple
Firebase SDK
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)


Auth.auth().signIn(with: credential) { (authResult, error) in


if (error
!
=
nil) {
.
.
.
}


print(“User signed in")


dismiss()


}


}


).frame(width: 280, height: 45, alignment: .center)


Sign in with Apple
Firebase SDK
All books are stored in one single collection
Which user do
they belong to?
let query = db.collection("books")


.whereField("userId",


isEqualTo: self.userId)


query


.addSnapshotListener { [weak self] (querySnapsho


guard let documents = querySnapshot
?
.
documents els


Signed in user
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
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Combine
auth
?
.
signInAnonymously()


let user = auth
?
.
currentUser


print("User signed in with user ID: (user
?
.
uid)")
This might be nil
auth
?
.
signInAnonymously()


let user = auth
?
.
currentUser


print("User signed in with user ID: (user
?
.
uid)")
auth
?
.
signInAnonymously { result, error in


guard let result = result else {


return


}


print("User signed in with user ID: (result.user.uid)")


}
Do this instead
auth


.
.
.


auth
?
.
signInAnonymously()


.map{ $0.user }


.replaceError(with: nil)


.assign(to: &$user)
Even better
Launching with Firebase 8.9.0
h
tt
ps://github.com/
fi
rebase/
fi
rebase-ios-sdk/projects/3
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
async/await
Photo by Stephen H on Unsplash
extension ArticleAnalyser {


func process(url: String, completion: @escaping (Article)
-
>
Void) {


self.fetchArticle(from: url) { result in


switch result {


case .failure(let error):


print(error.localizedDescription)


case .success(let html):


self.extractTitle(from: html) { result in


switch result {


case .failure(let error):


print(error.localizedDescription)


case .success(let title):


self.extractText(from: html) { result in


switch result {


case .failure(let error):


print(error.localizedDescription)


case .success(let text):


self.extractImage(from: url) { result in




Problem: Callback Pyramid of Doom
extension AsyncArticleAnalyser {


func process(url: String) async throws
-
>
Article {


let htmlText = try await fetchArticle(from: url)


let text = try await extractText(from: htmlText)


let title = try await extractTitle(from: htmlText)


let imageUrl = try await extractImage(from: url)


let tags = await inferTags(from: text)




return Article(url: url,


title: title,


tags: tags,


imageUrlString: imageUrl)


}


}
Solution: Use async/await
Swift 5.5
func fetchArticle(from url: String) async throws
-
>
String {


guard let url = URL(string: url) else { throw AnalyserError.badURL }




return try await withUnsafeThrowingContinuation { continuation in


URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in


guard let localUrl = localUrl else {


continuation.resume(throwing: AnalyserError.badURL)


return


}


if let htmlText = try? String(contentsOf: localUrl) {


continuation.resume(returning: htmlText)


}


}


.resume()


}


}
Solution: Use async/await
Swift 5.5
func fetchArticle(from url: String) async throws
-
>
String {


guard let url = URL(string: url) else { throw AnalyserError.badURL }




return try await withUnsafeThrowingContinuation { continuation in


URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in


guard let localUrl = localUrl else {


continuation.resume(throwing: AnalyserError.badURL)


return


}


if let htmlText = try? String(contentsOf: localUrl) {


continuation.resume(returning: htmlText)


}


}


.resume()


}


}
Solution: Use async/await
Swift 5.5
func fetchArticle(from url: String) async throws
-
>
String {


guard let url = URL(string: url) else { throw AnalyserError.badURL }




return try await withUnsafeThrowingContinuation { continuation in


URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in


guard let localUrl = localUrl else {


continuation.resume(throwing: AnalyserError.badURL)


return


}


if let htmlText = try? String(contentsOf: localUrl) {


continuation.resume(returning: htmlText)


}


}


.resume()


}


}
Solution: Use async/await
Swift 5.5
func fetchArticle(from url: String) async throws
-
>
String {


guard let url = URL(string: url) else { throw AnalyserError.badURL }




return try await withUnsafeThrowingContinuation { continuation in


URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in


guard let localUrl = localUrl else {


continuation.resume(throwing: AnalyserError.badURL)


return


}


if let htmlText = try? String(contentsOf: localUrl) {


continuation.resume(returning: htmlText)


}


}


.resume()


}


}
Solution: Use async/await
Swift 5.5
auth
?
.
signInAnonymously { result, error in


guard let result = result else {


return


}


print("User signed in with user ID: (result.user.uid)")


}
do {


let result = try await Auth.auth().signIn(withEmail: email, password: password)


print("User signed in with user ID: (result.user.uid)")


}


catch {


print(error)


}


Works with Firebase, too!
Callback-style
Learn more
h
tt
ps://pete
rf
riese.dev/async-await-in-swi
f
h
tt
ps://www.youtube.com/watch?v=sEKw2BMcQtQ
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Architecture Swi
ft
UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Register now - it’s free!
h
tt
ps://
fi
rebase.google.com/summit
Thanks!
Peter Friese


h
tt
p://pete
rf
riese.dev


@pete
rf
riese


youtube.com/c/PeterFriese/
Follow me
h
tt
ps://github.com/pete
rf
riese/BookShelf
Code
Credits
fi
nish by Megan Chown from the Noun Project
Time by Nikita Kozin from the Noun Project
pipe by Komkrit Noenpoempisut from the Noun Project
Passpo
rt
by ProSymbols from the Noun Project
spiral by Alexander Skowalsky from the Noun Project
Architecture by Ervin Bolat from the Noun Project
Firebase logos cou
rt
esy h
tt
ps://
fi
rebase.google.com/brand-guidelines
Firebase logos cou
rt
esy h
tt
ps://
fi
rebase.google.com/brand-guidelines
Thanks!
Hea
rt
by Roman from the Noun Project
The End.

More Related Content

What's hot

A To-do Web App on Google App Engine
A To-do Web App on Google App EngineA To-do Web App on Google App Engine
A To-do Web App on Google App EngineMichael Parker
 
Developing iOS REST Applications
Developing iOS REST ApplicationsDeveloping iOS REST Applications
Developing iOS REST Applicationslmrei
 
Getting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUIGetting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUIScott Gardner
 
Parse London Meetup - Cloud Code Tips & Tricks
Parse London Meetup - Cloud Code Tips & TricksParse London Meetup - Cloud Code Tips & Tricks
Parse London Meetup - Cloud Code Tips & TricksHector Ramos
 
Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1David Rodenas
 
Server Side Swift with Swag
Server Side Swift with SwagServer Side Swift with Swag
Server Side Swift with SwagJens Ravens
 
Search APIs in Spotlight and Safari
Search APIs in Spotlight and SafariSearch APIs in Spotlight and Safari
Search APIs in Spotlight and SafariYusuke Kita
 
Making connected apps with BaaS (Droidcon Bangalore 2014)
Making connected apps with BaaS (Droidcon Bangalore 2014)Making connected apps with BaaS (Droidcon Bangalore 2014)
Making connected apps with BaaS (Droidcon Bangalore 2014)Varun Torka
 
jQuery Mobile Workshop
jQuery Mobile WorkshopjQuery Mobile Workshop
jQuery Mobile WorkshopRon Reiter
 
Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks Thinqloud
 
Angular 4 with firebase
Angular 4 with firebaseAngular 4 with firebase
Angular 4 with firebaseAnne Bougie
 
MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012
MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012
MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012Amazon Web Services
 
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
 
Android L02 - Activities and Adapters
Android L02 - Activities and AdaptersAndroid L02 - Activities and Adapters
Android L02 - Activities and AdaptersMohammad Shaker
 
Distributing information on iOS
Distributing information on iOSDistributing information on iOS
Distributing information on iOSMake School
 
A single language for backend and frontend from AngularJS to cloud with Clau...
A single language for backend and frontend  from AngularJS to cloud with Clau...A single language for backend and frontend  from AngularJS to cloud with Clau...
A single language for backend and frontend from AngularJS to cloud with Clau...Walter Dal Mut
 
Lightning Components Workshop
Lightning Components WorkshopLightning Components Workshop
Lightning Components WorkshopGordon Bockus
 
Medium TechTalk — iOS
Medium TechTalk — iOSMedium TechTalk — iOS
Medium TechTalk — iOSjimmyatmedium
 

What's hot (20)

A To-do Web App on Google App Engine
A To-do Web App on Google App EngineA To-do Web App on Google App Engine
A To-do Web App on Google App Engine
 
Developing iOS REST Applications
Developing iOS REST ApplicationsDeveloping iOS REST Applications
Developing iOS REST Applications
 
Getting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUIGetting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUI
 
Parse London Meetup - Cloud Code Tips & Tricks
Parse London Meetup - Cloud Code Tips & TricksParse London Meetup - Cloud Code Tips & Tricks
Parse London Meetup - Cloud Code Tips & Tricks
 
Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1Modules in angular 2.0 beta.1
Modules in angular 2.0 beta.1
 
Server Side Swift with Swag
Server Side Swift with SwagServer Side Swift with Swag
Server Side Swift with Swag
 
Search APIs in Spotlight and Safari
Search APIs in Spotlight and SafariSearch APIs in Spotlight and Safari
Search APIs in Spotlight and Safari
 
Making connected apps with BaaS (Droidcon Bangalore 2014)
Making connected apps with BaaS (Droidcon Bangalore 2014)Making connected apps with BaaS (Droidcon Bangalore 2014)
Making connected apps with BaaS (Droidcon Bangalore 2014)
 
jQuery Mobile Workshop
jQuery Mobile WorkshopjQuery Mobile Workshop
jQuery Mobile Workshop
 
Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks
 
Angular 4 with firebase
Angular 4 with firebaseAngular 4 with firebase
Angular 4 with firebase
 
Tips for Angular Applications
Tips for Angular ApplicationsTips for Angular Applications
Tips for Angular Applications
 
MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012
MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012
MBL302 Using the AWS Mobile SDKs - AWS re: Invent 2012
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
J query fundamentals
J query fundamentalsJ query fundamentals
J query fundamentals
 
Android L02 - Activities and Adapters
Android L02 - Activities and AdaptersAndroid L02 - Activities and Adapters
Android L02 - Activities and Adapters
 
Distributing information on iOS
Distributing information on iOSDistributing information on iOS
Distributing information on iOS
 
A single language for backend and frontend from AngularJS to cloud with Clau...
A single language for backend and frontend  from AngularJS to cloud with Clau...A single language for backend and frontend  from AngularJS to cloud with Clau...
A single language for backend and frontend from AngularJS to cloud with Clau...
 
Lightning Components Workshop
Lightning Components WorkshopLightning Components Workshop
Lightning Components Workshop
 
Medium TechTalk — iOS
Medium TechTalk — iOSMedium TechTalk — iOS
Medium TechTalk — iOS
 

Similar to  +  = ❤️ (Firebase for Apple Developers) at Swift Leeds

Firebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopFirebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopPeter Friese
 
Introduction to java beans
Introduction to java beansIntroduction to java beans
Introduction to java beansHitesh Parmar
 
A tour through Swift attributes
A tour through Swift attributesA tour through Swift attributes
A tour through Swift attributesMarco Eidinger
 
Search APIs & Universal Links
Search APIs & Universal LinksSearch APIs & Universal Links
Search APIs & Universal LinksYusuke Kita
 
MVC and Entity Framework
MVC and Entity FrameworkMVC and Entity Framework
MVC and Entity FrameworkJames Johnson
 
Alphorm.com Formation React Testing Library
Alphorm.com Formation React Testing LibraryAlphorm.com Formation React Testing Library
Alphorm.com Formation React Testing LibraryAlphorm
 
From Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practiceFrom Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practiceStefanTomm
 
Android application architecture
Android application architectureAndroid application architecture
Android application architectureRomain Rochegude
 
Building framework with shared code on Android and iOS using React Native. UA...
Building framework with shared code on Android and iOS using React Native. UA...Building framework with shared code on Android and iOS using React Native. UA...
Building framework with shared code on Android and iOS using React Native. UA...UA Mobile
 
SharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure FunctionsSharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure FunctionsSébastien Levert
 
Appcelerator Titanium Alloy + Kinvey Collection Databinding - Part One
Appcelerator Titanium Alloy + Kinvey Collection Databinding - Part OneAppcelerator Titanium Alloy + Kinvey Collection Databinding - Part One
Appcelerator Titanium Alloy + Kinvey Collection Databinding - Part OneAaron Saunders
 
JSAnkara Swift v React Native
JSAnkara Swift v React NativeJSAnkara Swift v React Native
JSAnkara Swift v React NativeMuhammed Demirci
 
Inverting Dependencies
Inverting DependenciesInverting Dependencies
Inverting DependenciesLuc Trudeau
 
Writing HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAEWriting HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAERon Reiter
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackNelson Glauber Leal
 
Itb 2021 - Bulding Quick APIs by Gavin Pickin
Itb 2021 - Bulding Quick APIs by Gavin PickinItb 2021 - Bulding Quick APIs by Gavin Pickin
Itb 2021 - Bulding Quick APIs by Gavin PickinGavin Pickin
 
properties-how-do-i - Transcript.pdf
properties-how-do-i - Transcript.pdfproperties-how-do-i - Transcript.pdf
properties-how-do-i - Transcript.pdfShaiAlmog1
 
Dependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDiego Lewin
 

Similar to  +  = ❤️ (Firebase for Apple Developers) at Swift Leeds (20)

Firebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopFirebase & SwiftUI Workshop
Firebase & SwiftUI Workshop
 
Introduction to java beans
Introduction to java beansIntroduction to java beans
Introduction to java beans
 
A tour through Swift attributes
A tour through Swift attributesA tour through Swift attributes
A tour through Swift attributes
 
Search APIs & Universal Links
Search APIs & Universal LinksSearch APIs & Universal Links
Search APIs & Universal Links
 
MVC and Entity Framework
MVC and Entity FrameworkMVC and Entity Framework
MVC and Entity Framework
 
Alphorm.com Formation React Testing Library
Alphorm.com Formation React Testing LibraryAlphorm.com Formation React Testing Library
Alphorm.com Formation React Testing Library
 
From Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practiceFrom Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practice
 
Android application architecture
Android application architectureAndroid application architecture
Android application architecture
 
Building framework with shared code on Android and iOS using React Native. UA...
Building framework with shared code on Android and iOS using React Native. UA...Building framework with shared code on Android and iOS using React Native. UA...
Building framework with shared code on Android and iOS using React Native. UA...
 
SharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure FunctionsSharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure Functions
 
Appcelerator Titanium Alloy + Kinvey Collection Databinding - Part One
Appcelerator Titanium Alloy + Kinvey Collection Databinding - Part OneAppcelerator Titanium Alloy + Kinvey Collection Databinding - Part One
Appcelerator Titanium Alloy + Kinvey Collection Databinding - Part One
 
JSAnkara Swift v React Native
JSAnkara Swift v React NativeJSAnkara Swift v React Native
JSAnkara Swift v React Native
 
Inverting Dependencies
Inverting DependenciesInverting Dependencies
Inverting Dependencies
 
Writing HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAEWriting HTML5 Web Apps using Backbone.js and GAE
Writing HTML5 Web Apps using Backbone.js and GAE
 
introduction of Java beans
introduction of Java beansintroduction of Java beans
introduction of Java beans
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
Unit iv
Unit ivUnit iv
Unit iv
 
Itb 2021 - Bulding Quick APIs by Gavin Pickin
Itb 2021 - Bulding Quick APIs by Gavin PickinItb 2021 - Bulding Quick APIs by Gavin Pickin
Itb 2021 - Bulding Quick APIs by Gavin Pickin
 
properties-how-do-i - Transcript.pdf
properties-how-do-i - Transcript.pdfproperties-how-do-i - Transcript.pdf
properties-how-do-i - Transcript.pdf
 
Dependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony ContainerDependency Injection, Zend Framework and Symfony Container
Dependency Injection, Zend Framework and Symfony Container
 

More from Peter Friese

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsPeter Friese
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in SwiftPeter 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
 
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
 
Do Androids Dream of Electric Sheep
Do Androids Dream of Electric SheepDo Androids Dream of Electric Sheep
Do Androids Dream of Electric SheepPeter Friese
 
Java based Cross-Platform Mobile Development
Java based Cross-Platform Mobile DevelopmentJava based Cross-Platform Mobile Development
Java based Cross-Platform Mobile DevelopmentPeter Friese
 

More from Peter Friese (20)

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
 
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
 
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
 
Do Androids Dream of Electric Sheep
Do Androids Dream of Electric SheepDo Androids Dream of Electric Sheep
Do Androids Dream of Electric Sheep
 
Java based Cross-Platform Mobile Development
Java based Cross-Platform Mobile DevelopmentJava based Cross-Platform Mobile Development
Java based Cross-Platform Mobile Development
 

Recently uploaded

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
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsChristian Birchler
 
How To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROHow To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROmotivationalword821
 
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
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
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
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 

Recently uploaded (20)

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
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving CarsSensoDat: Simulation-based Sensor Dataset of Self-driving Cars
SensoDat: Simulation-based Sensor Dataset of Self-driving Cars
 
How To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTROHow To Manage Restaurant Staff -BTRESTRO
How To Manage Restaurant Staff -BTRESTRO
 
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
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
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
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
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
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 

 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds

  • 1. Peter Friese | Firebase Developer Advocate | @pete rf riese  + Firebase for Apple Developers
  • 2. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 3. Architecture For Data-Driven Apps Photo by Lance Anderson on Unsplash
  • 5.
  • 6. Drill-down navigation Three-column layout (iPad / Mac) Single source of truth Driving factors UI always in sync
  • 8. BookShelfView BookEditView BookDetailsView BookRowView Source of Truth books: [Book] @ObservableObject @Binding Book Book Book
  • 9. Challenge #1 This needs to be a binding But this isn’t a list of bindings
  • 10. struct BookShelfView: View { @Binding var bookShelf: BookShelf var body: some View { List { ForEach(Array(bookShelf.books.enumerated()), id: .element.id) { index, item in BookRowView(book: $bookShelf.books[index]) } .onDelete { indexSet in bookShelf.books.remove(atOffsets: indexSet) } } .navigationTitle(bookShelf.title) } } Solution #1: iterate over enumerated items 🤔
  • 11. struct BookShelfView: View { @Binding var bookShelf: BookShelf var body: some View { List { ForEach($bookShelf.books) { $book in BookRowView(book: $book) } .onDelete { indexSet in bookShelf.books.remove(atOffsets: indexSet) } } .navigationTitle(bookShelf.title) } } Solution #1: use list bindings Everything is now bindable
  • 13. How to update only when the user commits? Challenge #2
  • 14. struct BookEditView: View { @Binding var book: Book @ObservedObject var bookEditViewModel: BookEditViewModel init(book: Binding<Book>) { self._book = book self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue) } var body: some View { TextField("Book title", text: $bookEditViewModel.book.title) } func save() { self.book = bookEditViewModel.book dismiss() } } Solution 2: use inner @ObservableObject
  • 15. struct BookEditView: View { @Binding var book: Book @ObservedObject var bookEditViewModel: BookEditViewModel init(book: Binding<Book>) { self._book = book self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue) } var body: some View { TextField("Book title", text: $bookEditViewModel.book.title) } func save() { self.book = bookEditViewModel.book dismiss() } } Solution 2: use inner @ObservableObject
  • 16. struct BookEditView: View { @Binding var book: Book @ObservedObject var bookEditViewModel: BookEditViewModel init(book: Binding<Book>) { self._book = book self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue) } var body: some View { TextField("Book title", text: $bookEditViewModel.book.title) } func save() { self.book = bookEditViewModel.book dismiss() } } Solution 2: use inner @ObservableObject
  • 17. struct BookEditView: View { @Binding var book: Book @ObservedObject var bookEditViewModel: BookEditViewModel init(book: Binding<Book>) { self._book = book self.bookEditViewModel = BookEditViewModel(book: book.wrappedValue) } var body: some View { TextField("Book title", text: $bookEditViewModel.book.title) } func save() { self.book = bookEditViewModel.book dismiss() } } Solution 2: use inner @ObservableObject
  • 19. class BookEditViewModel: ObservableObject { @Published var book: Book @Published var isISBNValid: Bool = true init(book: Book) { self.book = book self.$book .map { checkISBN(isbn: $0.isbn) } .assign(to: &$isISBNValid) } } Solution 2: use inner @ObservableObject Bonus: use Combine to perform validation
  • 20. Learn more about Building SwiftUI Components h tt ps://www.youtube.com/c/pete rf riese
  • 21. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 22.
  • 23. Run with confidence Engage users Develop apps faster
  • 24. 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
  • 25.
  • 26.
  • 27.
  • 28. #protip: Put it beneath Asset.xcasset
  • 29. Don’t forget to add to all the targets!
  • 30. Swift Package Manager now officially supported! h tt ps://github.com/ fi rebase/ fi rebase-ios-sdk
  • 31. 🤔
  • 32. Application Lifecycle SwiftUI 2 Photo by Thor Alvis on Unsplash
  • 33. SwiftUI 2: No more AppDelegate! import SwiftUI @main struct BookShelfApp: App { @StateObject var store = BookShelfStore(shelves: BookShelf.samples) var body: some Scene { WindowGroup { NavigationView { BookShelvesView(store: store) Text("Select a shelf to see its books") Text("Select a book to see its details") } } } }
  • 34. SwiftUI 2: No more AppDelegate! import SwiftUI @main struct BookShelfApp: App { @StateObject var store = BookShelfStore(shelves: BookShelf.samples) var body: some Scene { WindowGroup { NavigationView { BookShelvesView(store: store) Text("Select a shelf to see its books") Text("Select a book to see its details") } } } }
  • 35. Solution 1: use initialiser import SwiftUI @main struct BookShelfApp: App { @StateObject var store = BookShelfStore(shelves: BookShelf.samples) var body: some Scene { WindowGroup { NavigationView { BookShelvesView(store: store) Text("Select a shelf to see its books") Text("Select a book to see its details") }
  • 36. Solution 1: use initialiser import SwiftUI @main struct BookShelfApp: App { @StateObject var store = BookShelfStore(shelves: BookShelf.samples) var body: some Scene { WindowGroup { NavigationView { BookShelvesView(store: store) Text("Select a shelf to see its books") Text("Select a book to see its details") } init() { FirebaseApp.configure() }
  • 37. 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 BookShelfApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 38. 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 BookShelfApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 39. 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 BookShelfApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 40. 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 BookShelfApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  • 41. Watch the scene phase Handle deep links Continue user activities Do more with the new life cycle
  • 42. Watch the scene phase Handle deep links Continue user activities Learn more pete rf riese.dev/ultimate-guide-to-swi ft ui2-application-lifecycle/
  • 43. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 45.
  • 46.
  • 51. struct Book: Identifiable { var id = UUID().uuidString var shelfId: String? var userId: String? var title: String var author: String var isbn: String var pages: Int var isRead: Bool = false var coverEditionKey: String? } Data Model
  • 52. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 53. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 54. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 55. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 56. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 57. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 58. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } Can we do better?
  • 60. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { if let document = document { let id = document.documentID let data = document.data() let title = data?["title"] as? String ? ? "" let numberOfPages = data?["numberOfPages"] as? Int ? ? 0 let author = data?["author"] as? String ? ? "" self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author) } } } } Fetching a document from Firestore
  • 61. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { } } } Mapping data using Codable if let document = document { do { self.book = try document.data(as: Book.self) } catch { print(error) } }
  • 62. func fetchBook(documentId: String) { let docRef = Firestore.firestore().collection("books").document(documentId) docRef.getDocument { document, error in if let error = error as NSError? { print("Error getting document: (error.localizedDescription)") } else { } } } Mapping data using Codable if let document = document { do { self.book = try document.data(as: Book.self) } catch { print(error) } }
  • 63. BookShelfView BookEditView BookDetailsView BookRowView Source of Truth books: [Book] @ObservableObject @Binding Book Book Book Review: Architecture
  • 64. BookShelfView BookDetailsView BookRowView Source of Truth books: [Book] @ObservableObject @Binding Book Book Review: Architecture Snapshot Listener
  • 65. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 66. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 67. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 68. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 69. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 70. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 71. class BookStore: ObservableObject { var db = Firestore.firestore() private var listenerRegistration: ListenerRegistration? @Published var books = [Book]() func subscribe() { listenerRegistration = db.collection("books") .addSnapshotListener { [weak self] (querySnapshot, error) in guard let documents = querySnapshot ? . documents else { return } self ? . books = documents.compactMap { queryDocumentSnapshot in let result = Result { try queryDocumentSnapshot.data(as: Book.self) } switch result { case .success(let book): if let book = book { return book Fetching a collection of documents
  • 72.
  • 74. import Foundation import Combine import Firebase import FirebaseFirestoreSwift import os class BookStore: ObservableObject { / / MARK: - Dependencies var db = Firestore.firestore() / / MARK: - Publishers @Published var user: User? @Published var books = [Book]() / / MARK: - Private attributes @Published private var userId: String = "unknown" private var listenerRegistration: ListenerRegistration? private var cancellables = Set<AnyCancellable>() Fetching a collection of documents
  • 75. / / A Book value could not be initialized from the DocumentSnapshot. switch error { case DecodingError.typeMismatch(_, let context): self ? . logger.debug("(error.localizedDescription): (context.debugDescription)") case DecodingError.valueNotFound(_, let context): self ? . logger.debug("(error.localizedDescription): (context.debugDescription)") case DecodingError.keyNotFound(_, let context): self ? . logger.debug("(error.localizedDescription): (context.debugDescription)") case DecodingError.dataCorrupted(let key): self ? . logger.debug("(error.localizedDescription): (key.debugDescription)") default: self ? . logger.debug("Error decoding document: (error.localizedDescription)") } return nil } } } } Fetching a collection of documents
  • 76. / / A Book value could not be initialized from the DocumentSnapshot. switch error { case DecodingError.typeMismatch(_, let context): self ? . logger.debug("(error.localizedDescription): (context.debugDescription)") case DecodingError.valueNotFound(_, let context): self ? . logger.debug("(error.localizedDescription): (context.debugDescription)") case DecodingError.keyNotFound(_, let context): self ? . logger.debug("(error.localizedDescription): (context.debugDescription)") case DecodingError.dataCorrupted(let key): self ? . logger.debug("(error.localizedDescription): (key.debugDescription)") default: self ? . logger.debug("Error decoding document: (error.localizedDescription)") } return nil } } } } Fetching a collection of documents about 100 lines of code
  • 77. struct BookShelfView: View { @FirestoreQuery( collectionPath: "books", predicates: [ .where("userId", isEqualTo: userId), ] ) var books: Result<[Book], Error> @State var userId = "F18EBA5E" var body: some View { List(books) { book in Text(book.title) } } } Firestore Property Wrapper Firebase 8.9.0 @FloWritesCode @mo rt enditlevsen Thanks to
  • 78. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 80. Authentication Photo by Eduardo Soares on Unsplash
  • 81. Authentication Photo by Victor Freitas on Unsplash
  • 82.
  • 83. Sign in the user Update the data model Secure users’ data How to implement Firebase Authentication?
  • 85. func signIn() { registerStateListener() if Auth.auth().currentUser = = nil { Auth.auth().signInAnonymously() } } Anonymous Authentication
  • 86.
  • 87. 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) Auth.auth().signIn(with: credential) { (authResult, error) in if (error ! = nil) { . . . } print(“User signed in") dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 88. 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) Auth.auth().signIn(with: credential) { (authResult, error) in if (error ! = nil) { . . . } print(“User signed in") dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 89. 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) Auth.auth().signIn(with: credential) { (authResult, error) in if (error ! = nil) { . . . } print(“User signed in") dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 90. 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) Auth.auth().signIn(with: credential) { (authResult, error) in if (error ! = nil) { . . . } print(“User signed in") dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 91. 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) Auth.auth().signIn(with: credential) { (authResult, error) in if (error ! = nil) { . . . } print(“User signed in") dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple Firebase SDK
  • 92. 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) Auth.auth().signIn(with: credential) { (authResult, error) in if (error ! = nil) { . . . } print(“User signed in") dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple Firebase SDK
  • 93. All books are stored in one single collection Which user do they belong to?
  • 94. let query = db.collection("books") .whereField("userId", isEqualTo: self.userId) query .addSnapshotListener { [weak self] (querySnapsho guard let documents = querySnapshot ? . documents els Signed in user
  • 95. 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
  • 96. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 98. auth ? . signInAnonymously() let user = auth ? . currentUser print("User signed in with user ID: (user ? . uid)") This might be nil
  • 99. auth ? . signInAnonymously() let user = auth ? . currentUser print("User signed in with user ID: (user ? . uid)") auth ? . signInAnonymously { result, error in guard let result = result else { return } print("User signed in with user ID: (result.user.uid)") } Do this instead
  • 101. Launching with Firebase 8.9.0 h tt ps://github.com/ fi rebase/ fi rebase-ios-sdk/projects/3
  • 102. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 104. extension ArticleAnalyser { func process(url: String, completion: @escaping (Article) - > Void) { self.fetchArticle(from: url) { result in switch result { case .failure(let error): print(error.localizedDescription) case .success(let html): self.extractTitle(from: html) { result in switch result { case .failure(let error): print(error.localizedDescription) case .success(let title): self.extractText(from: html) { result in switch result { case .failure(let error): print(error.localizedDescription) case .success(let text): self.extractImage(from: url) { result in Problem: Callback Pyramid of Doom
  • 105. extension AsyncArticleAnalyser { func process(url: String) async throws - > Article { let htmlText = try await fetchArticle(from: url) let text = try await extractText(from: htmlText) let title = try await extractTitle(from: htmlText) let imageUrl = try await extractImage(from: url) let tags = await inferTags(from: text) return Article(url: url, title: title, tags: tags, imageUrlString: imageUrl) } } Solution: Use async/await Swift 5.5
  • 106. func fetchArticle(from url: String) async throws - > String { guard let url = URL(string: url) else { throw AnalyserError.badURL } return try await withUnsafeThrowingContinuation { continuation in URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in guard let localUrl = localUrl else { continuation.resume(throwing: AnalyserError.badURL) return } if let htmlText = try? String(contentsOf: localUrl) { continuation.resume(returning: htmlText) } } .resume() } } Solution: Use async/await Swift 5.5
  • 107. func fetchArticle(from url: String) async throws - > String { guard let url = URL(string: url) else { throw AnalyserError.badURL } return try await withUnsafeThrowingContinuation { continuation in URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in guard let localUrl = localUrl else { continuation.resume(throwing: AnalyserError.badURL) return } if let htmlText = try? String(contentsOf: localUrl) { continuation.resume(returning: htmlText) } } .resume() } } Solution: Use async/await Swift 5.5
  • 108. func fetchArticle(from url: String) async throws - > String { guard let url = URL(string: url) else { throw AnalyserError.badURL } return try await withUnsafeThrowingContinuation { continuation in URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in guard let localUrl = localUrl else { continuation.resume(throwing: AnalyserError.badURL) return } if let htmlText = try? String(contentsOf: localUrl) { continuation.resume(returning: htmlText) } } .resume() } } Solution: Use async/await Swift 5.5
  • 109. func fetchArticle(from url: String) async throws - > String { guard let url = URL(string: url) else { throw AnalyserError.badURL } return try await withUnsafeThrowingContinuation { continuation in URLSession.shared.downloadTask(with: url) { (localUrl, urlResponse, error) in guard let localUrl = localUrl else { continuation.resume(throwing: AnalyserError.badURL) return } if let htmlText = try? String(contentsOf: localUrl) { continuation.resume(returning: htmlText) } } .resume() } } Solution: Use async/await Swift 5.5
  • 110. auth ? . signInAnonymously { result, error in guard let result = result else { return } print("User signed in with user ID: (result.user.uid)") } do { let result = try await Auth.auth().signIn(withEmail: email, password: password) print("User signed in with user ID: (result.user.uid)") } catch { print(error) } Works with Firebase, too! Callback-style
  • 112. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 113. Architecture Swi ft UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 114. Register now - it’s free! h tt ps:// fi rebase.google.com/summit
  • 116. Credits fi nish by Megan Chown from the Noun Project Time by Nikita Kozin from the Noun Project pipe by Komkrit Noenpoempisut from the Noun Project Passpo rt by ProSymbols from the Noun Project spiral by Alexander Skowalsky from the Noun Project Architecture by Ervin Bolat from the Noun Project Firebase logos cou rt esy h tt ps:// fi rebase.google.com/brand-guidelines Firebase logos cou rt esy h tt ps:// fi rebase.google.com/brand-guidelines Thanks! Hea rt by Roman from the Noun Project