SlideShare a Scribd company logo
1 of 273
Download to read offline
Optional
tips and tricks
Anatoly Tukhtarov, iOS Developer at Ciklum Digital
What this talk is not
What this talk is not
• “How to draw an owl?” talk
What this talk is not
• “How to draw an owl?” talk
• The single way to develop software
What this talk is not
• “How to draw an owl?” talk
• The single way to develop software
• “How we improved everything” talk
Agenda
Agenda
• Optional and optional binding
Agenda
• Optional and optional binding
• Error handling
Agenda
• Optional and optional binding
• Error handling
• DI patterns in Swift
Agenda
• Optional and optional binding
• Error handling
• DI patterns in Swift
• One more thing…
What is Optional?
enum Optional<Wrapped> {
/// The absence of a value
case none
/// The presence of a value
case some(Wrapped)
...
}
What is Optional?
• Optional is a container type
What is Optional?
• Optional is a container type
What is Optional?
Int
• Optional is a container type
What is Optional?
Int
• Optional is a container type
What is Optional?
Int
Int?
• Optional is a container type
What is Optional?
Int
Int?
.some
• Optional is a container type
What is Optional?
Int
Int?
.some .none
Optional binding
func generate(from string: String,
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
guard let image = filter.outputImage else { return nil }
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
guard let image = filter.outputImage else { return nil }
let transform = scaleTransform(from:…to:…)
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
guard let image = filter.outputImage else { return nil }
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: image.transformed(by: transform))
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
guard let image = filter.outputImage else { return nil }
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: image.transformed(by: transform))
}
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
guard let image = filter.outputImage else { return nil }
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: image.transformed(by: transform))
}
Optional binding
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
guard let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
guard let image = filter.outputImage else { return nil }
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: image.transformed(by: transform))
}
Optional binding
Optional binding
Filter?
Optional binding
Filter?
Optional binding
.none
Filter?
Optional binding
CIF*
.some .none
Filter?
*CIFilter
CIF
.some .none
Filter?
Optional binding
CIF
.some .none
Filter?
Optional binding
CIF
CIF
.some .none
Filter?
.setValue
Optional binding
CIF
CIF
.some .none
Filter?
.setValue
Optional binding
CIF
CIF
.some .none
Filter?
.setValue
Optional binding
CIF
CIF
CIF
.some .none
Filter?
.setValue
Optional binding
.outputImage
CIF
CIF
CIF
.some .none
Filter?
.setValue
Optional binding
.outputImage
CIF
CIF
.some
.none
CII
UIImage(ciImage: )
CIF
.some .none
Filter?
.setValue
Optional binding
.outputImage
CIF
CIF
.some
.none
CII
These are not bindings,
these are 

