SlideShare a Scribd company logo
Peter Friese | Firebase Developer Advocate | @pete!riese
 +
Firebase for Apple Developers
Architecture Swi!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
Sequences are not bindable!
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(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
Challenge 2
How to update only
when the user commits?
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
presentationMode.wrappedValue.dismiss()
}
}
Solution 2: use inner @ObserverableObject
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
presentationMode.wrappedValue.dismiss()
}
}
Solution 2: use inner @ObserverableObject
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
presentationMode.wrappedValue.dismiss()
}
}
Solution 2: use inner @ObserverableObject
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
presentationMode.wrappedValue.dismiss()
}
}
Solution 2: use inner @ObserverableObject
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 @ObserverableObject
Bonus: use Combine to
perform validation
Architecture Swi!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"ps://github.com/#rebase/#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$riese.dev/ultimate-guide-to-swi!ui2-application-lifecycle/
Architecture Swi!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
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 {
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)
}
}
}
}
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 {
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)
}
}
}
}
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"ps://pete$riese.dev/#restore-codable-the-comprehensive-guide/
Architecture Swi!UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Authentication
Photo by Conve"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) { !!& }
self.presentationMode.wrappedValue.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) { !!& }
self.presentationMode.wrappedValue.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) { !!& }
self.presentationMode.wrappedValue.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) { !!& }
self.presentationMode.wrappedValue.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) { !!& }
self.presentationMode.wrappedValue.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) { !!& }
self.presentationMode.wrappedValue.dismiss()
}
}
).frame(width: 280, height: 45, alignment: .center)
Sign in with Apple
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!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!$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)")
}
@Published var user: User?
!!&
auth!$signInAnonymously()
.map{ $0.user }
.replaceError(with: nil)
.assign(to: &$user)
Even better
Follow the project for updates
h"ps://github.com/#rebase/#rebase-ios-sdk/projects/3
Architecture Swi!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
Probably in 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
Probably in 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
Probably in 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
Probably in 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
Probably in 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"ps://pete$riese.dev/async-await-in-swi!
h"ps://www.youtube.com/watch?v=sEKw2BMcQtQ
Architecture Swi!UI 2 Life Cycle
Firebase Firestore Authentication Combine async/await Completion block
Thanks!
Peter Friese
h"p://pete$riese.dev
@pete$riese
youtube.com/c/PeterFriese/
Follow me
Credits
#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% 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%esy h"ps://#rebase.google.com/brand-guidelines
Firebase logos cou%esy h"ps://#rebase.google.com/brand-guidelines
Thanks!
Hea% by Roman from the Noun Project
The End.

More Related Content

What's hot

Developing iOS REST Applications
Developing iOS REST ApplicationsDeveloping iOS REST Applications
Developing iOS REST Applications
lmrei
 
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
David Rodenas
 
Getting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUIGetting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUI
Scott 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 & Tricks
Hector Ramos
 
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
 
J query fundamentals
J query fundamentalsJ query fundamentals
J query fundamentals
Attaporn Ninsuwan
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
Juan C Catalan
 
Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks
Thinqloud
 
Android L02 - Activities and Adapters
Android L02 - Activities and AdaptersAndroid L02 - Activities and Adapters
Android L02 - Activities and Adapters
Mohammad Shaker
 
The Many Ways to Build Modular JavaScript
The Many Ways to Build Modular JavaScriptThe Many Ways to Build Modular JavaScript
The Many Ways to Build Modular JavaScriptTim Perry
 
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
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
Robert DeLuca
 
Ios - Introduction to memory management
Ios - Introduction to memory managementIos - Introduction to memory management
Ios - Introduction to memory management
Vibrant Technologies & Computers
 
Angular 4 with firebase
Angular 4 with firebaseAngular 4 with firebase
Angular 4 with firebase
Anne Bougie
 
iOS Memory Management Basics
iOS Memory Management BasicsiOS Memory Management Basics
iOS Memory Management Basics
Bilue
 
Distributing information on iOS
Distributing information on iOSDistributing information on iOS
Distributing information on iOS
Make School
 
Memory Management on iOS
Memory Management on iOSMemory Management on iOS
Memory Management on iOS
Make School
 
