SlideShare a Scribd company logo
.(
) . ..)
!
Disk Defragment
Code Defragment
→ →
Advantages
EASY to UNDERSTAND
READ
FOLLOW
FIND
MAINTENANCE
Goal
→
NO FREE LUNCH
CASE STUDY
CASE #1. Data + Logic + UI
class ViewController: UIViewController {
@IBOutlet var number1: UILabel!
@IBOutlet var number2: UILabel!
@IBOutlet var result: UILabel!
struct CalcData {
let num1: Int
let num2: Int
}
private func textToInt(_ text: String?) -> Int {
guard let text = text else { return 0 }
return Int(text) ?? 0
}
@IBAction func calcResult(_ sender: Any) {
let n1 = textToInt(number1.text)
let n2 = textToInt(number2.text)
let data = CalcData(num1: n1, num2: n2)
let res = calcPlus(data)
result.text = intToText(res)
}
private func intToText(_ num: Int) -> String {
return "(num)"
}
private func calcPlus(_ data: CalcData) -> Int {
return data.num1 + data.num2
}
}
❓
CASE #1. Data + Logic + UI
class ViewController: UIViewController {
// MARK: - DATA
struct CalcData {
let num1: Int
let num2: Int
}
// MARK: - LOGIC
private func textToInt(_ text: String?) -> Int {
guard let text = text else { return 0 }
return Int(text) ?? 0
}
private func intToText(_ num: Int) -> String {
return "(num)"
}
private func calcPlus(_ data: CalcData) -> Int {
return data.num1 + data.num2
}
// MARK: - UI
@IBOutlet var number1: UILabel!
@IBOutlet var number2: UILabel!
@IBOutlet var result: UILabel!
@IBAction func calcResult(_ sender: Any) {
let n1 = textToInt(number1.text)
let n2 = textToInt(number2.text)
let data = CalcData(num1: n1, num2: n2)
let res = calcPlus(data)
result.text = intToText(res)
}
}
❗
CASE #2. Data Setter ❓
class ViewController: UIViewController {
@IBOutlet var textLabel: UILabel!
var count: Int = 0
@IBAction func addOne() {
count += 1
textLabel.text = "(count)"
}
@IBAction func subOne() {
count -= 1
textLabel.text = "(count)" // duplicated
}
}
CASE #2. Data Setter ❗
class ViewController: UIViewController {
@IBOutlet var textLabel: UILabel!
var count: Int = 0 {
didSet {
textLabel.text = "(count)"
}
}
@IBAction func addOne() {
count += 1
}
@IBAction func subOne() {
count -= 1
}
}
CASE #3. Overrides
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
func setupNavigationBar() {
self.navigationController?.title = "App Title"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
//Other Codes
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
//Other Codes
}
}
❓
class BaseViewController: UIViewController {
var viewWillAppearActions: [(Bool) -> ()] = []
var viewWillDisappearActions: [(Bool) -> ()] = []
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewWillAppearActions.forEach({ $0(animated) })
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
viewWillDisappearActions.forEach({ $0(animated) })
}
}
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
func setupNavigationBar() {
self.navigationController?.title = "App Title"
viewWillAppearActions.append({ [weak self] anim in
self?.navigationController?.setNavigationBarHidden(false, animated: anim)
})
viewWillDisappearActions.append({ [weak self] anim in
self?.navigationController?.setNavigationBarHidden(true, animated: anim)
})
}
}
❗CASE #3. Overrides
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupKeyboardEvent()
}
func setupKeyboardEvent() {
NotificationCenter.default.addObserver(self,
selector: #selector(onKeyboardWillShow),
name: .UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(onKeyboardWillHide),
name: .UIKeyboardWillHide,
object: nil)
}
@objc func onKeyboardWillShow(notification: Notification) {
print("Keyboard will show")
}
@objc func onKeyboardWillHide(notification: Notification) {
print("Keyboard will hide")
}
}
CASE #4. Selector ❓
class KeyboardEventWrapper {
var onKeyboardWillShowCallBack: (Notification) -> () = { _ in }
init() {
NotificationCenter.default.addObserver(self,
selector: #selector(onKeyboardWillShow),
name: .UIKeyboardWillShow,
object: nil)
}
@objc func onKeyboardWillShow(notification: Notification) {
onKeyboardWillShowCallBack(notification)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
class ViewController: UIViewController {
var keyboardEventWrapper: KeyboardEventWrapper!
override func viewDidLoad() {
super.viewDidLoad()
setupKeyboardEvent()
}
func setupKeyboardEvent() {
keyboardEventWrapper = KeyboardEventWrapper()
keyboardEventWrapper.onKeyboardWillShowCallBack = { _ in
print("Keyboard will show")
}
}
}
❗CASE #4. Selector
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
@IBAction func onLoadImage() {
let picker = UIImagePickerController()
picker.delegate = self
present(picker, animated: true, completion: nil)
}
// MARK: - UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String: Any]) {
let originalInfo = info[UIImagePickerControllerOriginalImage]
guard let originalImage = originalInfo as? UIImage else { return }
// ...
picker.dismiss(animated: true, completion: nil)
}
}
CASE #5. Delegate ❓
class ImagePickerWrapper: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var onImagePicked: (UIImage?) -> () = { _ in }
func showImagePicker(on vc: UIViewController) {
let picker = UIImagePickerController()
picker.delegate = self
vc.present(picker, animated: true, completion: nil)
}
// MARK: - UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String: Any]) {
let originalInfo = info[UIImagePickerControllerOriginalImage]
guard let originalImage = originalInfo as? UIImage else { return }
onImagePicked(originalImage)
picker.dismiss(animated: true, completion: nil)
}
}
class ViewController: UIViewController {
var imagePicker: ImagePickerWrapper!
@IBAction func onLoadImage() {
imagePicker = ImagePickerWrapper()
imagePicker.showImagePicker(on: self)
imagePicker.onImagePicked = { image in
guard let image = image else { return }
// ...
}
}
}
❗CASE #5. Delegate
ELEGANT WAY
feat. RxSwift
CASE #1. Data + Logic + UI
struct CalcData {
let num1: Int
let num2: Int
}
func textToInt(_ text: String?) -> Int {
guard let text = text else { return 0 }
return Int(text) ?? 0
}
func intToText(_ num: Int) -> String {
return "(num)"
}
func calcPlus(_ data: CalcData) -> Int {
return data.num1 + data.num2
}
class ViewController: UIViewController {
@IBOutlet var number1: UILabel!
@IBOutlet var number2: UILabel!
@IBOutlet var result: UILabel!
@IBAction func calcResult(_ sender: Any) {
let n1 = textToInt(number1.text)
let n2 = textToInt(number2.text)
let data = CalcData(num1: n1, num2: n2)
let res = calcPlus(data)
result.text = intToText(res)
}
}
Model.swift
Context.swift
ViewController.swift
❗❗
CASE #2. Data Setter
import RxSwift
import RxCocoa
// Common Functions
let intToText = { (i: Int) in "(i)" }
///
class ViewController: UIViewController {
@IBOutlet var textLabel: UILabel!
let disposeBag = DisposeBag()
let count = BehaviorRelay<Int>(value: 0)
override func viewDidLoad() {
super.viewDidLoad()
count.map(intToText)
.bind(to: textLabel.rx.text)
.disposed(by: disposeBag)
}
@IBAction func addOne() {
count.accept(count.value + 1)
}
@IBAction func subOne() {
count.accept(count.value - 1)
}
}
❗❗
CASE #2. Data Setter
import RxSwift
import RxCocoa
// Common Functions
let intToText = { (i: Int) in "(i)" }
///
class ViewController: UIViewController {
@IBOutlet var textLabel: UILabel!
@IBOutlet var textLabelx10: UILabel!
let disposeBag = DisposeBag()
let count = BehaviorRelay<Int>(value: 0)
override func viewDidLoad() {
super.viewDidLoad()
count.map(intToText)
.bind(to: textLabel.rx.text)
.disposed(by: disposeBag)
count.map({ $0 * 10 })
.map(intToText)
.bind(to: textLabelx10.rx.text)
.disposed(by: disposeBag)
}
@IBAction func addOne() {
count.accept(count.value + 1)
}
@IBAction func subOne() {
count.accept(count.value - 1)
}
}
❗❗
import RxSwift
import RxCocoa
import RxViewController // <==
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
func setupNavigationBar() {
self.navigationController?.title = "App Title"
self.rx.viewWillAppear.subscribe(onNext: { [weak self] anim in
self?.navigationController?.setNavigationBarHidden(false, animated: anim)
}).disposed(by: disposeBag)
self.rx.viewWillDisappear.subscribe(onNext: { [weak self] anim in
self?.navigationController?.setNavigationBarHidden(true, animated: anim)
}).disposed(by: disposeBag)
}
}
CASE #3. Overrides ❗❗
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setupKeyboardEvent()
}
func setupKeyboardEvent() {
NotificationCenter.default.rx.notification(.UIKeyboardWillShow)
.subscribe(onNext: { notifiaction in
print("Keyboard will show")
})
.disposed(by: disposeBag)
NotificationCenter.default.rx.notification(.UIKeyboardWillHide)
.subscribe(onNext: { notifiaction in
print("Keyboard will hide")
})
.disposed(by: disposeBag)
}
}
CASE #4. Selector ❗❗
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
@IBOutlet weak var imageView: UIImageView!
@IBAction func onLoadImage() {
let picker = UIImagePickerController()
present(picker, animated: true, completion: nil)
picker.rx.didFinishPickingMediaWithInfo
.map({ info -> UIImage? in
info[UIImagePickerControllerOriginalImage] as? UIImage
})
.bind(to: imageView.rx.image)
.disposed(by: disposeBag)
}
}
CASE #5. Delegate ❗❗
// MARK:- UIImagePickerController.rx
// picker.rx.didFinishPickingMediaWithInfo
// ~~~~~~ ~~
// Base Reactive
extension Reactive where Base: UIImagePickerController {
public var didFinishPickingMediaWithInfo: Observable<[String : Any]> {
return RxImagePickerProxy.proxy(for: base)
.didFinishPickingMediaWithInfoSubject
.asObservable()
.do(onCompleted: {
self.base.dismiss(animated: true, completion: nil)
})
}
public var didCancel: Observable<Void> {
return RxImagePickerProxy.proxy(for: base)
.didCancelSubject
.asObservable()
.do(onCompleted: {
self.base.dismiss(animated: true, completion: nil)
})
}
}
CASE #5. Delegate ❗❗
import UIKit
import RxSwift
import RxCocoa
public typealias ImagePickerDelegate = UIImagePickerControllerDelegate & UINavigationControllerDelegate
extension UIImagePickerController: HasDelegate {
public typealias Delegate = ImagePickerDelegate
}
class RxImagePickerProxy: DelegateProxy<UIImagePickerController, ImagePickerDelegate>,
DelegateProxyType, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
public init(imagePicker: UIImagePickerController) {
super.init(parentObject: imagePicker, delegateProxy: RxImagePickerProxy.self)
}
//MARK:- DelegateProxyType
public static func registerKnownImplementations() {
self.register { RxImagePickerProxy(imagePicker: $0) }
}
static func currentDelegate(for object: UIImagePickerController) -> ImagePickerDelegate? {
return object.delegate
}
static func setCurrentDelegate(_ delegate: ImagePickerDelegate?, to object: UIImagePickerController)
{
object.delegate = delegate
}
//MARK:- Proxy Subject
//MARK:- UIImagePickerControllerDelegate
//MARK:- Completed
}
CASE #5. Delegate ❗❗
class RxImagePickerProxy: DelegateProxy<UIImagePickerController, ImagePickerDelegate>,
DelegateProxyType, UIImagePickerControllerDelegate, UINavigationControllerDelegate
{
//MARK:- DelegateProxyType
//MARK:- Proxy Subject
internal lazy var didFinishPickingMediaWithInfoSubject = PublishSubject<[String : Any]>()
internal lazy var didCancelSubject = PublishSubject<Void>()
//MARK:- UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String: Any]) {
didFinishPickingMediaWithInfoSubject.onNext(info)
didFinishPickingMediaWithInfoSubject.onCompleted()
didCancelSubject.onCompleted()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
didCancelSubject.onNext(())
didCancelSubject.onCompleted()
didFinishPickingMediaWithInfoSubject.onCompleted()
}
//MARK:- Completed
deinit {
self.didFinishPickingMediaWithInfoSubject.onCompleted()
self.didCancelSubject.onCompleted()
}
}
CASE #5. Delegate ❗❗
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
@IBOutlet weak var imageView: UIImageView!
@IBAction func onLoadImage() {
let picker = UIImagePickerController()
present(picker, animated: true, completion: nil)
picker.rx.didFinishPickingMediaWithInfo
.map({ info -> UIImage? in
info[UIImagePickerControllerOriginalImage] as? UIImage
})
.bind(to: imageView.rx.image)
.disposed(by: disposeBag)
}
}
CASE #5. Delegate ❗❗
ALL TOGETHER
SUMMARY
UI
Model.swift
struct CalcData {
let n1: Int?
let n2: Int?
let result: Int?
}
extension CalcData {
static func empty() -> CalcData {
return CalcData(n1: nil, n2: nil, result: nil)
}
}
Context.swift (Business Logic)
let i2s = { (i: Int?) in "(i ?? 0)" }
let s2i = { (s: String?) in Int(s ?? "0") }
class Context {
let model = BehaviorRelay<CalcData>(value: CalcData.empty())
func doCalc(data: CalcData) -> CalcData {
guard let n1 = data.n1 else { return data }
guard let n2 = data.n2 else { return data }
let result = n1 + n2
return CalcData(n1: n1, n2: n2, result: result)
}
}
ViewController.swift
class ViewController: UIViewController {
@IBOutlet weak var n1Field: UITextField! // Input
@IBOutlet weak var n2Field: UITextField! // Input
@IBOutlet weak var resultLabel: UILabel! // Output
@IBOutlet weak var calcButton: UIButton! // Event
let context = Context()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindUI()
bindEvent()
}
func bindUI() {
// ...
}
func bindEvent() {
// …
}
}
ViewController.swift
class ViewController: UIViewController {
func bindUI() {
//n1 input
n1Field.rx.text.map(s2i)
.withLatestFrom(context.model) { (n1, dat) in
CalcData(n1: n1, n2: dat.n2, result: dat.result)
}
.bind(to: context.model)
.disposed(by: disposeBag)
//n2 input
n2Field.rx.text.map(s2i)
.withLatestFrom(context.model) { (n2, dat) in
CalcData(n1: dat.n1, n2: n2, result: dat.result)
}
.bind(to: context.model)
.disposed(by: disposeBag)
//result output
viewModel.model.map({ $0.result })
.map(i2s)
.bind(to: resultLabel.rx.text)
.disposed(by: disposeBag)
}
func bindEvent() {
//event
calcButton.rx.tap
.withLatestFrom(context.model)
.map(context.doCalc)
.bind(to: context.model)
.disposed(by: disposeBag)
}
}
Summary
class ViewController: UIViewController {
@IBOutlet weak var n1Field: UITextField! // Input
@IBOutlet weak var n2Field: UITextField! // Input
@IBOutlet weak var resultLabel: UILabel! // Output
@IBOutlet weak var calcButton: UIButton! // Event
let context = Context()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindUI()
bindEvent()
}
func bindUI() {
//n1 input
n1Field.rx.text.map(s2i)
.withLatestFrom(context.model) { (n1, dat) in
CalcData(n1: n1, n2: dat.n2, result: dat.result)
}
.bind(to: context.model)
.disposed(by: disposeBag)
//n2 input
n2Field.rx.text.map(s2i)
.withLatestFrom(context.model) { (n2, dat) in
CalcData(n1: dat.n1, n2: n2, result: dat.result)
}
.bind(to: context.model)
.disposed(by: disposeBag)
//result output
viewModel.model.map({ $0.result })
.map(i2s)
.bind(to: resultLabel.rx.text)
.disposed(by: disposeBag)
}
func bindEvent() {
//event
calcButton.rx.tap
.withLatestFrom(context.model)
.map(context.doCalc)
.bind(to: context.model)
.disposed(by: disposeBag)
}
}
struct CalcData {
let n1: Int?
let n2: Int?
let result: Int?
}
extension CalcData {
static func empty() -> CalcData {
return CalcData(n1: nil, n2: nil, result: nil)
}
}
View
ViewController
Context
Model
let i2s = { (i: Int?) in "(i ?? 0)" }
let s2i = { (s: String?) in Int(s ?? "0") }
class Context {
let model = BehaviorRelay<CalcData>(value: CalcData.empty())
func doCalc(data: CalcData) -> CalcData {
guard let n1 = data.n1 else { return data }
guard let n2 = data.n2 else { return data }
let result = n1 + n2
return CalcData(n1: n1, n2: n2, result: result)
}
}
struct CalcData {
let n1: Int?
let n2: Int?
let result: Int?
}
extension CalcData {
static func empty() -> CalcData {
return CalcData(n1: nil, n2: nil, result: nil)
}
}
View
ViewController
Context
Model
ViewModel
Summary
class ViewController: UIViewController {
@IBOutlet weak var n1Field: UITextField! // Input
@IBOutlet weak var n2Field: UITextField! // Input
@IBOutlet weak var resultLabel: UILabel! // Output
@IBOutlet weak var calcButton: UIButton! // Event
let viewModel = ViewModel()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindUI()
bindEvent()
}
func bindUI() {
//n1 input
n1Field.rx.text.map(s2i)
.withLatestFrom(viewModel.model) { (n1, dat) in
CalcData(n1: n1, n2: dat.n2, result: dat.result)
}
.bind(to: viewModel.model)
.disposed(by: disposeBag)
//n2 input
n2Field.rx.text.map(s2i)
.withLatestFrom(viewModel.model) { (n2, dat) in
CalcData(n1: dat.n1, n2: n2, result: dat.result)
}
.bind(to: viewModel.model)
.disposed(by: disposeBag)
//result output
viewModel.model.map({ $0.result })
.map(i2s)
.bind(to: resultLabel.rx.text)
.disposed(by: disposeBag)
}
func bindEvent() {
//event
calcButton.rx.tap
.withLatestFrom(viewModel.model)
.map(viewModel.doCalc)
.bind(to: viewModel.model)
.disposed(by: disposeBag)
}
}
let i2s = { (i: Int?) in "(i ?? 0)" }
let s2i = { (s: String?) in Int(s ?? "0") }
class ViewModel {
let model = BehaviorRelay<CalcData>(value: CalcData.empty())
func doCalc(data: CalcData) -> CalcData {
guard let n1 = data.n1 else { return data }
guard let n2 = data.n2 else { return data }
let result = n1 + n2
return CalcData(n1: n1, n2: n2, result: result)
}
}
20180721 code defragment