conditional statements
Deep dive
Deep dive
• Initializers
Deep dive
• Initializers
• map(_:)
Deep dive
• Initializers
• map(_:)
• flatMap(_:)
func map<U>(_ transform:
(Wrapped) throws -> U)
rethrows -> U?
map(_:)
func flatMap<U>(_ transform:
(Wrapped) throws -> U?)
rethrows -> U?
flatMap(_:)
Functional Optional
func generate(from string: String,
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return $0.outputImage
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return $0.outputImage
}.map {
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return $0.outputImage
}.map {
let transform = scaleTransform(from:…to:…)
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return $0.outputImage
}.map {
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: $0.transformed(by: transform))
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return $0.outputImage
}.map {
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: $0.transformed(by: transform))
}
Functional Optional
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator").flatMap {
$0.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return $0.outputImage
}.map {
let transform = scaleTransform(from:…to:…)
return UIImage(ciImage: $0.transformed(by: transform))
}
}
Functional Optional
Functional Optional
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator")
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator")
.flatMap { codeImage(from: string, filter: $0) }
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator")
.flatMap { codeImage(from: string, filter: $0) }
.map { image(form: $0, scaledTo: requestedSize) }
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator")
.flatMap { codeImage(from: string, filter: $0) }
.map { image(form: $0, scaledTo: requestedSize) }
}
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator")
.flatMap { codeImage(from: string, filter: $0) }
.map { image(form: $0, scaledTo: requestedSize) }
}
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
Functional Optional
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage {
let transform = scaleTransform(from: image.extent.size, to: size)
return UIImage(ciImage: image.transformed(by: transform))
}
func generate(from string: String, size
requestedSize: CGSize) -> UIImage? {
return CIFilter(name: "CIQRCodeGenerator")
.flatMap { codeImage(from: string, filter: $0) }
.map { image(form: $0, scaledTo: requestedSize) }
}
func codeImage(from string: String,
filter: CIFilter) -> CIImage? {
filter.setValue(string.data(using: .ascii),
forKey: "inputMessage")
return filter.outputImage
}
codeImage
image(. , size:)codeImage
map
CIImg
.some
image( , size:)codeImage
map
CIImg
.some
image( , size:)codeImage
map
.none
CIImg
.some
CIFilter()
CIFilter()
flatMap
codeImage( )CIF
.some
CIFilter()
flatMap
codeImage( )CIF
.some
.none
What is Optional?
What is Optional?
• Optional is container for a reason: it preserves optional
context
What is Optional?
• Optional is container for a reason: it preserves optional
context
• Optional ensures that null values aren’t passed as
arguments to functions that can’t handle them
What is Optional?
• Optional is container for a reason: it preserves optional
context
• Optional ensures that null values aren’t passed as
arguments to functions that can’t handle them
• Optional interface provides a handful mechanism for
applying functions to such values in a pipeline
What is Optional?
• Optional is container for a reason: it preserves optional
context
• Optional ensures that null values aren’t passed as
arguments to functions that can’t handle them
• Optional interface provides a handful mechanism for
applying functions to such values in a pipeline
• Optional binding applies a function to a value stored in an
Optional, not binds the value to a variable
Optional context
Optional context
• The absence of a value is OK, not an error
Optional context
• The absence of a value is OK, not an error
• One doesn’t care why Optional computation’s value is
absent
Optional context
• The absence of a value is OK, not an error
• One doesn’t care why Optional computation’s value is
absent
• Optional isn’t designed for error handling
Speaking about

