Slide 1

Slide 1 text

EXPLORING STATELESS UIs in SWIFT Adam May & Sam Kirchmeier

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

tchacknight.com

Slide 4

Slide 4 text

TOPICS

Slide 5

Slide 5 text

Define application state TOPICS

Slide 6

Slide 6 text

Define application state Describe a stateless architecture for iOS TOPICS

Slide 7

Slide 7 text

Define application state Describe a stateless architecture for iOS Show examples TOPICS

Slide 8

Slide 8 text

Define application state Describe a stateless architecture for iOS Show examples Discuss challenges and future work TOPICS

Slide 9

Slide 9 text

STATELESS UIs

Slide 10

Slide 10 text

WHAT IS STATE?

Slide 11

Slide 11 text

The value of an identity at a moment in time WHAT IS STATE? Source: http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey

Slide 12

Slide 12 text

The value of an identity at a moment in time WHAT IS STATE?

Slide 13

Slide 13 text

The value of an identity at a moment in time WHAT IS STATE?

Slide 14

Slide 14 text

The value of an identity at a moment in time WHAT IS STATE?

Slide 15

Slide 15 text

The value of an identity at a moment in time WHAT IS STATE?

Slide 16

Slide 16 text

STATE IN SWIFT

Slide 17

Slide 17 text

STATE IN SWIFT var str = "Hello, playground"

Slide 18

Slide 18 text

Identity Value Time STATE IN SWIFT var str = "Hello, playground"

Slide 19

Slide 19 text

Identity Value Time STATE IN SWIFT var str = "Hello, playground"

Slide 20

Slide 20 text

Identity Value Time STATE IN SWIFT var str = "Hello, playground"

Slide 21

Slide 21 text

HIDDEN COMPLEXITY var str = "Hello, playground"

Slide 22

Slide 22 text

Assume str is global HIDDEN COMPLEXITY var str = "Hello, playground"

Slide 23

Slide 23 text

Assume str is global It’s mutable from anywhere HIDDEN COMPLEXITY var str = "Hello, playground"

Slide 24

Slide 24 text

ENCAPSULATION class NeatlyEncapsulated { private } var str = "Hello, playground"

Slide 25

Slide 25 text

Mutable via private methods ENCAPSULATION var str = "Hello, playground" class NeatlyEncapsulated { private }

Slide 26

Slide 26 text

Mutable via private methods Indirectly mutable via public methods ENCAPSULATION var str = "Hello, playground" class NeatlyEncapsulated { private }

Slide 27

Slide 27 text

Mutable via private methods Indirectly mutable via public methods Same number of moving parts ENCAPSULATION var str = "Hello, playground" class NeatlyEncapsulated { private }

Slide 28

Slide 28 text

STATE, THE PROBLEM

Slide 29

Slide 29 text

State breeds complexity STATE, THE PROBLEM

Slide 30

Slide 30 text

State breeds complexity Unexpected mutations STATE, THE PROBLEM

Slide 31

Slide 31 text

State breeds complexity Unexpected mutations Concurrency and parallelism STATE, THE PROBLEM

Slide 32

Slide 32 text

State breeds complexity Unexpected mutations Concurrency and parallelism Testing STATE, THE PROBLEM

Slide 33

Slide 33 text

WHAT DOES IT MEAN TO BE STATELESS?

Slide 34

Slide 34 text

Does not depend on variables WHAT DOES IT MEAN TO BE STATELESS?

Slide 35

Slide 35 text

Does not depend on variables Same inputs same outputs WHAT DOES IT MEAN TO BE STATELESS?

Slide 36

Slide 36 text

Does not depend on variables Same inputs same outputs No side effects WHAT DOES IT MEAN TO BE STATELESS?

Slide 37

Slide 37 text

iOS IS INHERENTLY STATEFUL

Slide 38

Slide 38 text

Persistence iOS IS INHERENTLY STATEFUL

Slide 39

Slide 39 text

Persistence Networking iOS IS INHERENTLY STATEFUL

Slide 40

Slide 40 text

Persistence Networking Memory iOS IS INHERENTLY STATEFUL

Slide 41

Slide 41 text

Persistence Networking Memory UI iOS IS INHERENTLY STATEFUL

Slide 42

Slide 42 text

Model View Controller update update notify action Source: https://developer.apple.com/library/ios/documentation/General/ Conceptual/DevPedia-CocoaCore/MVC.html

