SlideShare a Scribd company logo
SwiftUI and Combine
All The Things
Scott Gardner
Download starter: bit.ly/combineswiftuiatt
SwiftUI and Combine
All The Things
@scotteg
scotteg
scotteg.com
About Me
iOS app architect & developer since 2010
iOS Architect at Wayfair
Author of video courses & books on
Swift & iOS app development
Apple Certified Trainer for Swift and iOS
What’s In Store
Combine basics
UIKit vs. SwiftUI + Combine
Anatomy of a SwiftUI View
Convert UIKit app
What’s In Store
Combine basics
UIKit vs. SwiftUI + Combine
Anatomy of a SwiftUI View
Convert UIKit app
Wrap up
Want more?
What is Combine?
Reactive framework for asynchronous operations
Declarative vs. imperative
Unified approach
Data
Notification
Center
Grand Central
Dispatch
Delegates
ClosuresOperations
Timers
Why Combine?
Data
Combine
SwiftUI
Why Combine?
Combine
SwiftUI
Foundation Core Data
@FetchRequest
NSManagedObject
URLSession
NotificationCenter
Why Combine?
ObservableObject
@Published
@ObservedObject
(@State / @Binding)
Combine Basics
Publisher Subscriber
subscribes
gives subscription
requests values
sends values
sends completion
Combine Basics
Publisher Subscriber
subscribes
gives subscription
requests values
sends values
sends completion
Combine Basics
Publisher Subscriber
subscribes
gives subscription
requests values
sends values
sends completion
cancels
Combine Operators
1 2 3
map { $0 * 2 }
2 4 6
Publisher
Subscriber
UIKit + Traditional
Fetch data
Decode
Update state
Update UI
UIKit + Traditional
Fetch dataDecodeUpdate stateUpdate UI
SwiftUI + Combine
Fetch data
Decode
Update state
SwiftUI + Combine
Fetch dataDecodeUpdate state
SwiftUI + CombineUIKit + Traditional
U
Update UI in UIKit
OUT OF STOCK
Product: 8’ Gnome
Product: 8’ Gnome
F
D
S
Quantity: 1
If you loved gottfried,
you’ll fall even harder
for his truly giant big
brother who simply
cannot be missed.
Gottfried's big bro
thunders onto the scene
from Schleswig…
Quantity: 1
Update UI in Combine + SwiftUI
Product: 8’ Gnome
Product: 8’ Gnome
F
D
S
Quantity: 1
If you loved gottfried,
you’ll fall even harder
for his truly giant big
brother who simply
cannot be missed.
Gottfried's big bro
thunders onto the scene
from Schleswig…
Quantity: 1
BUY NOW
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Typically a struct, but can also be a final class
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
A protocol that requires a body property
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
A protocol that requires a body property
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Property wrappers that manage observable state
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Creates a visible navigation hierarchy
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Creates a vertical stack view
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
A custom view that wraps a UISearchBar
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Creates a table view
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Creates views on demand from a collection
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
A button that triggers navigation
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
View modifiers
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
View modifiers
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
Presents a view as a sheet
struct ContentView: View {
@ObservedObject private var viewModel = GistsViewModel()
@State private var showSettings = false
var body: some View {
NavigationView {
VStack {
SearchBar(text: $viewModel.searchText)
List {
ForEach(viewModel.gists) { gist in
NavigationLink(destination: SafariView(url: gist.htmlURL)
.navigationBarTitle("")
.navigationBarHidden(true)) {
GistView(gist: gist)
}
}
}
.navigationBarTitle(Text("GitHub Gists"))
.navigationBarItems(trailing:
Button(action: { self.showSettings.toggle() }, label: {
Image(systemName: "gear")
.foregroundColor(.primary)
})
)
.edgesIgnoringSafeArea(.bottom)
}
.sheet(isPresented: $showSettings) {
SettingsView()
}
}
}
}
Anatomy of a SwiftUI View
A protocol that requires a body property
Typically a struct, but can also be a final class
Property wrappers that manage observable state
From SwiftUI and Combine All The Things - Scott Gardner - scotteg.com @scotteg
Creates a visible navigation hierarchy
Creates a vertical stack view
A custom view that wraps a UISearchBar
Creates a table view
Creates views on demand from a collection
A button that triggers navigation
View modifiers
Presents a view as a sheet
–Bruce Lee
“If you want to learn to swim,
jump into the water.”
Download starter:
bit.ly/combineswiftuiatt
Download final:
bit.ly/combineswiftuiatt-final
1401 ./App/AppDelegate.swift
2142 ./App/SceneDelegate.swift
803 ./View Models/GistCellViewModel.swift
1722 ./View Models/GistsViewModel.swift
1680 ./Models/Gist.swift
535 ./Models/Owner.swift
1738 ./Views/GistView.swift
1046 ./Views/SafariView.swift
1034 ./Views/SearchBar.swift
1155 ./Views/ContentView.swift
1077 ./Services/RemoteImageService.swift
14333 total
find . -name *.swift -print0 | xargs -0 wc
UIKit + Traditional SwiftUI + Combine
1281 ./App/AppDelegate.swift
2354 ./App/SceneDelegate.swift
615 ./View Models/GistCellViewModel.swift
1926 ./View Models/GistsViewModel.swift
1646 ./Models/Gist.swift
535 ./Models/Owner.swift
5135 ./Views/GistCell.swift
3446 ./Views/GistsViewController.swift
1269 ./Services/RemoteImageService.swift
18207 total
14333 total
UIKit + Traditional SwiftUI + Combine
18207 total
UIKit + Traditional SwiftUI + Combine
bit.ly/combinebook bit.ly/swiftuibook
What property wrapper creates
a Publisher?
@Published
What View type triggers a
navigation when pressed?
NavigationLink
Questions?
Thanks!
@scotteg
scotteg
scotteg.com
$scotteg