error handling
Error handling
Error handling
• It’s better to handle errors or they will handle you
Error handling
• It’s better to handle errors or they will handle you
• try-catch is the default technique and can be
improved
Error handling
• It’s better to handle errors or they will handle you
• try-catch is the default technique and can be
improved
• Please, don’t use Optional for error handling
Why?
Why?
Optional doesn’t explain why
computation fails or interrupts
Error handling
Error handling
protocol Storage {
associatedtype Value
func fetch() -> Value?
}
Error handling
protocol Storage {
associatedtype Value
func fetch() -> Value?
}
Error handling
Error handling
protocol Storage {
associatedtype Value
func fetch() throws -> Value
}
Error handling
Error handling
struct PersistentStorage<T>: Storage {
typealias Value = T
func fetch() throws -> T { ... }
}
Error handling
struct PersistentStorage<T>: Storage {
typealias Value = T
func fetch() throws -> T { ... }
}
enum PersistentStorageError: Error {
case empty
case corruptedData
case file(error: NSError)
}
Error handling
Error handling
protocol ErrorHandling {
associatedtype ErrorType: Error
func handle(error: ErrorType)
}
Error handling
protocol ErrorHandling {
associatedtype ErrorType: Error
func handle(error: ErrorType)
}
struct PersistentStorageErrorHandler: ErrorHandling {
typealias ErrorType = PersistentStorageError
func handle(error: PersistentStorageError) { ... }
}
Error handling
do {
Error handling
do {
let cities = try citiesStorage.fetch()
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
errorHandler.handle(.corruptedData)
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
errorHandler.handle(.corruptedData)
} catch is PersistentStorageError.file(let fileError) {
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
errorHandler.handle(.corruptedData)
} catch is PersistentStorageError.file(let fileError) {
errorHandler.handle(.file(error: fileError))
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
errorHandler.handle(.corruptedData)
} catch is PersistentStorageError.file(let fileError) {
errorHandler.handle(.file(error: fileError))
}
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
errorHandler.handle(.corruptedData)
} catch is PersistentStorageError.file(let fileError) {
errorHandler.handle(.file(error: fileError))
}
Error handling
do {
let cities = try citiesStorage.fetch()
} catch is PersistentStorageError.empty {
errorHandler.handle(.empty)
} catch is PersistentStorageError.corruptedData {
errorHandler.handle(.corruptedData)
} catch is PersistentStorageError.file(let fileError) {
errorHandler.handle(.file(error: fileError))
}
Error handling
DRY?
Error handling
Error handling
do {
let cities = try citiesStorage.fetch()
} catch {
(error as? PersistentStorageError)
.map(errorHandler.handle)
}
Error handling
do {
let cities = try citiesStorage.fetch()
} catch {
(error as? PersistentStorageError)
.map(errorHandler.handle)
}
Error handling
do {
let cities = try citiesStorage.fetch()
} catch {
(error as? PersistentStorageError)
.map(errorHandler.handle)
}
TYPE
CAST?
What if we had
Optional
with .error case
instead of .none?
Result<Value, Error>
Result<Value, Error>
*standard type since Swift 5
What is Result?
enum Result<Success, Error> {
/// A success, storing `Success`
case success(Success)
/// A failure, storing `Error`
case failure(Error)
...
}
apple/swift-evolution
Error handling
Error handling
protocol Storage {
associatedtype Value
associatedtype Error
func fetch() -> Result<Value, Error>
}
Error handling
Error handling
let result = citiesStorage.fetch()
Error handling
result.mapError(errorHandler.handle)
let result = citiesStorage.fetch()
Error handling (async)
Error handling (async)
• Reactive/promise primitives are preferable over closures*
Error handling (async)
• Reactive/promise primitives are preferable over closures*
* beware the memory management
Error handling (async)
• Reactive/promise primitives are preferable over closures*
• Allow you to chain multiple async operations in a pipeline
* beware the memory management
Error handling (async)
• Reactive/promise primitives are preferable over closures*
• Allow you to chain multiple async operations in a pipeline
• ReactiveSwift has typed errors in primitives
* beware the memory management
Why not to throw?
Why not to throw?
• Forces boilerplate code
Why not to throw?
• Forces boilerplate code
• Opposes DRY
Why not to throw?
• Forces boilerplate code
• Opposes DRY
• Forces type casting
And	now	for	something	
completely	different
Dependency injection
Dependency injection
• Introduces modularity
Dependency injection
• Introduces modularity
• Allows components to stay loosely-coupled
Dependency injection
• Introduces modularity
• Allows components to stay loosely-coupled
• There’re several injection patterns
Injection patterns
Injection patterns
• Initializer injection
Injection patterns
• Initializer injection
• Property injection
Injection patterns
• Initializer injection
• Property injection
• Method injection
Initializer injection
Initializer injection
protocol Animal { ... }
struct PetOwner {
let pet: Animal
init(pet: Animal) {
self.pet = pet
}
...
}
Initializer injection
protocol Animal { ... }
struct PetOwner {
let pet: Animal
init(pet: Animal) {
self.pet = pet
}
...
}
Initializer injection
Initializer injection
• In Swift a type must be completely initialized before
being used
Initializer injection
• In Swift a type must be completely initialized before
being used
• Can’t be used with UIKit objects initialized from a xib or
a storyboard…
Initializer injection
• In Swift a type must be completely initialized before
being used
• Can’t be used with UIKit objects initialized from a xib or
a storyboard…
• Avoid injections in UIViewController
Initializer injection
• In Swift a type must be completely initialized before
being used
• Can’t be used with UIKit objects initialized from a xib or
a storyboard…
• Avoid injections in UIViewController
• UIViewController is a view, not a controller*
*Swift Heroes 2018. 

Krzysztof Zablocki — Maintainable iOS Architectures
Initializer injection
Initializer injection
• Some types might have a lot of dependencies
Initializer injection
• Some types might have a lot of dependencies
• Try to use protocol composition in order to avoid large
initializers*
*Krzysztof Zablocki — Using protocol compositon 

