SlideShare una empresa de Scribd logo
1 de 36
Descargar para leer sin conexión
.(
) . ..)
!
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

Más contenido relacionado

La actualidad más candente

La actualidad más candente (20)

Angular Observables & RxJS Introduction
Angular Observables & RxJS IntroductionAngular Observables & RxJS Introduction
Angular Observables & RxJS Introduction
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA
 
DSC program.pdf
DSC program.pdfDSC program.pdf
DSC program.pdf
 
Redux Toolkit - Quick Intro - 2022
Redux Toolkit - Quick Intro - 2022Redux Toolkit - Quick Intro - 2022
Redux Toolkit - Quick Intro - 2022
 
The Power Of Composition (DotNext 2019)
The Power Of Composition (DotNext 2019)The Power Of Composition (DotNext 2019)
The Power Of Composition (DotNext 2019)
 
An Introduction to Redux
An Introduction to ReduxAn Introduction to Redux
An Introduction to Redux
 
Struts2
Struts2Struts2
Struts2
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with Examples
 
Redux essentials
Redux essentialsRedux essentials
Redux essentials
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 
ソフトウェア開発とパタン・ランゲージ再考
ソフトウェア開発とパタン・ランゲージ再考ソフトウェア開発とパタン・ランゲージ再考
ソフトウェア開発とパタン・ランゲージ再考
 
Angular and The Case for RxJS
Angular and The Case for RxJSAngular and The Case for RxJS
Angular and The Case for RxJS
 
PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)PHP 良好實踐 (Best Practice)
PHP 良好實踐 (Best Practice)
 
react redux.pdf
react redux.pdfreact redux.pdf
react redux.pdf
 
Spring boot - an introduction
Spring boot - an introductionSpring boot - an introduction
Spring boot - an introduction
 
RDFチェックツール「rdflint」のご紹介
RDFチェックツール「rdflint」のご紹介RDFチェックツール「rdflint」のご紹介
RDFチェックツール「rdflint」のご紹介
 
RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)RxJS Operators - Real World Use Cases (FULL VERSION)
RxJS Operators - Real World Use Cases (FULL VERSION)
 
SPAのルーティングの話
SPAのルーティングの話SPAのルーティングの話
SPAのルーティングの話
 
1. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES61. Arrow Functions | JavaScript | ES6
1. Arrow Functions | JavaScript | ES6
 
今日こそ理解するHot / Cold @社内RxSwift勉強会
今日こそ理解するHot / Cold @社内RxSwift勉強会今日こそ理解するHot / Cold @社内RxSwift勉強会
今日こそ理解するHot / Cold @社内RxSwift勉強会
 

Similar a 20180721 code defragment

ViewController.swift Calculatorimport Cocoaimport UIKit.pdf
 ViewController.swift Calculatorimport Cocoaimport UIKit.pdf ViewController.swift Calculatorimport Cocoaimport UIKit.pdf
ViewController.swift Calculatorimport Cocoaimport UIKit.pdf
arasanlethers
 

Similar a 20180721 code defragment (20)

That’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your BatteryThat’s My App - Running in Your Background - Draining Your Battery
That’s My App - Running in Your Background - Draining Your Battery
 
ViewController.swift Calculatorimport Cocoaimport UIKit.pdf
 ViewController.swift Calculatorimport Cocoaimport UIKit.pdf ViewController.swift Calculatorimport Cocoaimport UIKit.pdf
ViewController.swift Calculatorimport Cocoaimport UIKit.pdf
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
What's new in iOS9
What's new in iOS9What's new in iOS9
What's new in iOS9
 
iOS
iOSiOS
iOS
 
How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Get the Most Out of iOS 11 with Visual Studio Tools for Xamarin
Get the Most Out of iOS 11 with Visual Studio Tools for XamarinGet the Most Out of iOS 11 with Visual Studio Tools for Xamarin
Get the Most Out of iOS 11 with Visual Studio Tools for Xamarin
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
Advancing the UI — Part 1: Look, Motion, and Gestures
Advancing the UI — Part 1: Look, Motion, and GesturesAdvancing the UI — Part 1: Look, Motion, and Gestures
Advancing the UI — Part 1: Look, Motion, and Gestures
 
