In this talk, we are going to walk you through the building blocks for creating modern applications on Apple’s platforms using SwiftUI and Firebase.
We will cover the following topics:
- Setting up an architecture for data-driven applications
- What’s new in SwiftUI’s application life cycle
- Syncing data in realtime across multiple devices with Cloud Firestore
- Mapping Firestore documents in a type-safe way using the Codable protocol
- Storing images and other large files in Cloud Storage
- Using Firebase in WidgetKit and on watchOS
- How async/await (new in Swift 5.5) will affect the way we write asynchronous code (plus some surprises in Firebase's code base)
Firebase is always evolving, so we will also talk about how we’re making use of Apple’s latest technologies in our own code base, for example
- Combine
- async/await
- SwiftUI view modifiers
No matter if you’re a seasoned Firebase user or just wondering what it is all about, you should leave this session with a deeper understanding of what Firebase it and how you can use it your apps on Apple’s platforms.
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
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()
}
51. struct Book: Identifiable {
var id = String?
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)
}
}
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
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&enditlevsen
Thanks to
85. class ShareViewController: SLComposeServiceViewController {
override func didSelectPost() {
!" get data from share controller context
Task {
!" write image data to Cloud Storage
let _ = try await ref.putDataAsync(self.imageData as Data)
let url = try await ref.downloadURL()
!" write post data to Firestore
let post = Post(description: description !# "", url: url.absoluteString)
try db.collection("Posts").document("post").setData(from: post)
!" refresh widgets and return to share controller
WidgetCenter.shared.reloadAllTimelines()
extensionContext!$completeRequest(returningItems: [])
}
}
Firebase in a ShareViewController
86. class ShareViewController: SLComposeServiceViewController {
override func didSelectPost() {
!" get data from share controller context
Task {
!" write image data to Cloud Storage
let _ = try await ref.putDataAsync(self.imageData as Data)
let url = try await ref.downloadURL()
!" write post data to Firestore
let post = Post(description: description !# "", url: url.absoluteString)
try db.collection("Posts").document("post").setData(from: post)
!" refresh widgets and return to share controller
WidgetCenter.shared.reloadAllTimelines()
extensionContext!$completeRequest(returningItems: [])
}
}
Firebase in a ShareViewController
87. class ShareViewController: SLComposeServiceViewController {
override func didSelectPost() {
!" get data from share controller context
Task {
!" write image data to Cloud Storage
let _ = try await ref.putDataAsync(self.imageData as Data)
let url = try await ref.downloadURL()
!" write post data to Firestore
let post = Post(description: description !# "", url: url.absoluteString)
try db.collection("Posts").document("post").setData(from: post)
!" refresh widgets and return to share controller
WidgetCenter.shared.reloadAllTimelines()
extensionContext!$completeRequest(returningItems: [])
}
}
Firebase in a ShareViewController
88. class ShareViewController: SLComposeServiceViewController {
override func didSelectPost() {
!" get data from share controller context
Task {
!" write image data to Cloud Storage
let _ = try await ref.putDataAsync(self.imageData as Data)
let url = try await ref.downloadURL()
!" write post data to Firestore
let post = Post(description: description !# "", url: url.absoluteString)
try db.collection("Posts").document("post").setData(from: post)
!" refresh widgets and return to share controller
WidgetCenter.shared.reloadAllTimelines()
extensionContext!$completeRequest(returningItems: [])
}
}
Firebase in a ShareViewController
89. class ShareViewController: SLComposeServiceViewController {
override func didSelectPost() {
!" get data from share controller context
Task {
!" write image data to Cloud Storage
let _ = try await ref.putDataAsync(self.imageData as Data)
let url = try await ref.downloadURL()
!" write post data to Firestore
let post = Post(description: description !# "", url: url.absoluteString)
try db.collection("Posts").document("post").setData(from: post)
!" refresh widgets and return to share controller
WidgetCenter.shared.reloadAllTimelines()
extensionContext!$completeRequest(returningItems: [])
}
}
Firebase in a ShareViewController
90. class ShareViewController: SLComposeServiceViewController {
override func didSelectPost() {
!" get data from share controller context
Task {
!" write image data to Cloud Storage
let _ = try await ref.putDataAsync(self.imageData as Data)
let url = try await ref.downloadURL()
!" write post data to Firestore
let post = Post(description: description !# "", url: url.absoluteString)
try db.collection("Posts").document("post").setData(from: post)
!" refresh widgets and return to share controller
WidgetCenter.shared.reloadAllTimelines()
extensionContext!$completeRequest(returningItems: [])
}
}
Firebase in a ShareViewController
110. 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
111. 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
112. 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
113. 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
114. 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
115. 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
116. All books are stored in one single collection
Which user do
they belong to?
117. let query = db.collection("books")
.whereField("userId",
isEqualTo: self.userId)
query
.addSnapshotListener { [weak self] (querySnapsho
guard let documents = querySnapshot!$documents els
Signed in user
118. 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
122. 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
123. 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
127. 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
128. 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
129. 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
130. 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
131. 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
132. 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
133. 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
139. 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
Dashboard by BomSymbols from the Noun Project