for dependency injection
Property injection
Property injection
protocol Animal { ... }
struct Cat: Animal { ... }
struct PetOwner {
var pet: Animal?
...
}
var person = PetOwner()
person.pet = Cat()
Property injection
protocol Animal { ... }
struct Cat: Animal { ... }
struct PetOwner {
var pet: Animal?
...
}
var person = PetOwner()
person.pet = Cat()
Property injection
Property injection
• Introduces Optional in a client
Property injection
• Introduces Optional in a client
• Forces optional binding all over the client
Property injection
• Introduces Optional in a client
• Forces optional binding all over the client
• Avoid implicitly unwrapped Optional if you forced to use
property injection
Property injection
• Introduces Optional in a client
• Forces optional binding all over the client
• Avoid implicitly unwrapped Optional if you forced to use
property injection
• Implicitly unwrapped Optional is a code smell in pure
Swift
Method injection
Method injection
protocol Animal { ... }
protocol AnimalSanctuary {
associatedtype Pet: Animal
func adopt(pet: Pet)
...
}
Method injection
protocol Animal { ... }
protocol AnimalSanctuary {
associatedtype Pet: Animal
func adopt(pet: Pet)
...
}
Method injection
protocol Animal { ... }
protocol AnimalSanctuary {
associatedtype Pet: Animal
func adopt(pet: Pet)
...
}
Method injection
Method injection
• Don’t assign injected dependency as a client’s property
Method injection
• Don’t assign injected dependency as a client’s property
• Suitable for simple computations over injected type
Injection patterns
Injection patterns
• Initializer injection is a default pattern in Swift
Injection patterns
• Initializer injection is a default pattern in Swift
• Try to avoid property injection
Injection patterns
• Initializer injection is a default pattern in Swift
• Try to avoid property injection
• If a project has both Swift and Objective-C code try to
annotate Nullability in Objective-C types
One more thing…
Optional is 

a Monad
Monad
Monad
• Is a design pattern (data type in pure functional
languages)
Monad
• Is a design pattern (data type in pure functional
languages)
• Is extremely abstract and generic
Monad
• Is a design pattern (data type in pure functional
languages)
• Is extremely abstract and generic
• Defined by mathematical laws
Monad
• Is a design pattern (data type in pure functional
languages)
• Is extremely abstract and generic
• Defined by mathematical laws
• Due to high abstraction level it’s pretty complex to explain
what a monad is
Monad as a container
Monad as a container
• Encapsulates a data type
Monad as a container
• Encapsulates a data type
• Preserves specific context of computation
Monad as a container
• Encapsulates a data type
• Preserves specific context of computation
• Knows how to combine (bind) same containers together
Perform impure, sequenced
computations in pure
composite way
Why monad?
How to use monads?
How to use monads?
• Functions! Lots of them
How to use monads?
• Functions! Lots of them
• Clear, robust API
How to use monads?
• Functions! Lots of them
• Clear, robust API
• Rich generic support
How to use monads?
• Functions! Lots of them
• Clear, robust API
• Rich generic support
Monad API
Monad API
• Clear
Monad API
• Clear
• Composite
Monad API
• Clear
• Composite
• Easy to read
Monad API
• Clear
• Composite
• Easy to read
• Generic
Monad API
• Clear
• Composite
• Easy to read
• Generic
• Swift doesn’t have one by default
Monad API
• Clear
• Composite
• Easy to read
• Generic
• Swift doesn’t have one by default
• Let’s implement our own!
Optional eDSL
Optional eDSL
• Protocols with associated types aren’t polymorphic
Optional eDSL
• Protocols with associated types aren’t polymorphic
• Custom operators
Optional eDSL
• Protocols with associated types aren’t polymorphic
• Custom operators
• Currying and partial application
Optional eDSL
• Protocols with associated types aren’t polymorphic
• Custom operators
• Currying and partial application
• Function composition
apply operator
precedencegroup ApplicativePrecedence {
associativity: left
higherThan: NilCoalescingPrecedence
}
infix operator <*>: ApplicativePrecedence
func <*><A, B>(_ transform: ((A) -> B)?,
arg: A?) -> B? {
return transform.flatMap { arg.map($0) }
}
apply operator
precedencegroup ApplicativePrecedence {
associativity: left
higherThan: NilCoalescingPrecedence
}
infix operator <*>: ApplicativePrecedence
func <*><A, B>(_ transform: ((A) -> B)?,
arg: A?) -> B? {
return transform.flatMap { arg.map($0) }
}
precedencegroup BindingPrecedence {
associativity: left
higherThan: ApplicativePrecedence
}
infix operator |>>: BindingPrecedence
func |>><A, B>(_ transform: ((A) -> B?)?,
arg: A?) -> B? {
return transform
.flatMap { arg.flatMap($0) }
}
bind operator
precedencegroup BindingPrecedence {
associativity: left
higherThan: ApplicativePrecedence
}
infix operator |>>: BindingPrecedence
func |>><A, B>(_ transform: ((A) -> B?)?,
arg: A?) -> B? {
return transform
.flatMap { arg.flatMap($0) }
}
bind operator
But how can one pass 