More Related Content

PDF
Programmer en html5, css 3 et java script (70 480)
Chamseddine Ouerhani
 
PPTX
React - Start learning today
Nitin Tyagi
 
PDF
Sequence and Traverse - Part 2
Philip Schwarz
 
PPTX
Nodejs functions & modules
monikadeshmane
 
PDF
PHP 8 で Web 以外の世界の扉を叩く
shinjiigarashi
 
PDF
MVVM & RxSwift
Thai Son Dang
 
PDF
Alphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm
 
PDF
Cours java avance avancé thread arraylist
Houssem Hamrouni
 
Programmer en html5, css 3 et java script (70 480)
Chamseddine Ouerhani
 
React - Start learning today
Nitin Tyagi
 
Sequence and Traverse - Part 2
Philip Schwarz
 
Nodejs functions & modules
monikadeshmane
 
PHP 8 で Web 以外の世界の扉を叩く
shinjiigarashi
 
MVVM & RxSwift
Thai Son Dang
 
Alphorm.com Formation Laravel : Construire une Application de A à Z
Alphorm
 
Cours java avance avancé thread arraylist
Houssem Hamrouni
 

What's hot (20)

PDF
Variable hoisting in JavaScript
Ideas2IT Technologies
 
PPTX
Introduction à React
Abdoulaye Dieng
 