Introduction to MongoDB
Introduction to MongoDBIntroduction to MongoDB
Introduction to MongoDB
Chun-Kai Wang
 
Lightning Components Workshop
Lightning Components WorkshopLightning Components Workshop
Lightning Components Workshop
Gordon Bockus
 

What's hot (20)

Developing iOS REST Applications
Developing iOS REST ApplicationsDeveloping iOS REST Applications
Developing iOS REST Applications
 
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
 
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
 
Tips for Angular Applications
Tips for Angular ApplicationsTips for Angular Applications
Tips for Angular Applications
 
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...
 
J query fundamentals
J query fundamentalsJ query fundamentals
J query fundamentals
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks Salesforce Lightning Tips & Tricks
Salesforce Lightning Tips & Tricks
 
Android L02 - Activities and Adapters
Android L02 - Activities and AdaptersAndroid L02 - Activities and Adapters
Android L02 - Activities and Adapters
 
The Many Ways to Build Modular JavaScript
The Many Ways to Build Modular JavaScriptThe Many Ways to Build Modular JavaScript
The Many Ways to Build Modular JavaScript
 
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)
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
Ios - Introduction to memory management
Ios - Introduction to memory managementIos - Introduction to memory management
Ios - Introduction to memory management
 
Angular 4 with firebase
Angular 4 with firebaseAngular 4 with firebase
Angular 4 with firebase
 
iOS Memory Management Basics
iOS Memory Management BasicsiOS Memory Management Basics
iOS Memory Management Basics
 
Distributing information on iOS
Distributing information on iOSDistributing information on iOS
Distributing information on iOS
 
Memory Management on iOS
Memory Management on iOSMemory Management on iOS
Memory Management on iOS
 
Introduction to MongoDB
Introduction to MongoDBIntroduction to MongoDB
Introduction to MongoDB
 
Lightning Components Workshop
Lightning Components WorkshopLightning Components Workshop
Lightning Components Workshop
 

Similar to Firebase for Apple Developers

Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
Nelson Glauber Leal
 
4시간만에 따라해보는 Windows 10 앱 개발 샘플코드
4시간만에 따라해보는 Windows 10 앱 개발 샘플코드4시간만에 따라해보는 Windows 10 앱 개발 샘플코드
4시간만에 따라해보는 Windows 10 앱 개발 샘플코드
영욱 김
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
Nelson Glauber Leal
 
First java-server-faces-tutorial-en
First java-server-faces-tutorial-enFirst java-server-faces-tutorial-en
First java-server-faces-tutorial-entechbed
 
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...
Corley S.r.l.
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
Nelson Glauber Leal
 
EMF Tips n Tricks
EMF Tips n TricksEMF Tips n Tricks
EMF Tips n Tricks
Kaniska Mandal
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
Rafael Felix da Silva
 
Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
Michael Galpin
 
Cordova training : Day 4 - Advanced Javascript
Cordova training : Day 4 - Advanced JavascriptCordova training : Day 4 - Advanced Javascript
Cordova training : Day 4 - Advanced Javascript
Binu Paul
 
813 LAB Library book sorting Note that only maincpp can .pdf
813 LAB Library book sorting Note that only maincpp can .pdf813 LAB Library book sorting Note that only maincpp can .pdf
813 LAB Library book sorting Note that only maincpp can .pdf
sastaindin
 
Building DSLs with Groovy
Building DSLs with GroovyBuilding DSLs with Groovy
Building DSLs with Groovy
Sten Anderson
 
Advanced Django ORM techniques
Advanced Django ORM techniquesAdvanced Django ORM techniques
Advanced Django ORM techniques
Daniel Roseman
 
Javascript.ppt
Javascript.pptJavascript.ppt
Javascript.ppt
NoralieNicol
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
Peter Friese
 
Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...Shakir Majeed Khan
 
Intro to Core Data
Intro to Core DataIntro to Core Data
Intro to Core Data
Make School
 
The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84
Mahmoud Samir Fayed
 