303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code303 TANSTAAFL: Using Open Source iPhone UI Code
303 TANSTAAFL: Using Open Source iPhone UI Code
 
Blending Culture in Twitter Client
Blending Culture in Twitter ClientBlending Culture in Twitter Client
Blending Culture in Twitter Client
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
 
The zen of async: Best practices for best performance
The zen of async: Best practices for best performanceThe zen of async: Best practices for best performance
The zen of async: Best practices for best performance
 
Introduction to kotlin
Introduction to kotlinIntroduction to kotlin
Introduction to kotlin
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
 
Swift ui userinput
Swift ui userinputSwift ui userinput
Swift ui userinput
 

Más de Chiwon Song

Más de Chiwon Song (20)

20240330_고급진 코드를 위한 exception 다루기
20240330_고급진 코드를 위한 exception 다루기20240330_고급진 코드를 위한 exception 다루기
20240330_고급진 코드를 위한 exception 다루기
 
요즘 유행하는 AI 나도 해보자 (feat. CoreML)
요즘 유행하는 AI 나도 해보자 (feat. CoreML)요즘 유행하는 AI 나도 해보자 (feat. CoreML)
요즘 유행하는 AI 나도 해보자 (feat. CoreML)
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP
 
20210812 컴퓨터는 어떻게 동작하는가?
20210812 컴퓨터는 어떻게 동작하는가?20210812 컴퓨터는 어떻게 동작하는가?
20210812 컴퓨터는 어떻게 동작하는가?
 
20201121 코드 삼분지계
20201121 코드 삼분지계20201121 코드 삼분지계
20201121 코드 삼분지계
 
20200815 inversions
20200815 inversions20200815 inversions
20200815 inversions
 
20191116 custom operators in swift
20191116 custom operators in swift20191116 custom operators in swift
20191116 custom operators in swift
 
[20190601] 직업훈련교사_수업의실행_교안
[20190601] 직업훈련교사_수업의실행_교안[20190601] 직업훈련교사_수업의실행_교안
[20190601] 직업훈련교사_수업의실행_교안
 
[20190601] 직업훈련교사_수업의실행
[20190601] 직업훈련교사_수업의실행[20190601] 직업훈련교사_수업의실행
[20190601] 직업훈련교사_수업의실행
 
20190330 immutable data
20190330 immutable data20190330 immutable data
20190330 immutable data
 
20190306 만들면서 배우는 IoT / IoT의 이해
20190306 만들면서 배우는 IoT / IoT의 이해20190306 만들면서 배우는 IoT / IoT의 이해
20190306 만들면서 배우는 IoT / IoT의 이해
 
20181020 advanced higher-order function
20181020 advanced higher-order function20181020 advanced higher-order function
20181020 advanced higher-order function
 
20180310 functional programming
20180310 functional programming20180310 functional programming
20180310 functional programming
 
20171104 FRP 패러다임
20171104 FRP 패러다임20171104 FRP 패러다임
20171104 FRP 패러다임
 
스크래치로 시작하는 코딩
스크래치로 시작하는 코딩스크래치로 시작하는 코딩
스크래치로 시작하는 코딩
 
메이커운동과 아두이노
메이커운동과 아두이노메이커운동과 아두이노
메이커운동과 아두이노
 
아두이노 RC카 만들기
아두이노 RC카 만들기아두이노 RC카 만들기
아두이노 RC카 만들기
 
[5] 아두이노로 만드는 IoT
[5] 아두이노로 만드는 IoT[5] 아두이노로 만드는 IoT
[5] 아두이노로 만드는 IoT
 
[4] 아두이노와 인터넷
[4] 아두이노와 인터넷[4] 아두이노와 인터넷
[4] 아두이노와 인터넷
 
[2] 아두이노 활용 실습
[2] 아두이노 활용 실습[2] 아두이노 활용 실습
[2] 아두이노 활용 실습
 

Último

Último (20)

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 

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) } }