a function with multiple
arguments to operators?
Currying
Currying
Translates evaluation of a function
with multiple arguments into
evaluation a sequence of multiple
functions with a single argument
Currying
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage
{
...
}
Currying
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage
{
...
}
let img = image(form: ciImage,
scaledTo: requestedSize)
Currying
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage
{
...
}
Currying
func image(form image: CIImage,
scaledTo size: CGSize) -> UIImage
{
...
}
Currying
func image(CIImage, CGSize) -> UIImage {
...
}
Currying
func image(CIImage, CGSize) -> UIImage {
...
}
Currying
(CIImage, CGSize) -> UIImage
(A, B) -> C
Currying
(A) -> (B) -> C
Currying
curry
func curry<A, B, C>(_ function:
@escaping (A, B) -> C) -> (A) -> (B) -> C {
return { argA in { argB in
function(argA, argB) }
}
}
Currying
let img = image(form: ciImage,
scaledTo: requestedSize)
Currying
let img = curry(image)(ciImage)(requestedSize)
Currying
let img = curry(image)(ciImage)(requestedSize)
At this moment you’ve gotta
ask yourself a question:
What’s img type?
Go ahead, make my day
Go ahead, make my day
(CGSize) -> UIImage
Partial application
Partial application
* the statement is only true for functional languages like Haskell.
Partial application is a bit complex :)
Partial application
Supplying fewer than the total
number of arguments in a curried
function is referred to as partial
application*
* the statement is only true for functional languages like Haskell.
Partial application is a bit complex :)
Optional functions
• Performing partial application of a function using our
optional operators returns an optional function
Optional functions
• Performing partial application of a function using our
optional operators returns an optional function
ciImage <*> curry(image)
Optional functions
And the type is…
((CGSize) -> UIImage)?
Pure functions
Pure functions
• static only (memory management)
Pure functions
• static only (memory management)
• Use structures (immutability)
func generate(from string: String,
size requestedSize: CGSize) -> UIImage? {
return curry(image)
<*> curry(codeImage)
|>> CIFilter(name: "CIQRCodeGenerator")
|>> string
<*> requestedSize
}
Functional Optional
infix operator <^>: ApplicativePrecedence
func <^><A, B>(_ transform: (A) -> B,
arg: A?) -> B? {
return arg.map(transform)
}
map operator
let a: Int? = 5
let b: Int? = nil
let sum = curry(+) <^> a <*> b
let product = curry(*) <^> a <*> b
Functional Optional
Code sample
swordfishyou/mSwift
More monads?
More monads?
• Array (nondetermenism)
More monads?
• Array (nondetermenism)
• Result (error handling)
More monads?
• Array (nondetermenism)
• Result (error handling)
• Some Reactive primitives (concurrency)
But wait…
But wait…
• Functional style allows to stay DRY
But wait…
• Functional style allows to stay DRY
• Swift’s type system doesn’t allow generic monads
But wait…
• Functional style allows to stay DRY
• Swift’s type system doesn’t allow generic monads
• Each monad needs its own implementation of eDSL
But wait…
• Functional style allows to stay DRY
• Swift’s type system doesn’t allow generic monads
• Each monad needs its own implementation of eDSL
• …
But wait…
• Functional style allows to stay DRY
• Swift’s type system doesn’t allow generic monads
• Each monad needs its own implementation of eDSL
• …
• PROFIT!!11
But why?
But why?
• Introduces new powerful tools
But why?
• Introduces new powerful tools
• Encourages modularity and composition
But why?
• Introduces new powerful tools
• Encourages modularity and composition
• Encourages careful treatment of types and mutable
states
But why?
• Introduces new powerful tools
• Encourages modularity and composition
• Encourages careful treatment of types and mutable
states
• Fun!
Next steps
Next steps
• Functional Swift
Next steps
• Functional Swift
• Learn You a Haskell for a Greater Good
Next steps
• Functional Swift
• Learn You a Haskell for a Greater Good
• Haskell Wiki
Deep cuts
Deep cuts
• Jeremy Gibbons, Oege de Moor; The Fun of
Programming (ISBN: 978-0333992852)
Deep cuts
• Jeremy Gibbons, Oege de Moor; The Fun of
Programming (ISBN: 978-0333992852)
• Harold Abelson, Gerald Jay Sussman, Julie Sussman;
Structure and Interpretation of Computer Programs
(ISBN: 978-0262510875)
Thank you!
/tukhtarov anvitu@gmail.com @anatolytukhtarov