More Related Content

What's hot

Spring framework in depth
Spring framework in depthSpring framework in depth
Spring framework in depth
Vinay Kumar
 
REST APIs with Spring
REST APIs with SpringREST APIs with Spring
REST APIs with Spring
Joshua Long
 
Node Architecture and Getting Started with Express
Node Architecture and Getting Started with ExpressNode Architecture and Getting Started with Express
Node Architecture and Getting Started with Express
jguerrero999
 
Spring Boot
Spring BootSpring Boot
Spring Boot
Pei-Tang Huang
 
Introduction to Spring Framework and Spring IoC
Introduction to Spring Framework and Spring IoCIntroduction to Spring Framework and Spring IoC
Introduction to Spring Framework and Spring IoC
Funnelll
 
Angular - Chapter 7 - HTTP Services
Angular - Chapter 7 - HTTP ServicesAngular - Chapter 7 - HTTP Services
Angular - Chapter 7 - HTTP Services
WebStackAcademy
 
iOS Modular Architecture with Tuist
iOS Modular Architecture with TuistiOS Modular Architecture with Tuist
iOS Modular Architecture with Tuist
정민 안
 
Angular modules in depth
Angular modules in depthAngular modules in depth
Angular modules in depth
Christoffer Noring
 
Spring boot - an introduction
Spring boot - an introductionSpring boot - an introduction
Spring boot - an introduction
Jonathan Holloway
 
Introduzione ad angular 7/8
Introduzione ad angular 7/8Introduzione ad angular 7/8
Introduzione ad angular 7/8
Valerio Radice
 
SwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory ManagementSwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory Management
WannitaTolaema
 
Angular 8
Angular 8 Angular 8
Angular 8
Sunil OS
 
Declarative UIs with Jetpack Compose
Declarative UIs with Jetpack ComposeDeclarative UIs with Jetpack Compose
Declarative UIs with Jetpack Compose
Ramon Ribeiro Rabello
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
Ignacio Martín
 
