SlideShare a Scribd company logo
Getting Started with
Combine and SwiftUI in iOS
Scott Gardner
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
Create Crosstalk app
What’s In Store
Combine basics
UIKit vs. SwiftUI + Combine
Anatomy of a SwiftUI View
Create Crosstalk app
Wrap up
Want more?
Download resources:
bit.ly/gscsresources
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
Update UI in UIKit
OUT OF STOCK
Product: 8’ Gnome
Quantity: 0
Product: 8’ Gnome
Quantity: 0
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…
U
Update UI in UIKit
OUT OF STOCK
Product: 8’ Gnome
Quantity: 0
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…
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
OUT OF STOCK
Product: 8’ Gnome
Quantity: 0
Product: 8’ Gnome
Quantity: 0
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…
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 Getting Started with Combine and SwiftUI in iOS - 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 resources:
bit.ly/gscsresources
Download final:
bit.ly/gscsfinal
Getting Started with Combine And SwiftUI
bit.ly/combinebook bit.ly/swiftuibook
What property lets you receive a
bindable object from an ancestor?
@EnvironmentObject
What property wrapper lets you
connect an @State property to a
child view?
@Binding
Questions?
Thanks!
@scotteg
scotteg
scotteg.com
$scotteg

More Related Content

PDF
SwiftUI and Combine All the Things
PDF
MVVM with SwiftUI and Combine
PDF
Building Universal Web Apps with React ForwardJS 2017
PPTX
J Query (Complete Course) by Muhammad Ehtisham Siddiqui
PDF
jQuery and Rails, Sitting in a Tree
PDF
[FEConf Korea 2017]Angular 컴포넌트 대화법
PDF
Creating lightweight JS Apps w/ Web Components and lit-html
KEY
Design Patterns for Tablets and Smartphones
SwiftUI and Combine All the Things
MVVM with SwiftUI and Combine
Building Universal Web Apps with React ForwardJS 2017
J Query (Complete Course) by Muhammad Ehtisham Siddiqui
jQuery and Rails, Sitting in a Tree
[FEConf Korea 2017]Angular 컴포넌트 대화법
Creating lightweight JS Apps w/ Web Components and lit-html
Design Patterns for Tablets and Smartphones

What's hot (20)

PDF
Drupal 7 Entity & Entity API
PDF
jQuery Mobile Workshop
PDF
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
PDF
Webcomponents at Frontend meetup 2014.06.25
PDF
Frontend meetup 2014.06.25
PPT
Agile and rails
PDF
Arquitetando seu aplicativo Android com Jetpack
PPTX
Angular Tutorial Freshers and Experienced
PPTX
A Rich Web Experience with jQuery, Ajax and .NET
PPT
Backbone.js
PPTX
jQuery for web development
PDF
Building iPhone Web Apps using "classic" Domino
PDF
The Art of AngularJS in 2015 - Angular Summit 2015
PPTX
PDF
Short intro to JQuery and Modernizr
PPTX
jQuery
PDF
Firebase for Apple Developers
PPTX
Javascript first-class citizenery
PPTX
Migration to jQuery 3.5.x
PDF
Understanding backbonejs
Drupal 7 Entity & Entity API
jQuery Mobile Workshop
 +  = ❤️ (Firebase for Apple Developers) at Swift Leeds
Webcomponents at Frontend meetup 2014.06.25
Frontend meetup 2014.06.25
Agile and rails
Arquitetando seu aplicativo Android com Jetpack
Angular Tutorial Freshers and Experienced
A Rich Web Experience with jQuery, Ajax and .NET
Backbone.js
jQuery for web development
Building iPhone Web Apps using "classic" Domino
The Art of AngularJS in 2015 - Angular Summit 2015
Short intro to JQuery and Modernizr
jQuery
Firebase for Apple Developers
Javascript first-class citizenery
Migration to jQuery 3.5.x
Understanding backbonejs
Ad

Similar to Getting Started with Combine And SwiftUI (20)