PPT
Hibernate
Sunil OS
 
ODP
Linguagem C 06 Funcoes
Regis Magalhães
 
PDF
Pros & cons of svelte
ElenorWisozk
 
PDF
Introduction à React
Thibault Martinez
 
PPTX
Owl: The New Odoo UI Framework
Odoo
 
DOCX
Java practical
shweta-sharma99
 
PDF
Support de Cours JSF2 Première partie Intégration avec Spring
ENSET, Université Hassan II Casablanca
 
PPT
JavaScript Event Loop
Thomas Hunter II
 
PDF
Callback Function
Roland San Nicolas
 
PDF
PLAT-13 Metadata Extraction and Transformation
Alfresco Software
 
PDF
Railway Oriented Programming
Scott Wlaschin
 
PPTX
Learning Svelte
Christoffer Noring
 
PPTX
Getting started with docker
Saim Safder
 
PDF
DSC IIITL Flutter Workshop
DSCIIITLucknow
 
PDF
코틀린 멀티플랫폼, 미지와의 조우
Arawn Park
 
PDF
[131]해커의 관점에서 바라보기
NAVER D2
 
PPTX
React render props
Saikat Samanta
 
Variable hoisting in JavaScript
Ideas2IT Technologies
 
Introduction à React
Abdoulaye Dieng
 