More Related Content

What's hot

Lego: A brick system build by scala
Lego: A brick system build by scalaLego: A brick system build by scala
Lego: A brick system build by scala
lunfu zhong
 

What's hot (11)

Kotlin Mullets
Kotlin MulletsKotlin Mullets
Kotlin Mullets
 
Deriving Scalaz
Deriving ScalazDeriving Scalaz
Deriving Scalaz
 
F sh3tdkeq
F sh3tdkeqF sh3tdkeq
F sh3tdkeq
 
F[1]
F[1]F[1]
F[1]
 
Lego: A brick system build by scala
Lego: A brick system build by scalaLego: A brick system build by scala
Lego: A brick system build by scala
 
Exploring Koltin on Android
Exploring Koltin on AndroidExploring Koltin on Android
Exploring Koltin on Android
 
미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정미려한 UI/UX를 위한 여정
미려한 UI/UX를 위한 여정
 
F[5]
F[5]F[5]
F[5]
 
Kotlin: A pragmatic language by JetBrains
Kotlin: A pragmatic language by JetBrainsKotlin: A pragmatic language by JetBrains
Kotlin: A pragmatic language by JetBrains
 
스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내
 
F(3)
F(3)F(3)
F(3)
 

Similar to Optional. Tips and Tricks - UA Mobile 2019

ADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developersADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developers
Bartosz Kosarzycki
 
Quest 1 define a class batsman with the following specifications
Quest  1 define a class batsman with the following specificationsQuest  1 define a class batsman with the following specifications
Quest 1 define a class batsman with the following specifications
rajkumari873
 

Similar to Optional. Tips and Tricks - UA Mobile 2019 (20)

Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
Core Image: The Most Fun API You're Not Using, CocoaConf Atlanta, December 2014
 
Cocoaheads Meetup / Alex Zimin / Swift magic
Cocoaheads Meetup / Alex Zimin / Swift magicCocoaheads Meetup / Alex Zimin / Swift magic
Cocoaheads Meetup / Alex Zimin / Swift magic
 
Александр Зимин (Alexander Zimin) — Магия Swift
Александр Зимин (Alexander Zimin) — Магия SwiftАлександр Зимин (Alexander Zimin) — Магия Swift
Александр Зимин (Alexander Zimin) — Магия Swift
 
Clean Code Development
Clean Code DevelopmentClean Code Development
Clean Code Development
 
Protocol-Oriented Programming in Swift
Protocol-Oriented Programming in SwiftProtocol-Oriented Programming in Swift
Protocol-Oriented Programming in Swift
 
Yahoo Open Source - The Tour & Mystery of AppDevKit (MOPCON 2016)
Yahoo Open Source - The Tour & Mystery of AppDevKit (MOPCON 2016)Yahoo Open Source - The Tour & Mystery of AppDevKit (MOPCON 2016)
Yahoo Open Source - The Tour & Mystery of AppDevKit (MOPCON 2016)
 
MOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app developmentMOPCON 2014 - Best software architecture in app development
MOPCON 2014 - Best software architecture in app development
 
How do you create a programming language for the JVM?
How do you create a programming language for the JVM?How do you create a programming language for the JVM?
How do you create a programming language for the JVM?
 
Minimizing Decision Fatigue to Improve Team Productivity
Minimizing Decision Fatigue to Improve Team ProductivityMinimizing Decision Fatigue to Improve Team Productivity
Minimizing Decision Fatigue to Improve Team Productivity
 
Adopting F# at SBTech
Adopting F# at SBTechAdopting F# at SBTech
Adopting F# at SBTech
 
ADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developersADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developers
 
