SlideShare una empresa de Scribd logo
1 de 48
Descargar para leer sin conexión
Writing your App
Swiftly
Sommer Panage
Chorus Fitness
@sommer
Patterns!
Today, in 4 short tales
• Schrödinger's Result
• The Little Layout Engine that Could
• Swiftilocks and the Three View States
• Pete and the Repeated Code
The Demo App
Schrödinger's Result
Code in a box
func getFilms(completion: @escaping ([Film]?, APIError?) -> Void) {
let url = SWAPI.baseURL.appendingPathComponent(Endpoint.films.rawValue)
let task = self.session.dataTask(with: url) { (data, response, error) in
if let data = data {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
if let films = SWAPI.decodeFilms(jsonObject: jsonObject) {
completion(films, nil)
} else {
completion(nil, .decoding)
}
} catch {
completion(nil, .server(originalError: error))
}
} else {
completion(nil, .server(originalError: error!))
}
}
task.resume()
}
What we think is happening…
What's actually
happening…
override func viewDidLoad() {
super.viewDidLoad()
apiClient.getFilms() { films, error in
if let films = films {
// Show film UI
if let error = error {
// Log warning...this is weird
}
} else if let error = error {
// Show error UI
} else {
// No results at all? Show error UI I guess?
}
}
}
Result open source framework by Rob Rix
Model our server interaction as it actually is - success / failure!
public enum Result<T, Error: Swift.Error>: ResultProtocol {
case success(T)
case failure(Error)
}
New, improved code
func getFilms(completion: @escaping (Result<[Film], APIError>) -> Void) {
let task = self.session
.dataTask(with: SWAPI.baseURL.appendingPathComponent(Endpoint.films.rawValue)) { (data, response, error) in
let result = Result(data, failWith: APIError.server(originalError: error!))
.flatMap { data in
Result<Any, AnyError>(attempt: { try JSONSerialization.jsonObject(with: data, options: []) })
.mapError { _ in APIError.decoding }
}
.flatMap { Result(SWAPI.decodeFilms(jsonObject: $0), failWith: .decoding) }
completion(result)
}
task.resume()
}
New, improved code
override func viewDidLoad() {
super.viewDidLoad()
apiClient.getFilms() { result in
switch result {
case .success(let films): print(films) // Show my UI!
case .failure(let error): print(error) // Show some error UI
}
}
}
The Moral of the Story
Using the Result enum allowed us to
• Model the sucess/failure of our server interaction more
correctly
• Thus simplify our view controller code.
The Little Layout Engine that
Could
Old-school
override func layoutSubviews() {
super.layoutSubviews()
// WHY AM I DOING THIS?!?!
}
What about Storyboards and Xibs?
• Working in teams becomes harder because...
• XML diffs
• Merge conflicts?!
• No constants
• Stringly typed identifiers
• Fragile connections
Autolayout: iOS 9+ APIs
init() {
super.init(frame: .zero)
addSubview(tableView)
// Autolayout: Table same size as parent
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
}
Autolayout: Cartography by Robb Böhnke
init() {
super.init(frame: .zero)
addSubview(tableView)
// Autolayout: Table same size as parent
constrain(tableView, self) { table, parent in
table.edges == parent.edges
}
}
More Cartography
private let margin: CGFloat = 16
private let episodeLeftPadding: CGFloat = 8
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(episodeLabel)
contentView.addSubview(titleLabel)
constrain(episodeLabel, titleLabel, contentView) { episode, title, parent in
episode.leading == parent.leading + margin
episode.top == parent.top + margin
episode.bottom == parent.bottom - margin
title.leading == episode.trailing + episodeLeftPadding
title.trailing <= parent.trailing - margin
title.centerY == episode.centerY
}
}
The Moral of the Story
Using the Cartography framework harnesses Swift's
operator overloads to make programatic AutoLayout a
breeze!
Swiftilocks and the
Three View States
Swiftilocks and the Three View
States
LOADING
Swiftilocks and the Three View
States
SUCCESS
Swiftilocks and the Three View
States
ERROR
State management with bools
/// MainView.swift
var isLoading: Bool = false {
didSet {
errorView.isHidden = true
loadingView.isHidden = !isLoading
}
}
var isError: Bool = false {
didSet {
errorView.isHidden = !isError
loadingView.isHidden = true
}
}
var items: [MovieItem]? {
didSet {
tableView.reloadData()
}
}
/// MainViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
title = "Star Wars Films"
mainView.isLoading = true
apiClient.getFilms() { result in
DispatchQueue.main.async {
switch result {
case .success(let films):
self.mainView.items = films
.map { MovieItem(episodeID: $0.episodeID, title: $0.title) }
.sorted { $0.0.episodeID < $0.1.episodeID }
self.mainView.isLoading = false
self.mainView.isError = false
case .failure(let error):
self.mainView.isLoading = false
self.mainView.isError = true
}
}
}
}
Too many states!!
Data presence + state?!
Enums to the rescue!
final class MainView: UIView {
enum State {
case loading
case loaded(items: [MovieItem])
case error(message: String)
}
init(state: State) { ... }
// the rest of my class...
}
var state: State {
didSet {
switch state {
case .loading:
items = nil
loadingView.isHidden = false
errorView.isHidden = true
case .error(let message):
print(message)
items = nil
loadingView.isHidden = true
errorView.isHidden = false
case .loaded(let movieItems):
loadingView.isHidden = true
errorView.isHidden = true
items = movieItems
tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
title = "Star Wars Films"
mainView.state = .loading
apiClient.getFilms() { result in
DispatchQueue.main.async {
switch result {
case .success(let films):
let items = films
.map { MovieItem(episodeID: $0.episodeID, title: $0.title) }
.sorted { $0.0.episodeID < $0.1.episodeID }
self.mainView.state = .loaded(items: items)
case .failure(let error):
self.mainView.state = .error(message: "Error: (error.localizedDescription)")
}
}
}
}
The Moral of the Story
Modelling our view state with an enum with
associated values allows us to:
1. Simplify our VC
2. Avoid ambiguous state
3. Centralize our logic
It's better...but...
Pete and the Repeated Code.
Repeated code
var state: State {
didSet {
switch state {
case .loading:
text = nil
loadingView.isHidden = false
errorView.isHidden = true
case .error(let message):
print(message)
text = nil
loadingView.isHidden = true
errorView.isHidden = false
case .loaded(let text):
loadingView.isHidden = true
errorView.isHidden = true
text = text
tableView.reloadData()
}
}
}
Protocols save the day!!
• A shared interface of methods and properties
• Addresses a particular task
• Types adopting protocol need not be related
protocol DataLoading {
associatedtype Data
var state: ViewState<Data> { get set }
var loadingView: LoadingView { get }
var errorView: ErrorView { get }
func update()
}
enum ViewState<Content> {
case loading
case loaded(data: Content)
case error(message: String)
}
Default protocol implementation
extension DataLoading where Self: UIView {
func update() {
switch state {
case .loading:
loadingView.isHidden = false
errorView.isHidden = true
case .error(let error):
loadingView.isHidden = true
errorView.isHidden = false
Log.error(error)
case .loaded:
loadingView.isHidden = true
errorView.isHidden = true
}
}
}
Conforming to DataLoading
1. Provide an errorView variable
2. Provide an loadingView variable
3. Provide a state variable that take some sort of Data
4. Call update() whenever needed
DataLoading in our Main View
final class MainView: UIView, DataLoading {
let loadingView = LoadingView()
let errorView = ErrorView()
var state: ViewState<[MovieItem]> {
didSet {
update() // call update whenever we set our list of Movies
tableView.reloadData()
}
}
DataLoading in our Crawl View
class CrawlView: UIView, DataLoading {
let loadingView = LoadingView()
let errorView = ErrorView()
var state: ViewState<String> {
didSet {
update()
crawlLabel.text = state.data
}
}
The Moral of the Story
Decomposing functionality that is shared by non-
related objects into a protocol helps us
• Avoid duplicated code
• Consolidate our logic into one place
Conclusion
• Result: easily differentiate our success/error pathways
• Cartography: use operator overloading to make code more
readable
• ViewState enum: never have an ambigous view state!
• Protocols: define/decompose shared behaviors in unrelated
types
THANK YOU
Contact Me:
@sommer on Twitter
me@sommerpanage.com

Más contenido relacionado

La actualidad más candente

Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Thomas Fuchs
 

La actualidad más candente (20)

Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6Workshop 10: ECMAScript 6
Workshop 10: ECMAScript 6
 
The jQuery Divide
The jQuery DivideThe jQuery Divide
The jQuery Divide
 
Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 
Your JavaScript Library
Your JavaScript LibraryYour JavaScript Library
Your JavaScript Library
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
JavaScript and the AST
JavaScript and the ASTJavaScript and the AST
JavaScript and the AST
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내스위프트를 여행하는 히치하이커를 위한 스타일 안내
스위프트를 여행하는 히치하이커를 위한 스타일 안내
 
Rust ⇋ JavaScript
Rust ⇋ JavaScriptRust ⇋ JavaScript
Rust ⇋ JavaScript
 
JavaScript Promise
JavaScript PromiseJavaScript Promise
JavaScript Promise
 
A New Baseline for Front-End Devs
A New Baseline for Front-End DevsA New Baseline for Front-End Devs
A New Baseline for Front-End Devs
 
PHP Language Trivia
PHP Language TriviaPHP Language Trivia
PHP Language Trivia
 
A Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP GeneratorsA Functional Guide to Cat Herding with PHP Generators
A Functional Guide to Cat Herding with PHP Generators
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
Sylius and Api Platform The story of integration
Sylius and Api Platform The story of integrationSylius and Api Platform The story of integration
Sylius and Api Platform The story of integration
 

Destacado

React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigiReact Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
Yukiya Nakagawa
 

Destacado (20)

20170302 tryswift tasting_tests
20170302 tryswift tasting_tests20170302 tryswift tasting_tests
20170302 tryswift tasting_tests
 
Client-Side Deep Learning
Client-Side Deep LearningClient-Side Deep Learning
Client-Side Deep Learning
 
Swift + GraphQL
Swift + GraphQLSwift + GraphQL
Swift + GraphQL
 
RxSwift x Realm
RxSwift x RealmRxSwift x Realm
RxSwift x Realm
 
描画とビジネスをクリーンに分ける(公開用)
描画とビジネスをクリーンに分ける(公開用)描画とビジネスをクリーンに分ける(公開用)
描画とビジネスをクリーンに分ける(公開用)
 
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigiReact Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
 
みんなで Swift 復習会での談笑用スライド – 6th #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 6th #minna_de_swiftみんなで Swift 復習会での談笑用スライド – 6th #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 6th #minna_de_swift
 
Swift イニシャライザー復習会 #love_swift #akibaswift #21cafe
Swift イニシャライザー復習会 #love_swift #akibaswift #21cafeSwift イニシャライザー復習会 #love_swift #akibaswift #21cafe
Swift イニシャライザー復習会 #love_swift #akibaswift #21cafe
 
The Future of Mobile - Presented at SMX Munich
The Future of Mobile - Presented at SMX MunichThe Future of Mobile - Presented at SMX Munich
The Future of Mobile - Presented at SMX Munich
 
Presentazione Bando Pre-Seed
Presentazione Bando Pre-SeedPresentazione Bando Pre-Seed
Presentazione Bando Pre-Seed
 
minneにおけるテスト〜リリース〜リリース後にやっている事の紹介
minneにおけるテスト〜リリース〜リリース後にやっている事の紹介minneにおけるテスト〜リリース〜リリース後にやっている事の紹介
minneにおけるテスト〜リリース〜リリース後にやっている事の紹介
 
【掲載用】アウトプットし続ける技術20170314
【掲載用】アウトプットし続ける技術20170314【掲載用】アウトプットし続ける技術20170314
【掲載用】アウトプットし続ける技術20170314
 
決定版:サービスの盛り上がり具合をユーザの数(DAU)から読み解く方法
決定版:サービスの盛り上がり具合をユーザの数(DAU)から読み解く方法決定版:サービスの盛り上がり具合をユーザの数(DAU)から読み解く方法
決定版:サービスの盛り上がり具合をユーザの数(DAU)から読み解く方法
 
H2O x mrubyで人はどれだけ幸せになれるのか
H2O x mrubyで人はどれだけ幸せになれるのかH2O x mrubyで人はどれだけ幸せになれるのか
H2O x mrubyで人はどれだけ幸せになれるのか
 
スマホマーケットの概要と、 マーケティングの失敗例と改善 (アナリティクス アソシエーション 特別セミナー)
スマホマーケットの概要と、マーケティングの失敗例と改善 (アナリティクス アソシエーション 特別セミナー)スマホマーケットの概要と、マーケティングの失敗例と改善 (アナリティクス アソシエーション 特別セミナー)
スマホマーケットの概要と、 マーケティングの失敗例と改善 (アナリティクス アソシエーション 特別セミナー)
 
Design in Tech Report 2017
Design in Tech Report 2017Design in Tech Report 2017
Design in Tech Report 2017
 
Boletín 15/03/2017
Boletín 15/03/2017Boletín 15/03/2017
Boletín 15/03/2017
 
Personal effectiveness in the workplace
Personal effectiveness in the workplacePersonal effectiveness in the workplace
Personal effectiveness in the workplace
 
Gamification Workshop handouts
Gamification Workshop handoutsGamification Workshop handouts
Gamification Workshop handouts
 
3DCGMeetup08_MayaRigSystem_mGear
3DCGMeetup08_MayaRigSystem_mGear3DCGMeetup08_MayaRigSystem_mGear
3DCGMeetup08_MayaRigSystem_mGear
 

Similar a Writing Your App Swiftly

Our challenge for Bulkload reliability improvement
Our challenge for Bulkload reliability  improvementOur challenge for Bulkload reliability  improvement
Our challenge for Bulkload reliability improvement
Satoshi Akama
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testing
ryan_chambers
 
WebAPIs & WebRTC - Spotify/sthlm.js
WebAPIs & WebRTC - Spotify/sthlm.jsWebAPIs & WebRTC - Spotify/sthlm.js
WebAPIs & WebRTC - Spotify/sthlm.js
Robert Nyman
 

Similar a Writing Your App Swiftly (20)

Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React Crossing platforms with JavaScript & React
Crossing platforms with JavaScript & React
 
Future of Web Apps: Google Gears
Future of Web Apps: Google GearsFuture of Web Apps: Google Gears
Future of Web Apps: Google Gears
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
iOS Talks 6: Unit Testing
iOS Talks 6: Unit TestingiOS Talks 6: Unit Testing
iOS Talks 6: Unit Testing
 
SQLite and ORM Binding - Part 1 - Transcript.pdf
SQLite and ORM Binding - Part 1 - Transcript.pdfSQLite and ORM Binding - Part 1 - Transcript.pdf
SQLite and ORM Binding - Part 1 - Transcript.pdf
 
Our challenge for Bulkload reliability improvement
Our challenge for Bulkload reliability  improvementOur challenge for Bulkload reliability  improvement
Our challenge for Bulkload reliability improvement
 
Asynchronous Interfaces
Asynchronous InterfacesAsynchronous Interfaces
Asynchronous Interfaces
 
Building a Cloud API Server using Play(SCALA) & Riak
Building a Cloud API Server using  Play(SCALA) & Riak Building a Cloud API Server using  Play(SCALA) & Riak
Building a Cloud API Server using Play(SCALA) & Riak
 
Dojo and Adobe AIR
Dojo and Adobe AIRDojo and Adobe AIR
Dojo and Adobe AIR
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
 
Practialpop 160510130818
Practialpop 160510130818Practialpop 160510130818
Practialpop 160510130818
 
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in SwiftMCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
MCE^3 - Natasha Murashev - Practical Protocol-Oriented Programming in Swift
 
SproutCore and the Future of Web Apps
SproutCore and the Future of Web AppsSproutCore and the Future of Web Apps
SproutCore and the Future of Web Apps
 
Scti 2011 minicurso jquery
Scti 2011 minicurso jqueryScti 2011 minicurso jquery
Scti 2011 minicurso jquery
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
Automated javascript unit testing
Automated javascript unit testingAutomated javascript unit testing
Automated javascript unit testing
 
Swift Delhi: Practical POP
Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
 
WebAPIs & WebRTC - Spotify/sthlm.js
WebAPIs & WebRTC - Spotify/sthlm.jsWebAPIs & WebRTC - Spotify/sthlm.js
WebAPIs & WebRTC - Spotify/sthlm.js
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 

Último

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 

Último (20)

Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 

Writing Your App Swiftly

  • 1. Writing your App Swiftly Sommer Panage Chorus Fitness @sommer
  • 2.
  • 3.
  • 5. Today, in 4 short tales • Schrödinger's Result • The Little Layout Engine that Could • Swiftilocks and the Three View States • Pete and the Repeated Code
  • 7.
  • 9. Code in a box func getFilms(completion: @escaping ([Film]?, APIError?) -> Void) { let url = SWAPI.baseURL.appendingPathComponent(Endpoint.films.rawValue) let task = self.session.dataTask(with: url) { (data, response, error) in if let data = data { do { let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) if let films = SWAPI.decodeFilms(jsonObject: jsonObject) { completion(films, nil) } else { completion(nil, .decoding) } } catch { completion(nil, .server(originalError: error)) } } else { completion(nil, .server(originalError: error!)) } } task.resume() }
  • 10. What we think is happening…
  • 11. What's actually happening… override func viewDidLoad() { super.viewDidLoad() apiClient.getFilms() { films, error in if let films = films { // Show film UI if let error = error { // Log warning...this is weird } } else if let error = error { // Show error UI } else { // No results at all? Show error UI I guess? } } }
  • 12. Result open source framework by Rob Rix Model our server interaction as it actually is - success / failure! public enum Result<T, Error: Swift.Error>: ResultProtocol { case success(T) case failure(Error) }
  • 13. New, improved code func getFilms(completion: @escaping (Result<[Film], APIError>) -> Void) { let task = self.session .dataTask(with: SWAPI.baseURL.appendingPathComponent(Endpoint.films.rawValue)) { (data, response, error) in let result = Result(data, failWith: APIError.server(originalError: error!)) .flatMap { data in Result<Any, AnyError>(attempt: { try JSONSerialization.jsonObject(with: data, options: []) }) .mapError { _ in APIError.decoding } } .flatMap { Result(SWAPI.decodeFilms(jsonObject: $0), failWith: .decoding) } completion(result) } task.resume() }
  • 14. New, improved code override func viewDidLoad() { super.viewDidLoad() apiClient.getFilms() { result in switch result { case .success(let films): print(films) // Show my UI! case .failure(let error): print(error) // Show some error UI } } }
  • 15. The Moral of the Story Using the Result enum allowed us to • Model the sucess/failure of our server interaction more correctly • Thus simplify our view controller code.
  • 16. The Little Layout Engine that Could
  • 17. Old-school override func layoutSubviews() { super.layoutSubviews() // WHY AM I DOING THIS?!?! }
  • 18. What about Storyboards and Xibs? • Working in teams becomes harder because... • XML diffs • Merge conflicts?! • No constants • Stringly typed identifiers • Fragile connections
  • 19. Autolayout: iOS 9+ APIs init() { super.init(frame: .zero) addSubview(tableView) // Autolayout: Table same size as parent tableView.translatesAutoresizingMaskIntoConstraints = false tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true }
  • 20. Autolayout: Cartography by Robb Böhnke init() { super.init(frame: .zero) addSubview(tableView) // Autolayout: Table same size as parent constrain(tableView, self) { table, parent in table.edges == parent.edges } }
  • 21. More Cartography private let margin: CGFloat = 16 private let episodeLeftPadding: CGFloat = 8 override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(episodeLabel) contentView.addSubview(titleLabel) constrain(episodeLabel, titleLabel, contentView) { episode, title, parent in episode.leading == parent.leading + margin episode.top == parent.top + margin episode.bottom == parent.bottom - margin title.leading == episode.trailing + episodeLeftPadding title.trailing <= parent.trailing - margin title.centerY == episode.centerY } }
  • 22. The Moral of the Story Using the Cartography framework harnesses Swift's operator overloads to make programatic AutoLayout a breeze!
  • 24. Swiftilocks and the Three View States LOADING
  • 25. Swiftilocks and the Three View States SUCCESS
  • 26. Swiftilocks and the Three View States ERROR
  • 27.
  • 28. State management with bools /// MainView.swift var isLoading: Bool = false { didSet { errorView.isHidden = true loadingView.isHidden = !isLoading } } var isError: Bool = false { didSet { errorView.isHidden = !isError loadingView.isHidden = true } } var items: [MovieItem]? { didSet { tableView.reloadData() } }
  • 29. /// MainViewController.swift override func viewDidLoad() { super.viewDidLoad() title = "Star Wars Films" mainView.isLoading = true apiClient.getFilms() { result in DispatchQueue.main.async { switch result { case .success(let films): self.mainView.items = films .map { MovieItem(episodeID: $0.episodeID, title: $0.title) } .sorted { $0.0.episodeID < $0.1.episodeID } self.mainView.isLoading = false self.mainView.isError = false case .failure(let error): self.mainView.isLoading = false self.mainView.isError = true } } } }
  • 31. Data presence + state?!
  • 32. Enums to the rescue! final class MainView: UIView { enum State { case loading case loaded(items: [MovieItem]) case error(message: String) } init(state: State) { ... } // the rest of my class... }
  • 33. var state: State { didSet { switch state { case .loading: items = nil loadingView.isHidden = false errorView.isHidden = true case .error(let message): print(message) items = nil loadingView.isHidden = true errorView.isHidden = false case .loaded(let movieItems): loadingView.isHidden = true errorView.isHidden = true items = movieItems tableView.reloadData() } } }
  • 34. override func viewDidLoad() { super.viewDidLoad() title = "Star Wars Films" mainView.state = .loading apiClient.getFilms() { result in DispatchQueue.main.async { switch result { case .success(let films): let items = films .map { MovieItem(episodeID: $0.episodeID, title: $0.title) } .sorted { $0.0.episodeID < $0.1.episodeID } self.mainView.state = .loaded(items: items) case .failure(let error): self.mainView.state = .error(message: "Error: (error.localizedDescription)") } } } }
  • 35. The Moral of the Story Modelling our view state with an enum with associated values allows us to: 1. Simplify our VC 2. Avoid ambiguous state 3. Centralize our logic
  • 37. Pete and the Repeated Code.
  • 38. Repeated code var state: State { didSet { switch state { case .loading: text = nil loadingView.isHidden = false errorView.isHidden = true case .error(let message): print(message) text = nil loadingView.isHidden = true errorView.isHidden = false case .loaded(let text): loadingView.isHidden = true errorView.isHidden = true text = text tableView.reloadData() } } }
  • 39. Protocols save the day!! • A shared interface of methods and properties • Addresses a particular task • Types adopting protocol need not be related
  • 40. protocol DataLoading { associatedtype Data var state: ViewState<Data> { get set } var loadingView: LoadingView { get } var errorView: ErrorView { get } func update() }
  • 41. enum ViewState<Content> { case loading case loaded(data: Content) case error(message: String) }
  • 42. Default protocol implementation extension DataLoading where Self: UIView { func update() { switch state { case .loading: loadingView.isHidden = false errorView.isHidden = true case .error(let error): loadingView.isHidden = true errorView.isHidden = false Log.error(error) case .loaded: loadingView.isHidden = true errorView.isHidden = true } } }
  • 43. Conforming to DataLoading 1. Provide an errorView variable 2. Provide an loadingView variable 3. Provide a state variable that take some sort of Data 4. Call update() whenever needed
  • 44. DataLoading in our Main View final class MainView: UIView, DataLoading { let loadingView = LoadingView() let errorView = ErrorView() var state: ViewState<[MovieItem]> { didSet { update() // call update whenever we set our list of Movies tableView.reloadData() } }
  • 45. DataLoading in our Crawl View class CrawlView: UIView, DataLoading { let loadingView = LoadingView() let errorView = ErrorView() var state: ViewState<String> { didSet { update() crawlLabel.text = state.data } }
  • 46. The Moral of the Story Decomposing functionality that is shared by non- related objects into a protocol helps us • Avoid duplicated code • Consolidate our logic into one place
  • 47. Conclusion • Result: easily differentiate our success/error pathways • Cartography: use operator overloading to make code more readable • ViewState enum: never have an ambigous view state! • Protocols: define/decompose shared behaviors in unrelated types
  • 48. THANK YOU Contact Me: @sommer on Twitter me@sommerpanage.com