Hibernate
Sunil OS
 
Linguagem C 06 Funcoes
Regis Magalhães
 
Pros & cons of svelte
ElenorWisozk
 
Introduction à React
Thibault Martinez
 
Owl: The New Odoo UI Framework
Odoo
 
Java practical
shweta-sharma99
 
Support de Cours JSF2 Première partie Intégration avec Spring
ENSET, Université Hassan II Casablanca
 
JavaScript Event Loop
Thomas Hunter II
 
Callback Function
Roland San Nicolas
 
PLAT-13 Metadata Extraction and Transformation
Alfresco Software
 
Railway Oriented Programming
Scott Wlaschin
 
Learning Svelte
Christoffer Noring
 
Getting started with docker
Saim Safder
 
DSC IIITL Flutter Workshop
DSCIIITLucknow
 
코틀린 멀티플랫폼, 미지와의 조우
Arawn Park
 
[131]해커의 관점에서 바라보기
NAVER D2
 
React render props
Saikat Samanta
 
Ad

Similar to 20180721 code defragment (20)

PDF
Minimizing Decision Fatigue to Improve Team Productivity
Derek Lee
 
PDF
Writing Your App Swiftly
Sommer Panage
 
PDF
Swift - One step forward from Obj-C
Nissan Tsafrir
 
PDF
Using a model view-view model architecture for iOS apps
allanh0526
 
PDF
Some Stuff I was thinking about state machines and types
gillygize
 
PPTX
Basic iOS Training with SWIFT - Part 3
Manoj Ellappan
 
PDF
Prescribing RX Responsibly
Nareg Khoshafian
 
PDF
Hello, ReactorKit 
Suyeol Jeon
 
PPTX
Code camp 2011 Getting Started with IOS, Una Daly
Una Daly
 
PDF
Swift 함수 커링 사용하기
진성 오
 
PDF
RxSwift 활용하기 - Let'Swift 2017
Wanbok Choi
 
PPTX
Code Quality Management iOS
Arpit Kulsreshtha
 
PDF
Writing clean code
Angel Garcia Olloqui
 
PDF
Universal programming recipes​ - Ekaterina Trofimenko - Women In Technology
Badoo
 
PDF
"Universal programming recipes", Kateryna Trofimenko
Badoo Development
 
PDF
iOS: Table Views
Jussi Pohjolainen
 
PPTX
Swift Tableview iOS App Development
Ketan Raval
 
PDF
스위프트를 여행하는 히치하이커를 위한 스타일 안내
Jung Kim
 
PDF
Deep Dive Into Swift
Sarath C
 
PDF
Pragmatic Swift
Kenneth Blake Merryman
 
Minimizing Decision Fatigue to Improve Team Productivity
Derek Lee
 
Writing Your App Swiftly
Sommer Panage
 
Swift - One step forward from Obj-C
Nissan Tsafrir
 
Using a model view-view model architecture for iOS apps
allanh0526
 
Some Stuff I was thinking about state machines and types
gillygize
 