Slide 43

Slide 43 text

Model View Controller update update notify action state

Slide 44

Slide 44 text

Model View Controller update update notify action state state

Slide 45

Slide 45 text

Model View Controller update update notify action state state state

Slide 46

Slide 46 text

HOW CAN WE DEAL WITH THIS?

Slide 47

Slide 47 text

Isolate or eliminate state (vars) HOW CAN WE DEAL WITH THIS?

Slide 48

Slide 48 text

Isolate or eliminate state (vars) Rely on values whenever possible HOW CAN WE DEAL WITH THIS?

Slide 49

Slide 49 text

SWIFT TO THE RESCUE

Slide 50

Slide 50 text

Constants SWIFT TO THE RESCUE

Slide 51

Slide 51 text

Constants Value types SWIFT TO THE RESCUE

Slide 52

Slide 52 text

CONSTANTS let str = "Hello, playground" str = "Goodbye!" // Compiler error!

Slide 53

Slide 53 text

Code that relies on constants is stateless CONSTANTS let str = "Hello, playground" str = "Goodbye!" // Compiler error!

Slide 54

Slide 54 text

VALUE TYPES

Slide 55

Slide 55 text

Int, Float, Double VALUE TYPES

Slide 56

Slide 56 text

Int, Float, Double String VALUE TYPES

Slide 57

Slide 57 text

Int, Float, Double String Array, Dictionary, Set VALUE TYPES

Slide 58

Slide 58 text

Int, Float, Double String Array, Dictionary, Set Struct, Enum VALUE TYPES

Slide 59

Slide 59 text

Int, Float, Double String Array, Dictionary, Set Struct, Enum Code that relies on value types is stateless VALUE TYPES

Slide 60

Slide 60 text

VALUE TYPES class Product { var price: String }

Slide 61

Slide 61 text

VALUE TYPES class Product { var price: String } var credenza = Product(price: "$11.00")

Slide 62

Slide 62 text

VALUE TYPES class Product { var price: String } var credenza = Product(price: "$11.00") var niceCredenza = credenza niceCredenza.price = "$12.00"

Slide 63

Slide 63 text

VALUE TYPES class Product { var price: String } var credenza = Product(price: "$11.00") var niceCredenza = credenza niceCredenza.price = "$12.00" credenza.price // “$12.00”

Slide 64

Slide 64 text

VALUE TYPES struct Product { var price: String } class Product { var price: String }

Slide 65

Slide 65 text

VALUE TYPES struct Product { var price: String } var credenza = Product(price: "$11.00") var niceCredenza = credenza niceCredenza.price = "$12.00" credenza.price // "$11.00"

Slide 66

Slide 66 text

WE NEED A NEW PARADIGM

Slide 67

Slide 67 text

Eliminates and isolates state WE NEED A NEW PARADIGM

Slide 68

Slide 68 text

Eliminates and isolates state Takes advantage of Swift’s constants WE NEED A NEW PARADIGM

Slide 69

Slide 69 text

Eliminates and isolates state Takes advantage of Swift’s constants Takes advantage of Swift’s value types WE NEED A NEW PARADIGM

Slide 70

Slide 70 text

View Store Dispatcher

Slide 71

Slide 71 text

View Store Dispatcher

Slide 72

Slide 72 text

Model Controller View updates manipulates User uses shows Source: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

Slide 73

Slide 73 text

View Store Dispatcher

Slide 74

Slide 74 text

View Store

Slide 75

Slide 75 text

View Store Stores the current state state

Slide 76

Slide 76 text

View Store Stores the current state Produces the next state state

Slide 77

Slide 77 text

View Store Stores the current state Produces the next state Is a class state

Slide 78

Slide 78 text

View Store Dispatcher state

Slide 79

Slide 79 text

View Store state

Slide 80

Slide 80 text

View Store state Is app state

Slide 81

Slide 81 text

View Store state Is app state (everything)

Slide 82

Slide 82 text

View Store state Is app state (everything) Is a moment in time

Slide 83

Slide 83 text

View Store state Is app state (everything) Is a moment in time Is immutable

Slide 84

Slide 84 text

View Store state Is app state (everything) Is a moment in time Is immutable Is a value

Slide 85

Slide 85 text

View Store Dispatcher state