Similar to Firebase for Apple Developers (20)

Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
4시간만에 따라해보는 Windows 10 앱 개발 샘플코드
4시간만에 따라해보는 Windows 10 앱 개발 샘플코드4시간만에 따라해보는 Windows 10 앱 개발 샘플코드
4시간만에 따라해보는 Windows 10 앱 개발 샘플코드
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
First java-server-faces-tutorial-en
First java-server-faces-tutorial-enFirst java-server-faces-tutorial-en
First java-server-faces-tutorial-en
 
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...
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
 
EMF Tips n Tricks
EMF Tips n TricksEMF Tips n Tricks
EMF Tips n Tricks
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
 
Cordova training : Day 4 - Advanced Javascript
Cordova training : Day 4 - Advanced JavascriptCordova training : Day 4 - Advanced Javascript
Cordova training : Day 4 - Advanced Javascript
 
813 LAB Library book sorting Note that only maincpp can .pdf
813 LAB Library book sorting Note that only maincpp can .pdf813 LAB Library book sorting Note that only maincpp can .pdf
813 LAB Library book sorting Note that only maincpp can .pdf
 
Building DSLs with Groovy
Building DSLs with GroovyBuilding DSLs with Groovy
Building DSLs with Groovy
 
JNDI
JNDIJNDI
JNDI
 
Advanced Django ORM techniques
Advanced Django ORM techniquesAdvanced Django ORM techniques
Advanced Django ORM techniques
 
Javascript.ppt
Javascript.pptJavascript.ppt
Javascript.ppt
 
Metaworks3
Metaworks3Metaworks3
Metaworks3
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...Sharepoint Saturday India Online best practice for developing share point sol...
Sharepoint Saturday India Online best practice for developing share point sol...
 
Intro to Core Data
Intro to Core DataIntro to Core Data
Intro to Core Data
 
The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84
 

More from Peter Friese

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
Peter Friese
 
Firebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopFirebase & SwiftUI Workshop
Firebase & SwiftUI Workshop
Peter Friese
 
async/await in Swift
async/await in Swiftasync/await in Swift
async/await in Swift
Peter Friese
 
Building Apps with SwiftUI and Firebase
Building Apps with SwiftUI and FirebaseBuilding Apps with SwiftUI and Firebase
Building Apps with SwiftUI and Firebase
Peter 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 Firebase
Peter 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 Auth
Peter 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 Auth
Peter 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 Assistant
Peter 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 Google
Peter 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.0
Peter Friese
 
Google Fit, Android Wear & Xamarin
Google Fit, Android Wear & XamarinGoogle Fit, Android Wear & Xamarin
Google Fit, Android Wear & Xamarin
Peter Friese
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android Wear
Peter Friese
 
Google Play Services Rock
Google Play Services RockGoogle Play Services Rock
Google Play Services Rock
Peter Friese
 
Introduction to Android Wear
Introduction to Android WearIntroduction to Android Wear
Introduction to Android Wear
Peter 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 Android
Peter 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-In
Peter 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 Robolectric
Peter 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 Development
Peter Friese
 

More from Peter Friese (20)

Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Firebase & SwiftUI Workshop
Firebase & SwiftUI WorkshopFirebase & SwiftUI Workshop
Firebase & SwiftUI Workshop
 
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

Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Paul Groth
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
Product School
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Tobias Schneck
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 

Recently uploaded (20)

Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdfFIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
FIDO Alliance Osaka Seminar: FIDO Security Aspects.pdf
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 