Basic iOS Training with SWIFT - Part 3
Manoj Ellappan
 
Prescribing RX Responsibly
Nareg Khoshafian
 
Hello, ReactorKit 
Suyeol Jeon
 
Code camp 2011 Getting Started with IOS, Una Daly
Una Daly
 
Swift 함수 커링 사용하기
진성 오
 
RxSwift 활용하기 - Let'Swift 2017
Wanbok Choi
 
Code Quality Management iOS
Arpit Kulsreshtha
 
Writing clean code
Angel Garcia Olloqui
 
Universal programming recipes​ - Ekaterina Trofimenko - Women In Technology
Badoo
 
"Universal programming recipes", Kateryna Trofimenko
Badoo Development
 
iOS: Table Views
Jussi Pohjolainen
 
Swift Tableview iOS App Development
Ketan Raval
 
스위프트를 여행하는 히치하이커를 위한 스타일 안내
Jung Kim
 
Deep Dive Into Swift
Sarath C
 
Pragmatic Swift
Kenneth Blake Merryman
 
Ad

More from Chiwon Song (20)

PDF
20250425_AI가 코딩하는 시대에 개발자가 되겠다구요_.pdf
Chiwon Song
 
PDF
20250210_AI가 코딩하는시대에 개발자 되기 - Google Slides.pdf
Chiwon Song
 
PDF
20240330_고급진 코드를 위한 exception 다루기
Chiwon Song
 
PDF
요즘 유행하는 AI 나도 해보자 (feat. CoreML)
Chiwon Song
 
PDF
20220716_만들면서 느껴보는 POP
Chiwon Song
 
PDF
20210812 컴퓨터는 어떻게 동작하는가?
Chiwon Song
 
PDF
20201121 코드 삼분지계
Chiwon Song
 
PDF
20200815 inversions
Chiwon Song
 
PDF
20191116 custom operators in swift
Chiwon Song
 
PDF
[20190601] 직업훈련교사_수업의실행_교안
Chiwon Song
 
PPTX
[20190601] 직업훈련교사_수업의실행
Chiwon Song
 
PDF
20190330 immutable data
Chiwon Song
 
PPTX
20190306 만들면서 배우는 IoT / IoT의 이해
Chiwon Song
 
PDF
20181020 advanced higher-order function
Chiwon Song
 
PDF
20180310 functional programming
Chiwon Song
 
PDF
20171104 FRP 패러다임
Chiwon Song
 
PDF
스크래치로 시작하는 코딩
Chiwon Song
 
PPTX
메이커운동과 아두이노
Chiwon Song
 
PPTX
아두이노 RC카 만들기
Chiwon Song
 
PPTX
[5] 아두이노로 만드는 IoT
Chiwon Song
 
20250425_AI가 코딩하는 시대에 개발자가 되겠다구요_.pdf
Chiwon Song
 
20250210_AI가 코딩하는시대에 개발자 되기 - Google Slides.pdf
Chiwon Song
 
20240330_고급진 코드를 위한 exception 다루기
Chiwon Song
 
요즘 유행하는 AI 나도 해보자 (feat. CoreML)
Chiwon Song
 
20220716_만들면서 느껴보는 POP
Chiwon Song
 
20210812 컴퓨터는 어떻게 동작하는가?
Chiwon Song
 
20201121 코드 삼분지계
Chiwon Song
 
20200815 inversions
Chiwon Song
 
20191116 custom operators in swift
Chiwon Song
 
[20190601] 직업훈련교사_수업의실행_교안
Chiwon Song
 
[20190601] 직업훈련교사_수업의실행
Chiwon Song
 
20190330 immutable data
Chiwon Song
 
20190306 만들면서 배우는 IoT / IoT의 이해
Chiwon Song
 
20181020 advanced higher-order function
Chiwon Song
 
20180310 functional programming
Chiwon Song
 
20171104 FRP 패러다임
Chiwon Song
 
스크래치로 시작하는 코딩
Chiwon Song
 
메이커운동과 아두이노
Chiwon Song
 
아두이노 RC카 만들기
Chiwon Song
 
[5] 아두이노로 만드는 IoT
Chiwon Song
 

Recently uploaded (20)

PDF
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
PDF
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PPTX
The-Ethical-Hackers-Imperative-Safeguarding-the-Digital-Frontier.pptx
sujalchauhan1305
 
PDF
AI Unleashed - Shaping the Future -Starting Today - AIOUG Yatra 2025 - For Co...
Sandesh Rao
 
PPTX
Introduction to Flutter by Ayush Desai.pptx
ayushdesai204
 
PPTX
The Future of AI & Machine Learning.pptx
pritsen4700
 
PDF
Doc9.....................................
SofiaCollazos
 
PDF
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
PDF
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PDF
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
PPTX
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PDF
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
PDF
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
PDF
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
zz41354899
 
PDF
AI-Cloud-Business-Management-Platforms-The-Key-to-Efficiency-Growth.pdf
Artjoker Software Development Company
 
PDF
Structs to JSON: How Go Powers REST APIs
Emily Achieng
 
PPTX
Simple and concise overview about Quantum computing..pptx
mughal641
 
Make GenAI investments go further with the Dell AI Factory
Principled Technologies
 
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
The-Ethical-Hackers-Imperative-Safeguarding-the-Digital-Frontier.pptx
sujalchauhan1305
 
AI Unleashed - Shaping the Future -Starting Today - AIOUG Yatra 2025 - For Co...
Sandesh Rao
 
Introduction to Flutter by Ayush Desai.pptx
ayushdesai204
 
The Future of AI & Machine Learning.pptx
pritsen4700
 
Doc9.....................................
SofiaCollazos
 
NewMind AI Weekly Chronicles - July'25 - Week IV
NewMind AI
 
How ETL Control Logic Keeps Your Pipelines Safe and Reliable.pdf
Stryv Solutions Pvt. Ltd.
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
AI and Robotics for Human Well-being.pptx
JAYMIN SUTHAR
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
Research-Fundamentals-and-Topic-Development.pdf
ayesha butalia
 
Trying to figure out MCP by actually building an app from scratch with open s...
Julien SIMON
 
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
zz41354899
 