Slide 86

Slide 86 text

View Store Dispatcher state state

Slide 87

Slide 87 text

View

Slide 88

Slide 88 text

View Receives and renders state

Slide 89

Slide 89 text

View Receives and renders state Can’t mutate state

Slide 90

Slide 90 text

View Receives and renders state Can’t mutate state Triggers actions

Slide 91

Slide 91 text

View Receives and renders state Can’t mutate state Triggers actions Is a view controller or view

Slide 92

Slide 92 text

View Receives and renders state Can’t mutate state Triggers actions Is a view controller or view Is a class

Slide 93

Slide 93 text

View Store Dispatcher state state

Slide 94

Slide 94 text

View Store Dispatcher state state action

Slide 95

Slide 95 text

View action

Slide 96

Slide 96 text

View action Represents an interaction

Slide 97

Slide 97 text

View action Represents an interaction Is not state

Slide 98

Slide 98 text

View action Represents an interaction Is not state Includes an action type

Slide 99

Slide 99 text

View action Represents an interaction Is not state Includes an action type Includes relevant data

Slide 100

Slide 100 text

View action Represents an interaction Is not state Includes an action type Includes relevant data Is a value

Slide 101

Slide 101 text

View Store Dispatcher state action state

Slide 102

Slide 102 text

View Dispatcher state action

Slide 103

Slide 103 text

View Dispatcher state action Receives actions

Slide 104

Slide 104 text

View Dispatcher state action Receives actions Forwards to subscribers

Slide 105

Slide 105 text

View Dispatcher state action Receives actions Forwards to subscribers Has multiple subscribers

Slide 106

Slide 106 text

View Dispatcher state action Receives actions Forwards to subscribers Has multiple subscribers Is a class

Slide 107

Slide 107 text

View Store Dispatcher state action state

Slide 108

Slide 108 text

View Store Dispatcher state action action state Store state

Slide 109

Slide 109 text

Store state

Slide 110

Slide 110 text

Store action state

Slide 111

Slide 111 text

Store action + current state state

Slide 112

Slide 112 text

Store current state action + next state state

Slide 113

Slide 113 text

Store state View Store Dispatcher state action action state action

Slide 114

Slide 114 text

Store state View Store Dispatcher state action action state action

Slide 115

Slide 115 text

A SIMPLE EXAMPLE

Slide 116

Slide 116 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { } }

Slide 117

Slide 117 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { if let text = label.text, count = Int(text) { label.text = "\(count + 1)" } } }

Slide 118

Slide 118 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { } }

Slide 119

Slide 119 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! private var count: Int @IBAction func buttonTapped(sender: AnyObject) { count += 1 } }

Slide 120

Slide 120 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! private var count: Int @IBAction func buttonTapped(sender: AnyObject) { count += 1 label.text = "\(count)" } }

Slide 121

Slide 121 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { } }

Slide 122

Slide 122 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { dispatcher.dispatch(.Increment) } }

Slide 123

Slide 123 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { dispatcher.dispatch(.Increment) } } extension CounterViewController: Subscriber { }

Slide 124

Slide 124 text

class CounterViewController: UIViewController { @IBOutlet weak var label: UILabel! @IBAction func buttonTapped(sender: AnyObject) { dispatcher.dispatch(.Increment) } } extension CounterViewController: Subscriber { func receive(state: State) { label.text = "\(state.count)" } }

Slide 125

Slide 125 text

WHAT DID THIS ACCOMPLISH

Slide 126

Slide 126 text

Isolates state to a single component WHAT DID THIS ACCOMPLISH

Slide 127

Slide 127 text

Isolates state to a single component Uses values to communicate state WHAT DID THIS ACCOMPLISH

Slide 128

Slide 128 text

Isolates state to a single component Uses values to communicate state Eliminates state from the UI layer WHAT DID THIS ACCOMPLISH

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

Handle view reuse Support complex, dynamic UIs Automatically animate changes COLLECTION VIEWS

Slide 131

Slide 131 text

Buggy Crashy Frustrating COLLECTION VIEWS

Slide 132

Slide 132 text

Buggy Crashy Frustrating Why? COLLECTION VIEWS

Slide 133

Slide 133 text

COLLECTION VIEWS

Slide 134

Slide 134 text

Contrast between implementations COLLECTION VIEWS

Slide 135