PPTX
When You Can’t Go All In on SwiftUI, Build a Hybrid UI App Instead!
PPTX
Deep dive into swift UI
PDF
Сергій Міськів, «SwiftUI: Animations»
PDF
Swift
PDF
Explore the evolution of UI development with 'Why SwiftUI?'
PPTX
Enhancedpptfor_SwiftUI_Presentation.pptx
PDF
Do iOS Presentation - Mobile app architectures
PDF
SwiftUI in 2024: Bridging Perception and Reality
PPTX
iOS 7
KEY
UIAutomation + Mechanic.js
PDF
What 100M downloads taught us about iOS architectures
PDF
SwiftUI - Performance and Memory Management
PDF
What's new in iOS9
PDF
From iOS to Android
PPT
Programming iOS in C#
PPTX
Lessons-Learned-SwiftUI.pptx
PDF
New Features in iOS 15 and Swift 5.5.pdf
PDF
Xcode7 UI Automation
PDF
Beginning Ios Programming With Swift And Swiftui Ios 15 Simon Ng
PPTX
Building your first iOS app using Xamarin
When You Can’t Go All In on SwiftUI, Build a Hybrid UI App Instead!
Deep dive into swift UI
Сергій Міськів, «SwiftUI: Animations»
Swift
Explore the evolution of UI development with 'Why SwiftUI?'
Enhancedpptfor_SwiftUI_Presentation.pptx
Do iOS Presentation - Mobile app architectures
SwiftUI in 2024: Bridging Perception and Reality
iOS 7
UIAutomation + Mechanic.js
What 100M downloads taught us about iOS architectures
SwiftUI - Performance and Memory Management
What's new in iOS9
From iOS to Android
Programming iOS in C#
Lessons-Learned-SwiftUI.pptx
New Features in iOS 15 and Swift 5.5.pdf
Xcode7 UI Automation
Beginning Ios Programming With Swift And Swiftui Ios 15 Simon Ng
Building your first iOS app using Xamarin
Ad

Recently uploaded (20)

PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PPTX
Comunidade Salesforce São Paulo - Desmistificando o Omnistudio (Vlocity)
PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PDF
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Advanced Soft Computing BINUS July 2025.pdf
PPTX
Cloud computing and distributed systems.
PPT
Teaching material agriculture food technology
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Advanced IT Governance
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
AI And Its Effect On The Evolving IT Sector In Australia - Elevate
PDF
KodekX | Application Modernization Development
PDF
Modernizing your data center with Dell and AMD
Reach Out and Touch Someone: Haptics and Empathic Computing
Comunidade Salesforce São Paulo - Desmistificando o Omnistudio (Vlocity)
GamePlan Trading System Review: Professional Trader's Honest Take
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
NewMind AI Monthly Chronicles - July 2025
Dropbox Q2 2025 Financial Results & Investor Presentation
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Spectral efficient network and resource selection model in 5G networks
Advanced Soft Computing BINUS July 2025.pdf
Cloud computing and distributed systems.
Teaching material agriculture food technology
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Advanced IT Governance
NewMind AI Weekly Chronicles - August'25 Week I
AI And Its Effect On The Evolving IT Sector In Australia - Elevate
KodekX | Application Modernization Development
Modernizing your data center with Dell and AMD

Getting Started with Combine And SwiftUI

  • 1. Getting Started with Combine and SwiftUI in iOS Scott Gardner
  • 2. 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
  • 3. What’s In Store Combine basics UIKit vs. SwiftUI + Combine Anatomy of a SwiftUI View Create Crosstalk app
  • 4. What’s In Store Combine basics UIKit vs. SwiftUI + Combine Anatomy of a SwiftUI View Create Crosstalk 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. Update UI in UIKit OUT OF STOCK Product: 8’ Gnome Quantity: 0 Product: 8’ Gnome Quantity: 0 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…
  • 20. U Update UI in UIKit OUT OF STOCK Product: 8’ Gnome Quantity: 0 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…
  • 21. 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
  • 22. Update UI in Combine + SwiftUI OUT OF STOCK Product: 8’ Gnome Quantity: 0 Product: 8’ Gnome Quantity: 0 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…
  • 23. 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
  • 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
  • 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 Typically a struct, but can also be a final class
  • 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 A protocol that requires a body property
  • 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 A protocol that requires a body property
  • 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 Property wrappers that manage observable state
  • 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
  • 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
  • 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
  • 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 visible navigation hierarchy
  • 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 Creates a vertical stack 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 A custom view that wraps a UISearchBar
  • 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 Creates a table view
  • 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
  • 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 Creates views on demand from a collection
  • 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 A button that triggers navigation
  • 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 View modifiers
  • 40. 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
  • 41. 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
  • 42. 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 Getting Started with Combine and SwiftUI in iOS - 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
  • 43. –Bruce Lee “If you want to learn to swim, jump into the water.”
  • 48. What property lets you receive a bindable object from an ancestor?
  • 50. What property wrapper lets you connect an @State property to a child view?