Griffon @ Svwjug
Griffon @ SvwjugGriffon @ Svwjug
Griffon @ Svwjug
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Practical tips for building apps with kotlin
Practical tips for building apps with kotlinPractical tips for building apps with kotlin
Practical tips for building apps with kotlin
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05c
 
2016 gunma.web games-and-asm.js
2016 gunma.web games-and-asm.js2016 gunma.web games-and-asm.js
2016 gunma.web games-and-asm.js
 
Exploiting vectorization with ISPC
Exploiting vectorization with ISPCExploiting vectorization with ISPC
Exploiting vectorization with ISPC
 
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is FineCocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
CocoaConf Chicago 2017: Media Frameworks and Swift: This Is Fine
 
Quest 1 define a class batsman with the following specifications
Quest  1 define a class batsman with the following specificationsQuest  1 define a class batsman with the following specifications
Quest 1 define a class batsman with the following specifications
 
TechDays 2016 - Developing websites using asp.net core mvc6 and entity framew...
TechDays 2016 - Developing websites using asp.net core mvc6 and entity framew...TechDays 2016 - Developing websites using asp.net core mvc6 and entity framew...
TechDays 2016 - Developing websites using asp.net core mvc6 and entity framew...
 

More from UA Mobile

Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
UA Mobile
 
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
UA Mobile
 
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
UA Mobile
 
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
UA Mobile
 
До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019
UA Mobile
 
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
UA Mobile
 
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
UA Mobile
 

More from UA Mobile (20)

Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...
 
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
Декларативное программирование клиент-серверных приложений на андроид - UA Mo...
 
Leave your Room behind - UA Mobile 2019
Leave your Room behind - UA Mobile 2019Leave your Room behind - UA Mobile 2019
Leave your Room behind - UA Mobile 2019
 
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
OpenId and OAuth2: Rear, Medium, Well Done - UA Mobile 2019
 
Google Wear OS watch faces and applications development - UA Mobile 2019
Google Wear OS watch faces and applications development - UA Mobile 2019Google Wear OS watch faces and applications development - UA Mobile 2019
Google Wear OS watch faces and applications development - UA Mobile 2019
 
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019Історія декількох проектів та що в них пішло не так - UA Mobile 2019
Історія декількох проектів та що в них пішло не так - UA Mobile 2019
 
Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019
 
Managing State in Reactive applications - UA Mobile 2019
Managing State in Reactive applications - UA Mobile 2019Managing State in Reactive applications - UA Mobile 2019
Managing State in Reactive applications - UA Mobile 2019
 
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
Ідіоматична ін'єкція залежностей на Kotlin без фреймворків - UA Mobile2019
 
Актуальні практики дизайну мобільних додатків - UA Mobile 2019
Актуальні практики дизайну мобільних додатків - UA Mobile 2019Актуальні практики дизайну мобільних додатків - UA Mobile 2019
Актуальні практики дизайну мобільних додатків - UA Mobile 2019
 
До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019До чого прикладати Docker в Android? - UA Mobile 2019
До чого прикладати Docker в Android? - UA Mobile 2019
 
Building your Flutter apps using Redux - UA Mobile 2019
Building your Flutter apps using Redux - UA Mobile 2019Building your Flutter apps using Redux - UA Mobile 2019
Building your Flutter apps using Redux - UA Mobile 2019
 
Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...Designing iOS+Android project without using multiplatform frameworks - UA Mob...
Designing iOS+Android project without using multiplatform frameworks - UA Mob...
 
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
Бібліотеки та Інструменти на сторожі коду - UA Mobile 2019
 
Flutter: No more boring apps! - UA Mobile 2019
Flutter: No more boring apps! - UA Mobile 2019Flutter: No more boring apps! - UA Mobile 2019
Flutter: No more boring apps! - UA Mobile 2019
 
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
Долаючи прірву між дизайнерами та розробниками - UA Mobile 2019
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
 
Sceneform SDK на практиці - UA Mobile 2019
Sceneform SDK на практиці - UA Mobile 2019Sceneform SDK на практиці - UA Mobile 2019
Sceneform SDK на практиці - UA Mobile 2019
 
Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.
 
Augmented reality on Android. UA Mobile 2017.
Augmented reality on Android. UA Mobile 2017.Augmented reality on Android. UA Mobile 2017.
Augmented reality on Android. UA Mobile 2017.
 

Optional. Tips and Tricks - UA Mobile 2019