Slide 135 text

Contrast between implementations Trickiness of mutability COLLECTION VIEWS

Slide 136

Slide 136 text

Contrast between implementations Trickiness of mutability Statelessness and testability COLLECTION VIEWS

Slide 137

Slide 137 text

Contrast between implementations Trickiness of mutability Statelessness and testability Statelessness is the future COLLECTION VIEWS

Slide 138

Slide 138 text

Collection View Data Source

Slide 139

Slide 139 text

Collection View Data Source how many sections?

Slide 140

Slide 140 text

3 Collection View sectionCount Data Source how many sections?

Slide 141

Slide 141 text

3 1 Collection View sectionCount itemCounts Data Source how many sections? items in section 0?

Slide 142

Slide 142 text

3 1 item Collection View sectionCount itemCounts visibleItems Data Source how many sections? items in section 0? give me item 0, 0

Slide 143

Slide 143 text

Collection View Data Source View Controller

Slide 144

Slide 144 text

Collection View Model View Controller

Slide 145

Slide 145 text

Collection View Model View Controller item item item item

Slide 146

Slide 146 text

Collection View Model View Controller item delete item item item

Slide 147

Slide 147 text

Collection View Model View Controller item delete item delete item item

Slide 148

Slide 148 text

Collection View Model View Controller item delete item delete item 1 items in section 0? item

Slide 149

Slide 149 text

Collection View Model View Controller item delete item delete item insert item 1 items in section 0? item item

Slide 150

Slide 150 text

Collection View Model View Controller item delete item delete item insert item insert item 2 items in section 0? 1 items in section 0? item item item

Slide 151

Slide 151 text

PITFALLS

Slide 152

Slide 152 text

Business logic in the model PITFALLS

Slide 153

Slide 153 text

Business logic in the model No knowledge of the past PITFALLS

Slide 154

Slide 154 text

Business logic in the model No knowledge of the past Mutable state breeds complexity PITFALLS

Slide 155

Slide 155 text

Collection View Model View Controller

Slide 156

Slide 156 text

Collection View Model View Controller insert item

Slide 157

Slide 157 text

Collection View Model View Controller insert item item item

Slide 158

Slide 158 text

Collection View Model View Controller insert item insert item item item item

Slide 159

Slide 159 text

Collection View Model View Controller insert item insert item 2 items in section 0? item item item

Slide 160

Slide 160 text

Collection View Model View Controller insert item insert item 2 items in section 0? item item item

Slide 161

Slide 161 text

Collection View Model View Controller insert item insert item 2 items in section 0? item item insert item item item

Slide 162

Slide 162 text

Collection View Model View Controller insert item insert item 2 items in section 0? item item delete item delete item 1 items in section 0? insert item

Slide 163

Slide 163 text

Collection View Model View Controller insert item insert item 2 items in section 0? item item delete item delete item 1 items in section 0? insert item

Slide 164

Slide 164 text

reloadItemsAtIndexPaths reloadSections reloadData EXTREME MEASURES

Slide 165

Slide 165 text

No content

Slide 166

Slide 166 text

current state

Slide 167

Slide 167 text

current state next state

Slide 168

Slide 168 text

current state next state current state next state changeset

Slide 169

Slide 169 text

current state next state current state next state changeset Collection View

Slide 170

Slide 170 text

current state next state current state next state changeset next state current state next state changeset nex curre nex cha Collection View Collection View Collecti

Slide 171

Slide 171 text

current state next state changeset

Slide 172

Slide 172 text

current state next state changeset Grapes Nutella Gummi bears Pizza →

Slide 173

Slide 173 text

current state next state changeset Grapes Nutella Gummi bears Pizza Grapes Nutella Doritos Gummi bears Pizza →

Slide 174

Slide 174 text

current state next state changeset Grapes Nutella Gummi bears Pizza Grapes Nutella Doritos Gummi bears Pizza Insert at index 2 →

Slide 175

Slide 175 text

current state next state changeset Grapes Nutella Gummi bears Pizza Grapes Nutella Doritos Gummi bears Pizza Insert at index 2 → Collection view states are lists of items Only needs to be written once Huge win for testability

Slide 176

Slide 176 text

struct Item { let title: String }

Slide 177

Slide 177 text

struct Item { let title: String } extension Item: Equatable {} func ==(lhs: Item, rhs: Item) -> Bool { return lhs.title == rhs.title }