AI-Cloud-Business-Management-Platforms-The-Key-to-Efficiency-Growth.pdf
Artjoker Software Development Company
 
Structs to JSON: How Go Powers REST APIs
Emily Achieng
 
Simple and concise overview about Quantum computing..pptx
mughal641
 

20180721 code defragment

  • 2. !
  • 7. CASE #1. Data + Logic + UI class ViewController: UIViewController { @IBOutlet var number1: UILabel! @IBOutlet var number2: UILabel! @IBOutlet var result: UILabel! struct CalcData { let num1: Int let num2: Int } private func textToInt(_ text: String?) -> Int { guard let text = text else { return 0 } return Int(text) ?? 0 } @IBAction func calcResult(_ sender: Any) { let n1 = textToInt(number1.text) let n2 = textToInt(number2.text) let data = CalcData(num1: n1, num2: n2) let res = calcPlus(data) result.text = intToText(res) } private func intToText(_ num: Int) -> String { return "(num)" } private func calcPlus(_ data: CalcData) -> Int { return data.num1 + data.num2 } } ❓
  • 8. CASE #1. Data + Logic + UI class ViewController: UIViewController { // MARK: - DATA struct CalcData { let num1: Int let num2: Int } // MARK: - LOGIC private func textToInt(_ text: String?) -> Int { guard let text = text else { return 0 } return Int(text) ?? 0 } private func intToText(_ num: Int) -> String { return "(num)" } private func calcPlus(_ data: CalcData) -> Int { return data.num1 + data.num2 } // MARK: - UI @IBOutlet var number1: UILabel! @IBOutlet var number2: UILabel! @IBOutlet var result: UILabel! @IBAction func calcResult(_ sender: Any) { let n1 = textToInt(number1.text) let n2 = textToInt(number2.text) let data = CalcData(num1: n1, num2: n2) let res = calcPlus(data) result.text = intToText(res) } } ❗
  • 9. CASE #2. Data Setter ❓ class ViewController: UIViewController { @IBOutlet var textLabel: UILabel! var count: Int = 0 @IBAction func addOne() { count += 1 textLabel.text = "(count)" } @IBAction func subOne() { count -= 1 textLabel.text = "(count)" // duplicated } }
  • 10. CASE #2. Data Setter ❗ class ViewController: UIViewController { @IBOutlet var textLabel: UILabel! var count: Int = 0 { didSet { textLabel.text = "(count)" } } @IBAction func addOne() { count += 1 } @IBAction func subOne() { count -= 1 } }
  • 11. CASE #3. Overrides class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setupNavigationBar() } func setupNavigationBar() { self.navigationController?.title = "App Title" } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(false, animated: animated) //Other Codes } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.navigationController?.setNavigationBarHidden(true, animated: animated) //Other Codes } } ❓
  • 12. class BaseViewController: UIViewController { var viewWillAppearActions: [(Bool) -> ()] = [] var viewWillDisappearActions: [(Bool) -> ()] = [] override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewWillAppearActions.forEach({ $0(animated) }) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) viewWillDisappearActions.forEach({ $0(animated) }) } } class ViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() setupNavigationBar() } func setupNavigationBar() { self.navigationController?.title = "App Title" viewWillAppearActions.append({ [weak self] anim in self?.navigationController?.setNavigationBarHidden(false, animated: anim) }) viewWillDisappearActions.append({ [weak self] anim in self?.navigationController?.setNavigationBarHidden(true, animated: anim) }) } } ❗CASE #3. Overrides
  • 13. class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setupKeyboardEvent() } func setupKeyboardEvent() { NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillShow), name: .UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillHide), name: .UIKeyboardWillHide, object: nil) } @objc func onKeyboardWillShow(notification: Notification) { print("Keyboard will show") } @objc func onKeyboardWillHide(notification: Notification) { print("Keyboard will hide") } } CASE #4. Selector ❓
  • 14. class KeyboardEventWrapper { var onKeyboardWillShowCallBack: (Notification) -> () = { _ in } init() { NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillShow), name: .UIKeyboardWillShow, object: nil) } @objc func onKeyboardWillShow(notification: Notification) { onKeyboardWillShowCallBack(notification) } deinit { NotificationCenter.default.removeObserver(self) } } class ViewController: UIViewController { var keyboardEventWrapper: KeyboardEventWrapper! override func viewDidLoad() { super.viewDidLoad() setupKeyboardEvent() } func setupKeyboardEvent() { keyboardEventWrapper = KeyboardEventWrapper() keyboardEventWrapper.onKeyboardWillShowCallBack = { _ in print("Keyboard will show") } } } ❗CASE #4. Selector
  • 15. class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { @IBAction func onLoadImage() { let picker = UIImagePickerController() picker.delegate = self present(picker, animated: true, completion: nil) } // MARK: - UIImagePickerControllerDelegate func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) { let originalInfo = info[UIImagePickerControllerOriginalImage] guard let originalImage = originalInfo as? UIImage else { return } // ... picker.dismiss(animated: true, completion: nil) } } CASE #5. Delegate ❓
  • 16. class ImagePickerWrapper: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { var onImagePicked: (UIImage?) -> () = { _ in } func showImagePicker(on vc: UIViewController) { let picker = UIImagePickerController() picker.delegate = self vc.present(picker, animated: true, completion: nil) } // MARK: - UIImagePickerControllerDelegate func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) { let originalInfo = info[UIImagePickerControllerOriginalImage] guard let originalImage = originalInfo as? UIImage else { return } onImagePicked(originalImage) picker.dismiss(animated: true, completion: nil) } } class ViewController: UIViewController { var imagePicker: ImagePickerWrapper! @IBAction func onLoadImage() { imagePicker = ImagePickerWrapper() imagePicker.showImagePicker(on: self) imagePicker.onImagePicked = { image in guard let image = image else { return } // ... } } } ❗CASE #5. Delegate
  • 18. CASE #1. Data + Logic + UI struct CalcData { let num1: Int let num2: Int } func textToInt(_ text: String?) -> Int { guard let text = text else { return 0 } return Int(text) ?? 0 } func intToText(_ num: Int) -> String { return "(num)" } func calcPlus(_ data: CalcData) -> Int { return data.num1 + data.num2 } class ViewController: UIViewController { @IBOutlet var number1: UILabel! @IBOutlet var number2: UILabel! @IBOutlet var result: UILabel! @IBAction func calcResult(_ sender: Any) { let n1 = textToInt(number1.text) let n2 = textToInt(number2.text) let data = CalcData(num1: n1, num2: n2) let res = calcPlus(data) result.text = intToText(res) } } Model.swift Context.swift ViewController.swift ❗❗
  • 19. CASE #2. Data Setter import RxSwift import RxCocoa // Common Functions let intToText = { (i: Int) in "(i)" } /// class ViewController: UIViewController { @IBOutlet var textLabel: UILabel! let disposeBag = DisposeBag() let count = BehaviorRelay<Int>(value: 0) override func viewDidLoad() { super.viewDidLoad() count.map(intToText) .bind(to: textLabel.rx.text) .disposed(by: disposeBag) } @IBAction func addOne() { count.accept(count.value + 1) } @IBAction func subOne() { count.accept(count.value - 1) } } ❗❗
  • 20. CASE #2. Data Setter import RxSwift import RxCocoa // Common Functions let intToText = { (i: Int) in "(i)" } /// class ViewController: UIViewController { @IBOutlet var textLabel: UILabel! @IBOutlet var textLabelx10: UILabel! let disposeBag = DisposeBag() let count = BehaviorRelay<Int>(value: 0) override func viewDidLoad() { super.viewDidLoad() count.map(intToText) .bind(to: textLabel.rx.text) .disposed(by: disposeBag) count.map({ $0 * 10 }) .map(intToText) .bind(to: textLabelx10.rx.text) .disposed(by: disposeBag) } @IBAction func addOne() { count.accept(count.value + 1) } @IBAction func subOne() { count.accept(count.value - 1) } } ❗❗
  • 21. import RxSwift import RxCocoa import RxViewController // <== class ViewController: UIViewController { let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() setupNavigationBar() } func setupNavigationBar() { self.navigationController?.title = "App Title" self.rx.viewWillAppear.subscribe(onNext: { [weak self] anim in self?.navigationController?.setNavigationBarHidden(false, animated: anim) }).disposed(by: disposeBag) self.rx.viewWillDisappear.subscribe(onNext: { [weak self] anim in self?.navigationController?.setNavigationBarHidden(true, animated: anim) }).disposed(by: disposeBag) } } CASE #3. Overrides ❗❗
  • 22. import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() setupKeyboardEvent() } func setupKeyboardEvent() { NotificationCenter.default.rx.notification(.UIKeyboardWillShow) .subscribe(onNext: { notifiaction in print("Keyboard will show") }) .disposed(by: disposeBag) NotificationCenter.default.rx.notification(.UIKeyboardWillHide) .subscribe(onNext: { notifiaction in print("Keyboard will hide") }) .disposed(by: disposeBag) } } CASE #4. Selector ❗❗
  • 23. import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let disposeBag = DisposeBag() @IBOutlet weak var imageView: UIImageView! @IBAction func onLoadImage() { let picker = UIImagePickerController() present(picker, animated: true, completion: nil) picker.rx.didFinishPickingMediaWithInfo .map({ info -> UIImage? in info[UIImagePickerControllerOriginalImage] as? UIImage }) .bind(to: imageView.rx.image) .disposed(by: disposeBag) } } CASE #5. Delegate ❗❗
  • 24. // MARK:- UIImagePickerController.rx // picker.rx.didFinishPickingMediaWithInfo // ~~~~~~ ~~ // Base Reactive extension Reactive where Base: UIImagePickerController { public var didFinishPickingMediaWithInfo: Observable<[String : Any]> { return RxImagePickerProxy.proxy(for: base) .didFinishPickingMediaWithInfoSubject .asObservable() .do(onCompleted: { self.base.dismiss(animated: true, completion: nil) }) } public var didCancel: Observable<Void> { return RxImagePickerProxy.proxy(for: base) .didCancelSubject .asObservable() .do(onCompleted: { self.base.dismiss(animated: true, completion: nil) }) } } CASE #5. Delegate ❗❗
  • 25. import UIKit import RxSwift import RxCocoa public typealias ImagePickerDelegate = UIImagePickerControllerDelegate & UINavigationControllerDelegate extension UIImagePickerController: HasDelegate { public typealias Delegate = ImagePickerDelegate } class RxImagePickerProxy: DelegateProxy<UIImagePickerController, ImagePickerDelegate>, DelegateProxyType, UIImagePickerControllerDelegate, UINavigationControllerDelegate { public init(imagePicker: UIImagePickerController) { super.init(parentObject: imagePicker, delegateProxy: RxImagePickerProxy.self) } //MARK:- DelegateProxyType public static func registerKnownImplementations() { self.register { RxImagePickerProxy(imagePicker: $0) } } static func currentDelegate(for object: UIImagePickerController) -> ImagePickerDelegate? { return object.delegate } static func setCurrentDelegate(_ delegate: ImagePickerDelegate?, to object: UIImagePickerController) { object.delegate = delegate } //MARK:- Proxy Subject //MARK:- UIImagePickerControllerDelegate //MARK:- Completed } CASE #5. Delegate ❗❗
  • 26. class RxImagePickerProxy: DelegateProxy<UIImagePickerController, ImagePickerDelegate>, DelegateProxyType, UIImagePickerControllerDelegate, UINavigationControllerDelegate { //MARK:- DelegateProxyType //MARK:- Proxy Subject internal lazy var didFinishPickingMediaWithInfoSubject = PublishSubject<[String : Any]>() internal lazy var didCancelSubject = PublishSubject<Void>() //MARK:- UIImagePickerControllerDelegate func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) { didFinishPickingMediaWithInfoSubject.onNext(info) didFinishPickingMediaWithInfoSubject.onCompleted() didCancelSubject.onCompleted() } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { didCancelSubject.onNext(()) didCancelSubject.onCompleted() didFinishPickingMediaWithInfoSubject.onCompleted() } //MARK:- Completed deinit { self.didFinishPickingMediaWithInfoSubject.onCompleted() self.didCancelSubject.onCompleted() } } CASE #5. Delegate ❗❗
  • 27. import UIKit import RxSwift import RxCocoa class ViewController: UIViewController { let disposeBag = DisposeBag() @IBOutlet weak var imageView: UIImageView! @IBAction func onLoadImage() { let picker = UIImagePickerController() present(picker, animated: true, completion: nil) picker.rx.didFinishPickingMediaWithInfo .map({ info -> UIImage? in info[UIImagePickerControllerOriginalImage] as? UIImage }) .bind(to: imageView.rx.image) .disposed(by: disposeBag) } } CASE #5. Delegate ❗❗
  • 29. UI
  • 30. Model.swift struct CalcData { let n1: Int? let n2: Int? let result: Int? } extension CalcData { static func empty() -> CalcData { return CalcData(n1: nil, n2: nil, result: nil) } }
  • 31. Context.swift (Business Logic) let i2s = { (i: Int?) in "(i ?? 0)" } let s2i = { (s: String?) in Int(s ?? "0") } class Context { let model = BehaviorRelay<CalcData>(value: CalcData.empty()) func doCalc(data: CalcData) -> CalcData { guard let n1 = data.n1 else { return data } guard let n2 = data.n2 else { return data } let result = n1 + n2 return CalcData(n1: n1, n2: n2, result: result) } }
  • 32. ViewController.swift class ViewController: UIViewController { @IBOutlet weak var n1Field: UITextField! // Input @IBOutlet weak var n2Field: UITextField! // Input @IBOutlet weak var resultLabel: UILabel! // Output @IBOutlet weak var calcButton: UIButton! // Event let context = Context() let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() bindUI() bindEvent() } func bindUI() { // ... } func bindEvent() { // … } }
  • 33. ViewController.swift class ViewController: UIViewController { func bindUI() { //n1 input n1Field.rx.text.map(s2i) .withLatestFrom(context.model) { (n1, dat) in CalcData(n1: n1, n2: dat.n2, result: dat.result) } .bind(to: context.model) .disposed(by: disposeBag) //n2 input n2Field.rx.text.map(s2i) .withLatestFrom(context.model) { (n2, dat) in CalcData(n1: dat.n1, n2: n2, result: dat.result) } .bind(to: context.model) .disposed(by: disposeBag) //result output viewModel.model.map({ $0.result }) .map(i2s) .bind(to: resultLabel.rx.text) .disposed(by: disposeBag) } func bindEvent() { //event calcButton.rx.tap .withLatestFrom(context.model) .map(context.doCalc) .bind(to: context.model) .disposed(by: disposeBag) } }
  • 34. Summary class ViewController: UIViewController { @IBOutlet weak var n1Field: UITextField! // Input @IBOutlet weak var n2Field: UITextField! // Input @IBOutlet weak var resultLabel: UILabel! // Output @IBOutlet weak var calcButton: UIButton! // Event let context = Context() let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() bindUI() bindEvent() } func bindUI() { //n1 input n1Field.rx.text.map(s2i) .withLatestFrom(context.model) { (n1, dat) in CalcData(n1: n1, n2: dat.n2, result: dat.result) } .bind(to: context.model) .disposed(by: disposeBag) //n2 input n2Field.rx.text.map(s2i) .withLatestFrom(context.model) { (n2, dat) in CalcData(n1: dat.n1, n2: n2, result: dat.result) } .bind(to: context.model) .disposed(by: disposeBag) //result output viewModel.model.map({ $0.result }) .map(i2s) .bind(to: resultLabel.rx.text) .disposed(by: disposeBag) } func bindEvent() { //event calcButton.rx.tap .withLatestFrom(context.model) .map(context.doCalc) .bind(to: context.model) .disposed(by: disposeBag) } } struct CalcData { let n1: Int? let n2: Int? let result: Int? } extension CalcData { static func empty() -> CalcData { return CalcData(n1: nil, n2: nil, result: nil) } } View ViewController Context Model let i2s = { (i: Int?) in "(i ?? 0)" } let s2i = { (s: String?) in Int(s ?? "0") } class Context { let model = BehaviorRelay<CalcData>(value: CalcData.empty()) func doCalc(data: CalcData) -> CalcData { guard let n1 = data.n1 else { return data } guard let n2 = data.n2 else { return data } let result = n1 + n2 return CalcData(n1: n1, n2: n2, result: result) } }
  • 35. struct CalcData { let n1: Int? let n2: Int? let result: Int? } extension CalcData { static func empty() -> CalcData { return CalcData(n1: nil, n2: nil, result: nil) } } View ViewController Context Model ViewModel Summary class ViewController: UIViewController { @IBOutlet weak var n1Field: UITextField! // Input @IBOutlet weak var n2Field: UITextField! // Input @IBOutlet weak var resultLabel: UILabel! // Output @IBOutlet weak var calcButton: UIButton! // Event let viewModel = ViewModel() let disposeBag = DisposeBag() override func viewDidLoad() { super.viewDidLoad() bindUI() bindEvent() } func bindUI() { //n1 input n1Field.rx.text.map(s2i) .withLatestFrom(viewModel.model) { (n1, dat) in CalcData(n1: n1, n2: dat.n2, result: dat.result) } .bind(to: viewModel.model) .disposed(by: disposeBag) //n2 input n2Field.rx.text.map(s2i) .withLatestFrom(viewModel.model) { (n2, dat) in CalcData(n1: dat.n1, n2: n2, result: dat.result) } .bind(to: viewModel.model) .disposed(by: disposeBag) //result output viewModel.model.map({ $0.result }) .map(i2s) .bind(to: resultLabel.rx.text) .disposed(by: disposeBag) } func bindEvent() { //event calcButton.rx.tap .withLatestFrom(viewModel.model) .map(viewModel.doCalc) .bind(to: viewModel.model) .disposed(by: disposeBag) } } let i2s = { (i: Int?) in "(i ?? 0)" } let s2i = { (s: String?) in Int(s ?? "0") } class ViewModel { let model = BehaviorRelay<CalcData>(value: CalcData.empty()) func doCalc(data: CalcData) -> CalcData { guard let n1 = data.n1 else { return data } guard let n2 = data.n2 else { return data } let result = n1 + n2 return CalcData(n1: n1, n2: n2, result: result) } }