Rediscovering Spring with Spring Boot(1)
Rediscovering Spring with Spring Boot(1)Rediscovering Spring with Spring Boot(1)
Rediscovering Spring with Spring Boot(1)Gunith Devasurendra
 
ReactJS
ReactJSReactJS
Spring MVC 3.0 Framework
Spring MVC 3.0 FrameworkSpring MVC 3.0 Framework
Spring Boot in Action
Spring Boot in Action Spring Boot in Action
Spring Boot in Action
Alex Movila
 
Spring boot
Spring bootSpring boot
Spring boot
sdeeg
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!Jakub Kubrynski
 

What's hot (20)

Spring framework in depth
Spring framework in depthSpring framework in depth
Spring framework in depth
 
REST APIs with Spring
REST APIs with SpringREST APIs with Spring
REST APIs with Spring
 
Node Architecture and Getting Started with Express
Node Architecture and Getting Started with ExpressNode Architecture and Getting Started with Express
Node Architecture and Getting Started with Express
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Introduction to Spring Framework and Spring IoC
Introduction to Spring Framework and Spring IoCIntroduction to Spring Framework and Spring IoC
Introduction to Spring Framework and Spring IoC
 
Angular - Chapter 7 - HTTP Services
Angular - Chapter 7 - HTTP ServicesAngular - Chapter 7 - HTTP Services
Angular - Chapter 7 - HTTP Services
 
iOS Modular Architecture with Tuist
iOS Modular Architecture with TuistiOS Modular Architecture with Tuist
iOS Modular Architecture with Tuist
 
Angular modules in depth
Angular modules in depthAngular modules in depth
Angular modules in depth
 
Spring boot - an introduction
Spring boot - an introductionSpring boot - an introduction
Spring boot - an introduction
 
Introduzione ad angular 7/8
Introduzione ad angular 7/8Introduzione ad angular 7/8
Introduzione ad angular 7/8
 
SwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory ManagementSwiftUI - Performance and Memory Management
SwiftUI - Performance and Memory Management
 
Angular 8
Angular 8 Angular 8
Angular 8
 
Declarative UIs with Jetpack Compose
Declarative UIs with Jetpack ComposeDeclarative UIs with Jetpack Compose
Declarative UIs with Jetpack Compose
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
Rediscovering Spring with Spring Boot(1)
Rediscovering Spring with Spring Boot(1)Rediscovering Spring with Spring Boot(1)
Rediscovering Spring with Spring Boot(1)
 
ReactJS
ReactJSReactJS
ReactJS
 
Spring MVC 3.0 Framework
Spring MVC 3.0 FrameworkSpring MVC 3.0 Framework
Spring MVC 3.0 Framework
 
Spring Boot in Action
Spring Boot in Action Spring Boot in Action
Spring Boot in Action
 
Spring boot
Spring bootSpring boot
Spring boot
 
Introduction to Spring Boot!
Introduction to Spring Boot!Introduction to Spring Boot!
Introduction to Spring Boot!
 

Similar to SwiftUI and Combine All the Things

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
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
NAVER Engineering
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
Peter Friese
 
Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015 Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015
Mario Jorge Pereira
 
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
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
pootsbook
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentation
Valdis Iljuconoks
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of usOSCON Byrum
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
Robert DeLuca
 
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ - Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Yuji Hato
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
Nick Lee
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j queryMd. Ziaul Haq
 
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
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
Rafael Felix da Silva
 
Building iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" DominoBuilding iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" Domino
Rob Bontekoe
 
UIWebView Tips
UIWebView TipsUIWebView Tips
UIWebView Tips
Katsumi Kishikawa
 
Android query
Android queryAndroid query
Android query
Michal Pavlasek
 
Beautiful java script
Beautiful java scriptBeautiful java script
Beautiful java script
Ürgo Ringo
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
Knoldus Inc.
 