Slide 178

Slide 178 text

struct Item { let title: String } extension Item: Equatable {} func ==(lhs: Item, rhs: Item) -> Bool { return lhs.title == rhs.title } let item1 = Item(title: "Doritos") let item2 = Item(title: "Cheetos") item1 == item2 // false

Slide 179

Slide 179 text

protocol Identifiable { var id: String { get } }

Slide 180

Slide 180 text

protocol Identifiable { var id: String { get } } struct Item: Equatable, Identifiable { let id: String let title: String }

Slide 181

Slide 181 text

protocol Identifiable { var id: String { get } } struct Item: Equatable, Identifiable { let id: String let title: String } let item1 = Item(id: "1", title: "Doritos") let item2 = Item(id: "1", title: "Cheetos") item1.id == item2.id // true item1 == item2 // false

Slide 182

Slide 182 text

let state1 = [Item(id: "A", title: "Gummi bears")] let state2 = [Item(id: "B", title: "Cheetos")] state1.difference(state2) // [.Delete(0), .Insert(0)]

Slide 183

Slide 183 text

let state1 = [Item(id: "A", title: "Gummi bears")] let state2 = [Item(id: "B", title: "Cheetos")] state1.difference(state2) // [.Delete(0), .Insert(0)] extension CollectionType where Generator.Element: Equatable, Generator.Element: Identifiable { }

Slide 184

Slide 184 text

let state1 = [Item(id: "A", title: "Gummi bears")] let state2 = [Item(id: "B", title: "Cheetos")] state1.difference(state2) // [.Delete(0), .Insert(0)] extension CollectionType where Generator.Element: Equatable, Generator.Element: Identifiable { func difference(other: Self) -> [Change] { var changes = [Change]() // TODO return changes } }

Slide 185

Slide 185 text

let state1 = [Item(id: "A", title: "Gummi bears")] let state2 = [Item(id: "B", title: "Cheetos")] state1.difference(state2) // [.Delete(0), .Insert(0)] extension CollectionType where Generator.Element: Equatable, Generator.Element: Identifiable { func difference(other: Self) -> [Change] { var changes = [Change]() // TODO return changes } } enum Change { case Delete(Int) case Insert(Int) case Update(Int) }

Slide 186

Slide 186 text

for (index, item) in other.enumerate() { if !contains({ $0.id == item.id }) { changes.append(.Insert(index)) } } for (index, item) in enumerate() { if !other.contains({ $0.id == item.id }) { changes.append(.Delete(index)) } } for (index, item) in enumerate() { if other.contains({ $0.id == item.id && $0 != item }) { changes.append(.Update(index)) } }

Slide 187

Slide 187 text

for (index, item) in other.enumerate() { if !contains({ $0.id == item.id }) { changes.append(.Insert(index)) } } for (index, item) in enumerate() { if !other.contains({ $0.id == item.id }) { changes.append(.Delete(index)) } } for (index, item) in enumerate() { if other.contains({ $0.id == item.id && $0 != item }) { changes.append(.Update(index)) } }

Slide 188

Slide 188 text

for (index, item) in other.enumerate() { if !contains({ $0.id == item.id }) { changes.append(.Insert(index)) } } for (index, item) in enumerate() { if !other.contains({ $0.id == item.id }) { changes.append(.Delete(index)) } } for (index, item) in enumerate() { if other.contains({ $0.id == item.id && $0 != item }) { changes.append(.Update(index)) } }

Slide 189

Slide 189 text

for (index, item) in other.enumerate() { if !contains({ $0.id == item.id }) { changes.append(.Insert(index)) } } for (index, item) in enumerate() { if !other.contains({ $0.id == item.id }) { changes.append(.Delete(index)) } } for (index, item) in enumerate() { if other.contains({ $0.id == item.id && $0 != item }) { changes.append(.Update(index)) } }

Slide 190

Slide 190 text

Goes against the grain of UIKit Animations Performance Debugging CHALLENGES

Slide 191

Slide 191 text

Encapsulate complete app state Test fuzzing Time travel CURRENT WORK

Slide 192

Slide 192 text

THANKS!

Slide 193

Slide 193 text

http://livefront.com/speaks/stateless/ Adam May @yammada Sam Kirchmeier @skirchmeier THANKS!