SlideShare une entreprise Scribd logo
1  sur  36
Télécharger pour lire hors ligne
.(
) . ..)
!
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

Contenu connexe

Tendances

게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
Amazon Web Services Korea
 
AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018
AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018
AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018
Amazon Web Services Korea
 

Tendances (20)

AWS Aurora 운영사례 (by 배은미)
AWS Aurora 운영사례 (by 배은미)AWS Aurora 운영사례 (by 배은미)
AWS Aurora 운영사례 (by 배은미)
 
게임사를 위한 Amazon GameLift 세션 - 이정훈, AWS 솔루션즈 아키텍트
게임사를 위한 Amazon GameLift 세션 - 이정훈, AWS 솔루션즈 아키텍트게임사를 위한 Amazon GameLift 세션 - 이정훈, AWS 솔루션즈 아키텍트
게임사를 위한 Amazon GameLift 세션 - 이정훈, AWS 솔루션즈 아키텍트
 
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
게임서비스를 위한 ElastiCache 활용 전략 :: 구승모 솔루션즈 아키텍트 :: Gaming on AWS 2016
 
[2019] PAYCO 쇼핑 마이크로서비스 아키텍처(MSA) 전환기
[2019] PAYCO 쇼핑 마이크로서비스 아키텍처(MSA) 전환기[2019] PAYCO 쇼핑 마이크로서비스 아키텍처(MSA) 전환기
[2019] PAYCO 쇼핑 마이크로서비스 아키텍처(MSA) 전환기
 
20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP20220716_만들면서 느껴보는 POP
20220716_만들면서 느껴보는 POP
 
7. 게임 스트리밍 서비스를 위한 아키텍처 - 언리얼 엔진을 중심으로! [레벨 300] - 발표자: 하흥수, 솔루션즈 아키텍트, AWS :...
7.	게임 스트리밍 서비스를 위한 아키텍처 - 언리얼 엔진을 중심으로! [레벨 300] - 발표자: 하흥수, 솔루션즈 아키텍트, AWS :...7.	게임 스트리밍 서비스를 위한 아키텍처 - 언리얼 엔진을 중심으로! [레벨 300] - 발표자: 하흥수, 솔루션즈 아키텍트, AWS :...
7. 게임 스트리밍 서비스를 위한 아키텍처 - 언리얼 엔진을 중심으로! [레벨 300] - 발표자: 하흥수, 솔루션즈 아키텍트, AWS :...
 
Domain-Driven-Design 정복기 2탄
Domain-Driven-Design 정복기 2탄Domain-Driven-Design 정복기 2탄
Domain-Driven-Design 정복기 2탄
 
AWS Personalize 중심으로 살펴본 추천 시스템 원리와 구축
AWS Personalize 중심으로 살펴본 추천 시스템 원리와 구축AWS Personalize 중심으로 살펴본 추천 시스템 원리와 구축
AWS Personalize 중심으로 살펴본 추천 시스템 원리와 구축
 
Java EE 8先取り!MVC 1.0入門 [EDR2対応版] 2015-10-10更新
Java EE 8先取り!MVC 1.0入門 [EDR2対応版] 2015-10-10更新Java EE 8先取り!MVC 1.0入門 [EDR2対応版] 2015-10-10更新
Java EE 8先取り!MVC 1.0入門 [EDR2対応版] 2015-10-10更新
 
PUBG: Battlegrounds 라이브 서비스 EKS 전환 사례 공유 [크래프톤 - 레벨 300] - 발표자: 김정헌, PUBG Dev...
PUBG: Battlegrounds 라이브 서비스 EKS 전환 사례 공유 [크래프톤 - 레벨 300] - 발표자: 김정헌, PUBG Dev...PUBG: Battlegrounds 라이브 서비스 EKS 전환 사례 공유 [크래프톤 - 레벨 300] - 발표자: 김정헌, PUBG Dev...
PUBG: Battlegrounds 라이브 서비스 EKS 전환 사례 공유 [크래프톤 - 레벨 300] - 발표자: 김정헌, PUBG Dev...
 
Presentation1.pptx
Presentation1.pptxPresentation1.pptx
Presentation1.pptx
 
스토리포인트가이드
스토리포인트가이드스토리포인트가이드
스토리포인트가이드
 
AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018
AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018
AWS KMS를 활용하여 안전한 AWS 환경을 구축하기 위한 전략::임기성::AWS Summit Seoul 2018
 
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdfInjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
InjectionIII의 Hot Reload를 이용하여 앱 개발을 좀 더 편하게 하기.pdf
 
Java persistence api 2.1
Java persistence api 2.1Java persistence api 2.1
Java persistence api 2.1
 
Cucumber and Spock Primer
Cucumber and Spock PrimerCucumber and Spock Primer
Cucumber and Spock Primer
 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
 
쉽고 강력한 모바일 백엔드 Parse-server
쉽고 강력한 모바일 백엔드 Parse-server쉽고 강력한 모바일 백엔드 Parse-server
쉽고 강력한 모바일 백엔드 Parse-server
 
Clean Architecture Applications in Python
Clean Architecture Applications in PythonClean Architecture Applications in Python
Clean Architecture Applications in Python
 
Amazon OpenSearch Deep dive - 내부구조, 성능최적화 그리고 스케일링
Amazon OpenSearch Deep dive - 내부구조, 성능최적화 그리고 스케일링Amazon OpenSearch Deep dive - 내부구조, 성능최적화 그리고 스케일링
Amazon OpenSearch Deep dive - 내부구조, 성능최적화 그리고 스케일링
 

Similaire à 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
 

Similaire à 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
 

Plus de Chiwon Song

Plus de Chiwon Song (20)

20240330_고급진 코드를 위한 exception 다루기
20240330_고급진 코드를 위한 exception 다루기20240330_고급진 코드를 위한 exception 다루기
20240330_고급진 코드를 위한 exception 다루기
 
요즘 유행하는 AI 나도 해보자 (feat. CoreML)
요즘 유행하는 AI 나도 해보자 (feat. CoreML)요즘 유행하는 AI 나도 해보자 (feat. CoreML)
요즘 유행하는 AI 나도 해보자 (feat. CoreML)
 
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] 아두이노 활용 실습
 
[3] 프로세싱과 아두이노
[3] 프로세싱과 아두이노[3] 프로세싱과 아두이노
[3] 프로세싱과 아두이노
 

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Dernier (20)

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
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
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
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Evaluating the top large language models.pdf
Evaluating the top large language models.pdfEvaluating the top large language models.pdf
Evaluating the top large language models.pdf
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
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...
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
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
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
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...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 

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