Similar to SwiftUI and Combine All the Things (20)

Getting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUIGetting Started with Combine And SwiftUI
Getting Started with Combine And SwiftUI
 
[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern[22]Efficient and Testable MVVM pattern
[22]Efficient and Testable MVVM pattern
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015 Android por onde começar? Mini Curso Erbase 2015
Android por onde começar? Mini Curso Erbase 2015
 
Arquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com JetpackArquitetando seu aplicativo Android com Jetpack
Arquitetando seu aplicativo Android com Jetpack
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
Android crashcourse
Android crashcourseAndroid crashcourse
Android crashcourse
 
Knockoutjs UG meeting presentation
Knockoutjs UG meeting presentationKnockoutjs UG meeting presentation
Knockoutjs UG meeting presentation
 
Big Data for each one of us
Big Data for each one of usBig Data for each one of us
Big Data for each one of us
 
Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ - Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Kick start with j query
Kick start with j queryKick start with j query
Kick start with j query
 
Arquitetando seu app Android com Jetpack
Arquitetando seu app Android com JetpackArquitetando seu app Android com Jetpack
Arquitetando seu app Android com Jetpack
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
Building iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" DominoBuilding iPhone Web Apps using "classic" Domino
Building iPhone Web Apps using "classic" Domino
 
UIWebView Tips
UIWebView TipsUIWebView Tips
UIWebView Tips
 
Android query
Android queryAndroid query
Android query
 
Beautiful java script
Beautiful java scriptBeautiful java script
Beautiful java script
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 

Recently uploaded

HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
Robbie Edward Sayers
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
bakpo1
 
AP LAB PPT.pdf ap lab ppt no title specific
AP LAB PPT.pdf ap lab ppt no title specificAP LAB PPT.pdf ap lab ppt no title specific
AP LAB PPT.pdf ap lab ppt no title specific
BrazilAccount1
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
obonagu
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
karthi keyan
 
The role of big data in decision making.
The role of big data in decision making.The role of big data in decision making.
The role of big data in decision making.
ankuprajapati0525
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
seandesed
 
power quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptxpower quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptx
ViniHema
 
Immunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary AttacksImmunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary Attacks
gerogepatton
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
Jayaprasanna4
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
fxintegritypublishin
 
Standard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - NeometrixStandard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - Neometrix
Neometrix_Engineering_Pvt_Ltd
 
Planning Of Procurement o different goods and services
Planning Of Procurement o different goods and servicesPlanning Of Procurement o different goods and services
Planning Of Procurement o different goods and services
JoytuBarua2
 
Cosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdfCosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdf
Kamal Acharya
 
WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234
AafreenAbuthahir2
 
Governing Equations for Fundamental Aerodynamics_Anderson2010.pdf
Governing Equations for Fundamental Aerodynamics_Anderson2010.pdfGoverning Equations for Fundamental Aerodynamics_Anderson2010.pdf
Governing Equations for Fundamental Aerodynamics_Anderson2010.pdf
WENKENLI1
 
block diagram and signal flow graph representation
block diagram and signal flow graph representationblock diagram and signal flow graph representation
block diagram and signal flow graph representation
Divya Somashekar
 
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdfAKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
SamSarthak3
 
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
thanhdowork
 
一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理
一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理
一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理
zwunae
 

Recently uploaded (20)

HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
 
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
一比一原版(SFU毕业证)西蒙菲莎大学毕业证成绩单如何办理
 
AP LAB PPT.pdf ap lab ppt no title specific
AP LAB PPT.pdf ap lab ppt no title specificAP LAB PPT.pdf ap lab ppt no title specific
AP LAB PPT.pdf ap lab ppt no title specific
 
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
在线办理(ANU毕业证书)澳洲国立大学毕业证录取通知书一模一样
 
CME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional ElectiveCME397 Surface Engineering- Professional Elective
CME397 Surface Engineering- Professional Elective
 
The role of big data in decision making.
The role of big data in decision making.The role of big data in decision making.
The role of big data in decision making.
 
Architectural Portfolio Sean Lockwood
Architectural Portfolio Sean LockwoodArchitectural Portfolio Sean Lockwood
Architectural Portfolio Sean Lockwood
 
power quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptxpower quality voltage fluctuation UNIT - I.pptx
power quality voltage fluctuation UNIT - I.pptx
 
Immunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary AttacksImmunizing Image Classifiers Against Localized Adversary Attacks
Immunizing Image Classifiers Against Localized Adversary Attacks
 
ethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.pptethical hacking in wireless-hacking1.ppt
ethical hacking in wireless-hacking1.ppt
 
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdfHybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
Hybrid optimization of pumped hydro system and solar- Engr. Abdul-Azeez.pdf
 
Standard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - NeometrixStandard Reomte Control Interface - Neometrix
Standard Reomte Control Interface - Neometrix
 
Planning Of Procurement o different goods and services
Planning Of Procurement o different goods and servicesPlanning Of Procurement o different goods and services
Planning Of Procurement o different goods and services
 
Cosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdfCosmetic shop management system project report.pdf
Cosmetic shop management system project report.pdf
 
WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234WATER CRISIS and its solutions-pptx 1234
WATER CRISIS and its solutions-pptx 1234
 
Governing Equations for Fundamental Aerodynamics_Anderson2010.pdf
Governing Equations for Fundamental Aerodynamics_Anderson2010.pdfGoverning Equations for Fundamental Aerodynamics_Anderson2010.pdf
Governing Equations for Fundamental Aerodynamics_Anderson2010.pdf
 
block diagram and signal flow graph representation
block diagram and signal flow graph representationblock diagram and signal flow graph representation
block diagram and signal flow graph representation
 
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdfAKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
AKS UNIVERSITY Satna Final Year Project By OM Hardaha.pdf
 
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
RAT: Retrieval Augmented Thoughts Elicit Context-Aware Reasoning in Long-Hori...
 
一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理
一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理
一比一原版(IIT毕业证)伊利诺伊理工大学毕业证成绩单专业办理
 

SwiftUI and Combine All the Things

  • 1. SwiftUI and Combine All The Things Scott Gardner Download starter: bit.ly/combineswiftuiatt
  • 2. SwiftUI and Combine All The Things @scotteg scotteg scotteg.com
  • 3. About Me iOS app architect & developer since 2010 iOS Architect at Wayfair Author of video courses & books on Swift & iOS app development Apple Certified Trainer for Swift and iOS
  • 4. What’s In Store Combine basics UIKit vs. SwiftUI + Combine Anatomy of a SwiftUI View Convert UIKit app
  • 5. What’s In Store Combine basics UIKit vs. SwiftUI + Combine Anatomy of a SwiftUI View Convert UIKit app Wrap up Want more?
  • 6. What is Combine? Reactive framework for asynchronous operations Declarative vs. imperative Unified approach
  • 9. Combine SwiftUI Foundation Core Data @FetchRequest NSManagedObject URLSession NotificationCenter Why Combine? ObservableObject @Published @ObservedObject (@State / @Binding)
  • 10. Combine Basics Publisher Subscriber subscribes gives subscription requests values sends values sends completion
  • 11. Combine Basics Publisher Subscriber subscribes gives subscription requests values sends values sends completion
  • 12. Combine Basics Publisher Subscriber subscribes gives subscription requests values sends values sends completion cancels
  • 13. Combine Operators 1 2 3 map { $0 * 2 } 2 4 6 Publisher Subscriber
  • 14. UIKit + Traditional Fetch data Decode Update state Update UI
  • 15. UIKit + Traditional Fetch dataDecodeUpdate stateUpdate UI
  • 16. SwiftUI + Combine Fetch data Decode Update state
  • 17. SwiftUI + Combine Fetch dataDecodeUpdate state
  • 18. SwiftUI + CombineUIKit + Traditional
  • 19. U Update UI in UIKit OUT OF STOCK Product: 8’ Gnome Product: 8’ Gnome F D S Quantity: 1 If you loved gottfried, you’ll fall even harder for his truly giant big brother who simply cannot be missed. Gottfried's big bro thunders onto the scene from Schleswig… Quantity: 1
  • 20. Update UI in Combine + SwiftUI Product: 8’ Gnome Product: 8’ Gnome F D S Quantity: 1 If you loved gottfried, you’ll fall even harder for his truly giant big brother who simply cannot be missed. Gottfried's big bro thunders onto the scene from Schleswig… Quantity: 1 BUY NOW
  • 21. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View
  • 22. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Typically a struct, but can also be a final class
  • 23. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View A protocol that requires a body property
  • 24. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View A protocol that requires a body property
  • 25. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Property wrappers that manage observable state
  • 26. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View
  • 27. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View
  • 28. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View
  • 29. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Creates a visible navigation hierarchy
  • 30. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Creates a vertical stack view
  • 31. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View A custom view that wraps a UISearchBar
  • 32. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Creates a table view
  • 33. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View
  • 34. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Creates views on demand from a collection
  • 35. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View A button that triggers navigation
  • 36. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View View modifiers
  • 37. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View View modifiers
  • 38. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View Presents a view as a sheet
  • 39. struct ContentView: View { @ObservedObject private var viewModel = GistsViewModel() @State private var showSettings = false var body: some View { NavigationView { VStack { SearchBar(text: $viewModel.searchText) List { ForEach(viewModel.gists) { gist in NavigationLink(destination: SafariView(url: gist.htmlURL) .navigationBarTitle("") .navigationBarHidden(true)) { GistView(gist: gist) } } } .navigationBarTitle(Text("GitHub Gists")) .navigationBarItems(trailing: Button(action: { self.showSettings.toggle() }, label: { Image(systemName: "gear") .foregroundColor(.primary) }) ) .edgesIgnoringSafeArea(.bottom) } .sheet(isPresented: $showSettings) { SettingsView() } } } } Anatomy of a SwiftUI View A protocol that requires a body property Typically a struct, but can also be a final class Property wrappers that manage observable state From SwiftUI and Combine All The Things - Scott Gardner - scotteg.com @scotteg Creates a visible navigation hierarchy Creates a vertical stack view A custom view that wraps a UISearchBar Creates a table view Creates views on demand from a collection A button that triggers navigation View modifiers Presents a view as a sheet
  • 40. –Bruce Lee “If you want to learn to swim, jump into the water.”
  • 43. 1401 ./App/AppDelegate.swift 2142 ./App/SceneDelegate.swift 803 ./View Models/GistCellViewModel.swift 1722 ./View Models/GistsViewModel.swift 1680 ./Models/Gist.swift 535 ./Models/Owner.swift 1738 ./Views/GistView.swift 1046 ./Views/SafariView.swift 1034 ./Views/SearchBar.swift 1155 ./Views/ContentView.swift 1077 ./Services/RemoteImageService.swift 14333 total find . -name *.swift -print0 | xargs -0 wc UIKit + Traditional SwiftUI + Combine 1281 ./App/AppDelegate.swift 2354 ./App/SceneDelegate.swift 615 ./View Models/GistCellViewModel.swift 1926 ./View Models/GistsViewModel.swift 1646 ./Models/Gist.swift 535 ./Models/Owner.swift 5135 ./Views/GistCell.swift 3446 ./Views/GistsViewController.swift 1269 ./Services/RemoteImageService.swift 18207 total
  • 44. 14333 total UIKit + Traditional SwiftUI + Combine 18207 total
  • 45. UIKit + Traditional SwiftUI + Combine
  • 46.
  • 48. What property wrapper creates a Publisher?
  • 50. What View type triggers a navigation when pressed?