Firebase for Apple Developers

  • 1. Peter Friese | Firebase Developer Advocate | @pete!riese  + Firebase for Apple Developers
  • 2. Architecture Swi!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
  • 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(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
  • 12. Challenge 2 How to update only when the user commits?
  • 13. 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 presentationMode.wrappedValue.dismiss() } } Solution 2: use inner @ObserverableObject
  • 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 presentationMode.wrappedValue.dismiss() } } Solution 2: use inner @ObserverableObject
  • 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 presentationMode.wrappedValue.dismiss() } } Solution 2: use inner @ObserverableObject
  • 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 presentationMode.wrappedValue.dismiss() } } Solution 2: use inner @ObserverableObject
  • 18. 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 @ObserverableObject Bonus: use Combine to perform validation
  • 19. Architecture Swi!UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 20.
  • 21. Run with confidence Engage users Develop apps faster
  • 22. 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
  • 23.
  • 24.
  • 25.
  • 26. #protip: Put it beneath Asset.xcasset
  • 27. Don’t forget to add to all the targets!
  • 28. Swift Package Manager now officially supported! h"ps://github.com/#rebase/#rebase-ios-sdk
  • 29. !
  • 30. Application Lifecycle SwiftUI 2 Photo by Thor Alvis on Unsplash
  • 31. 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") } } } }
  • 32. 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") } } } }
  • 33. 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") }
  • 34. 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() }
  • 35. 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
  • 36. 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
  • 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. Watch the scene phase Handle deep links Continue user activities Do more with the new life cycle
  • 40. Watch the scene phase Handle deep links Continue user activities Learn more pete$riese.dev/ultimate-guide-to-swi!ui2-application-lifecycle/
  • 41. Architecture Swi!UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 43.
  • 44.
  • 49. 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
  • 50. 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
  • 51. 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
  • 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 Can we do better?
  • 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
  • 59. 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) } } } } Mapping data using Codable if let document = document { do { self.book = try document.data(as: Book.self) } catch { print(error) } }
  • 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) } } } } Mapping data using Codable if let document = document { do { self.book = try document.data(as: Book.self) } catch { print(error) } }
  • 61. BookShelfView BookEditView BookDetailsView BookRowView Source of Truth books: [Book] @ObservableObject @Binding Book Book Book Review: Architecture
  • 62. BookShelfView BookDetailsView BookRowView Source of Truth books: [Book] @ObservableObject @Binding Book Book Review: Architecture Snapshot Listener
  • 63. 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
  • 64. 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
  • 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.
  • 72. Architecture Swi!UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 74. Authentication Photo by Eduardo Soares on Unsplash
  • 75. Authentication Photo by Victor Freitas on Unsplash
  • 76.
  • 77. Sign in the user Update the data model Secure users’ data How to implement Firebase Authentication?
  • 79. func signIn() { registerStateListener() if Auth.auth().currentUser !% nil { Auth.auth().signInAnonymously() } } Anonymous Authentication
  • 80.
  • 81. 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) { !!& } self.presentationMode.wrappedValue.dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 82. 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) { !!& } self.presentationMode.wrappedValue.dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 83. 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) { !!& } self.presentationMode.wrappedValue.dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 84. 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) { !!& } self.presentationMode.wrappedValue.dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 85. 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) { !!& } self.presentationMode.wrappedValue.dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 86. 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) { !!& } self.presentationMode.wrappedValue.dismiss() } } ).frame(width: 280, height: 45, alignment: .center) Sign in with Apple
  • 87. All books are stored in one single collection Which user do they belong to?
  • 88. let query = db.collection("books") .whereField("userId", isEqualTo: self.userId) query .addSnapshotListener { [weak self] (querySnapsho guard let documents = querySnapshot!$documents els Signed in user
  • 89. 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
  • 90. Architecture Swi!UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 92. auth!$signInAnonymously() let user = auth!$currentUser print("User signed in with user ID: (user!$uid)") This might be nil
  • 93. 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
  • 94. 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)") } @Published var user: User? !!& auth!$signInAnonymously() .map{ $0.user } .replaceError(with: nil) .assign(to: &$user) Even better
  • 95. Follow the project for updates h"ps://github.com/#rebase/#rebase-ios-sdk/projects/3
  • 96. Architecture Swi!UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 98. 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
  • 99. 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 Probably in Swift 5.5
  • 100. 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 Probably in Swift 5.5
  • 101. 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 Probably in Swift 5.5
  • 102. 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 Probably in Swift 5.5
  • 103. 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 Probably in Swift 5.5
  • 104. 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
  • 106. Architecture Swi!UI 2 Life Cycle Firebase Firestore Authentication Combine async/await Completion block
  • 108. Credits #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% 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%esy h"ps://#rebase.google.com/brand-guidelines Firebase logos cou%esy h"ps://#rebase.google.com/brand-guidelines Thanks! Hea% by Roman from the Noun Project