안드로이드 개발자를 위한 스위프트
TRANSCRIPT
let swift(16)
안드로이드�개발자를�위한�스위프트Swift & VIPER
osxdev.org / todait유병한
저는…
유병한�
현재�투데잇에서�iOS�개발을�담당(하고�있지만�사실은�Android�개발자�🙈🙉)�
이제는�iOS�개발자가�되고픔
아니�Android�개발자가�왜�iOS�개발을...?
회사에�iOS�개발자가�없어서...😭
Agenda
Swift를�사용하면서�Java에�비해�좋았던�부분(중요한�부분만�빠르게�알아봐요!)�
VIPER�아키텍처�적용�사례
let swift(16)
Android�개발을�하면서�아쉬웠던�부분
Java ☕
Null�Pointer�Exception�
Getter�/�Setter�
Anonymous�Class�
복잡한�계산로직을�자유자재로,�가독성이�좋게�짜기�힘듦
Activity (== ViewController)
너무나�많은�역할을�하는�Activity�
View,�Life�Cycle,�Navigation,�Networking,�DB,�Business�Logic,�…�
어떤�코드가�어디에�있는지�찾기�힘듦(==�뭣이�중헌지�모름)�
이러한�구조로는�테스트도�불가능
빚이되어�돌아오리…💸
Dirty�&�Fast�
모든�것이�빚으로�돌아옴�
버그라던지.�
버그라던지..�
버그라던지...
인간의욕심은끝이없고
같은실수를반복한다
같은�실수를�반복하지�말자!�🤔
let swift(16)
Swift
Swift
Optional Type
Property
Closure & Functional
Protocol & Extension
Java,�넘나�위험한�것!�⚠
Optional Type
Optional�Type이란?
타입�선언�시�변수에�nil이�저장될�수�있는지�여부를�명시
var nonOptional: Int = 0 nonOptional = nil // Compile Error!
var optional: Int? = 0 optional = nil // OK!
Optional Chaining
let viewController = UIViewController() print(viewController.navigationItem.backBarButtonItem?.title?.characters.count)
print(viewController.navigationItem.backBarButtonItem?.title ?? "Title")
Optional Binding - if let
if let viewController = viewController as? UITableViewController { viewController.tableView.reloadData() }
Optional Binding - if let
if let viewController = viewController as? UITableViewController, refreshControl = viewController.refreshControl { refreshControl.beginRefreshing() }
Optional Binding - if let
if let viewController = viewController as? UITableViewController, refreshControl = viewController.refreshControl where refreshControl.refreshing { refreshControl.beginRefreshing() }
Optional Binding - guard let
func signUp1(email: String?) { guard let email = email else { return } print(email.characters.count) }
Optional Binding - guard let
func signUp(email: String?, password: String?) { guard let email = email, password = password else { return } print("\(email) / \(password)") }
Optional Binding - guard let
func signUp(email: String?, password: String?) { guard let email = email, password = password where 8 <= password.characters.count else { return } print("\(email) / \(password)") }
Optional Binding - switch
let passwordText: String? = ""
switch(passwordText) { case .Some(let password) where 8 <= password.characters.count: print(password) case .Some: print("8자리�이상�입력해주세요") case .None: print("password is nil") }
이제는�안녕�Getter,�Setter�🙋
Property
Stored Property
struct Box { let width: Double let height: Double let depth: Double }
Stored Propertystruct Box { var width: Double { willSet { print("newValue: \(newValue)") } didSet { print("oldValue: \(oldValue)") volume = width * height * depth } } var height: Double { ... } var depth: Double { ... } var volume: Double }
Computed Property
struct Box { var width: Double var height: Double var depth: Double var volume: Double { get { return width * height * depth } set { print("newValue: \(newValue)") } } }
Computed Property
struct Box { var width: Double var height: Double var depth: Double var volume: Double { return width * height * depth } }
for,�if의�늪에서�나를�구해줘요!�😱
Closure & Functional
객체로서의�함수
함수의�인자로�함수를�넘길�수�있음�
함수의�결과로�함수를�반환할�수�있음
Closure�사용과�타입�추론
Closure는�이름이�없는�함수
var sum = [1, 23, 4, 32].reduce(0, combine: { (sum: Int, element: Int) -> Int in return sum + element })
Closure�사용과�타입�추론
타입이�명확한�경우�생략�가능
extension SequenceType { /// Returns the result of repeatedly calling `combine` with an /// accumulated value initialized to `initial` and each element of /// `self`, in turn, i.e. return /// `combine(combine(...combine(combine(initial, self[0]), /// self[1]),...self[count-2]), self[count-1])`. @warn_unused_result public func reduce<T>(initial: T, @noescape combine: (T, Self.Generator.Element) throws -> T) rethrows -> T }
var sum = [1, 23, 4, 32].reduce(0, combine: { (sum: Int, element: Int) -> Int in return sum + element })
Closure�사용과�타입�추론
sum = [1, 23, 4, 32].reduce(0, combine: { sum, element in return sum + element })
sum = [1, 23, 4, 32].reduce(0, combine: { return $0 + $1 })
sum = [1, 23, 4, 32].reduce(0, combine: { $0 + $1 })
sum = [1, 23, 4, 32].reduce(0) { $0 + $1 }
sum = [1, 23, 4, 32].reduce(0, combine: +)
투데잇에서의�Functional
통계�계산�로직에서�적극적으로�사용�
대용량�데이터를�다루는�것이�아닌�유저�한사람의�데이터�그�중에서도�특정�날짜의�데이터를�주로�다룸�
스레드�처리만�잘�해주면�사용자�체감�상�크게�문제될�상황은�없음�
퍼포먼스도�물론�중요하지만�오류�없고,�가독성이�높고,�수정이�용이한�코드가�필요�
map,�flatMap,�filter,�reduce,�contains�...
Protocol & Extension
Property&Method Requirements
Property�&�Method�에�대한�명세
protocol Flyable { var distance: Int { get set } func fly() }
struct SwiftBird: Flyable { var distance: Int func fly() { print("flying!") } }
Protocol with an Extension
Protocol은�Property를�가질�수�없지만�Method는�가질�수�있음
protocol ViewControllerProtocol: class { var loadingView: LoadingView { get set } }
extension ViewControllerProtocol where Self: UIViewController { func showLoadingView() { loadingView.showLoadingView(self) } func dismissLoadingView() { loadingView.dismissView() } }
let swift(16)
VIPER
좋은�Architecture는?
Distribution�
Testability�
Ease�of�Use
VIPER�구성요소
View Presenter
Router
Interactor Entity
DataService
Todait의�VIPER�구성요소
View Presenter
Router
Interactor
Entity
ViewModel
DataService
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
View
UIViewController에�해당�
View�Life�Cycleex)�viewDidLoad�함수가�호출되었음을�Presenter에�알림�
View�Eventex)�signUpButton이�클릭되었음을�Presenter에�알림�
View�Controlex)�titleLabel의�텍스트를�바꾸는�방법을�Presenter에�제공
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
Presenter
View와�Interactor의�중간�다리�역할�
View에�대한�비즈니스�로직�
Life�Cycle에�대한�처리�
View�Event에�대한�처리�
View�업데이트�
UIKit�Independent
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
Interactor
Data(Entity)에�대한�비즈니스�로직
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
Entity
네트워크,�DB�등의�데이터�모델�
Realm�Object,�NSUserDefuatls,�Json�Data�등�
Interactor에서�사용
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
Router
View�간의�전환�
VIPER�컴포넌트들의�DI(Dependency�Injection)를�담당
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
DataService
Network�요청,�DB�접근,�UserDefaults,�이미지�처리�등�반복되는�코드를�모듈화�
Interactor가�호출함
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
ViewModel
View에�나타나는�데이터와�1:1�매칭�
Entity가�어떻게�View에�바인딩될지�정의�
Interactor에서�생성되어�Presenter로�전달
VIPER Flow
가입하기�버튼을�눌려�회원가입을�완료하는�과정
DataService
presenter.onClickSignUpButton()
View Presenter
Router
Interactor
Entity
ViewModel
DataService
DataService
viewController.showLoadingAlert()
View Presenter
Router
Interactor
Entity
ViewModel
DataService
DataService
interactor.signUp(signUpViewModel)
View Presenter
Router
Interactor
Entity
ViewModel
DataService
DataService
signUpViewModel.checkValidation()
View Presenter
Router
Interactor
Entity
ViewModel
DataService
DataServic
serverService.signUp(email, password)
View Presenter
Router
Interactor
Entity
ViewModel
DataService
realmService.saveUserData()
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
presenter.onSignUpSucceed() / presenter.onSignUpFailed()
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
viewController.dismissLoadingAlert()
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
router.dismissViewController()
DataServiceView Presenter
Router
Interactor
Entity
ViewModel
DataService
VIPER with Swift - Protocol
protocol ViewControllerProtocol: class { var loadingAlert: LoadingAlert { get set } }
extension ViewControllerProtocol where Self: UIViewController { func showLoadingAlert() { loadingView.showLoadingAlert(self) } func dismissLoadingAlert() { loadingView.dismissAlert() } }
VIPER with Swift - Computed Property
protocol SignUpViewControllerProtocolForPresenter: class { var email: String? { get set } var password: String? { get set } }
extension SignUpViewController: SignUpViewControllerProtocolForPresenter { var email: String? { get { return emailTextField.text } set { emailTextField.text = newValue } } var password: String? { get { return passwordTextField.text } set { passwordTextField.text = newValue } } }
장점
하나의�화면에서�기능�단위로�코드를�명확하게�분리�
(물론�모든�컴포넌트에�대해�테스트를�하고�있지�않지만)�모든�컴포넌트에�대해�테스트�가능해지는�구조가됨�
(물론�혼자�개발하고�있지만)�분업이�훨씬�수월해짐
단점
간단한�화면,�간단한�기능에�대해서도�작성해야할�코드가�늘어남
Next Step
Code�Generator�
RxSwift,�KVO�
Android에도�적용
Swift로�iOS�앱�개발을�하면서�느낀�점
Swift�정말�최고!�
UI가�있는�어플리케이션�만든다는�관점에서�큰�동작�원리는�Android나�iOS나�비슷한�것�같음�
iOS에도�문제가�많다고�하지만�안드로이드에�비할바는�아닌�것�같음�
훨씬�평화로운�iOS!�피쓰~�🕊
투데잇의 성장을 함께할 당신을 기다리고 있습니다.
i O S 개 발 을 박 ♥ 살 내 주 실 당 신 !
let swift(16)