Functional MVP with RXSwift Final - files.meetup.com

FUNCTIONAL PROGRAMMING 101 IN SWIFT ... Reactive Programming is about propagating changes while ... Functional_MVP_with_RXSwift_Final.key...

5 downloads 507 Views 3MB Size
@GIORGIONATILI

FUNCTIONAL MODEL VIEW PRESENTER WITH RXSWIFT

WHO AM I? ▸ Technical Leader and Agile Coach· ▸ Front-end Developer (test first!) ▸ Mobile Developer (iOS, Android and hybrid with Cordova) ▸ Engineering Lead in McGraw-Hill Education ▸ Founder of Mobile Tea and New England Software Engineers ▸ Droidcon Boston organizer

AGENDA ▸ The risk of bringing a POC in production ▸ POC + iOS + MVC, a dangerous combination ▸ Model View Presenter ▸ Functional programming in a nutshell ▸ FRP, what the hell ?!? ▸ Using FRP with MVP

THE RISK OF BRINGING A POC IN PRODUCTION

LONG STORY SHORT

POTENTIAL ISSUES ▸ Code contains dirty hacks ▸ Code is written using the most convenient technologies rather than the most appropriates ▸ Code performances are tailored for dev machines ▸ Tests barely exist ▸ The design is not scalable

TECHNICAL DEBT ▸ Gets quickly outside of control ▸ Nobody really track it ▸ There is no time allocation to address it ▸ The “we’ll fix it!” attitude start to be part of the team core values

IOS SPECIFIC ISSUES WHEN DEVELOPING A POC

THE MASSIVE VIEW CONTROLLER ▸ Controllers are not just controllers (the class name should suggest this) ▸ Controllers are to much tied in the view (aka IBAction and IBOutlet) ▸ UI element s are configured in the viewDidLoad ▸ Location services, map services, etc. are normally used in the controller (delegates arena)

LACK OF CODE DESIGN AND STRUCTURE ▸ Features get added without any plan ▸ Conversation is the only specification ▸ Code smells subtly start to be part of your day by day life

LET’S ADD A SHAKE FEATURE TO AN IMAGE class FoodImageView: UIImageView { func shake() {
 let animation = CABasicAnimation(keyPath: "position") animation.duration = 0.05 animation.repeatCount = 5 animation.autoreverses = true


}

}

animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 4.0, self.center.y)) animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 4.0, self.center.y)) layer.addAnimation(animation, forKey: "position")

NOTHING WRONG WITH THIS CODE, BUT… ▸ How add the same feature to other UIView subclasses? ▸ How communicate to others that the feature exists? ▸ How write semantic and clear code?

YOU CAN USE PROTOCOLS EXTENSIONS import UIKit protocol Shakeable { } extension Shakeable where Self: UIView {

}

func shake() { // implementation code }

PASSING DATA BETWEEN VIEWS override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) { if (segue.identifier == "segueTest") {

}

}

//Checking identifier is crucial as there might be multiple // segues attached to same view var detailVC = segue!.destinationViewController as DetailViewController; detailVC.toPass = Field.

▸ Not scalable ▸ Not maintainable

LET’S REVIEW THE CODE

MODEL VIEW PRESENTER

IN A NUTSHEL ▸ Greater separation of concerns ▸ Easier testability

HISTORY OF THE PATTERN ▸ Based on a generalization of the classic Smalltalk MVC ▸ Focused on decoupling the UI from the state of the app ▸ Designed to facilitate model composition and persistence ▸ Formalized by Taligent in 1996

MODEL VIEW PRESENTER IN DETAILS

THE VIEW IS PASSIVE ▸ A view react only to user inputs ▸ A view knows only how to render data ▸ A view can be easy tested

// MARK: - User interaction @IBAction func doLogin(sender: AnyObject) { } // MARK: - LoginView implementation func showErrorMessage(message: String) { }

CLEARLY DEFINED RELATIONSHIPS protocol LoginPresenter { var view: LoginView? { get set } var interactor: LoginInteractorInput? { get set } var router: LoginRouting? { get set }

}

/** * Communication VIEW -> PRESENTER * (how the presenter should be known from the view) */ func authenticate(username: String, _ password: String) func resetPassword(email: String)

protocol LoginView { var presenter: LoginPresenter? { get set }

}

/** * Communication PRESENTER -> VIEW * (how the view should be known from the presenter) */ func showErrorMessage(message: String) func showStatus(status: String) func enableInteraction(status: Bool)

TESTING THE VIEW (CONTROLLER) ▸ Indirectly testing the Presenter ▸ Testing the behavior of the UI

describe("Input field validation") { beforeEach { // Access the view to trigger LoginViewController.viewDidLoad(). let _ = viewController.view } it("It should render an error message when username and password are missing"){ viewController.login.sendActionsForControlEvents(UIControlEvents.TouchUpInside) expect(viewController.errorMessage.?.characters.count).toEventually(beGreaterThan(0), timeout: 1) } }

DEMO

FUNCTIONAL REACTIVE PROGRAMMING

FUNCTIONAL PROGRAMMING GLOSSARY ▸ Pure Functions ▸ When called with the same arguments, it always returns the same result ▸ There are no side effects, nothing mutated ▸ Lambdas are a shorthand version of a function declaration, often called “function literals” ▸ Currying allows you to partially supply parameters to a function so it can finish being filled in later.

IMPURE FUNCTIONS var a = 0 func f() -> Int {

}

a += 1 return a

let x = f() // 1 let y = f() // 2

FUNCTIONAL PROGRAMMING 101 IN SWIFT ▸ map, filter and reduce

let data = [1, 2, 3, 4, 5, 6] let sum = data.reduce(0, combine: {$0 + $1}) // 21 let tot = data.reduce(0, combine: +) // 21 let even = data.filter({$0 % 2 == 0}) // 2, 4, 6 let formatted = data.map {"\($0)$"} // 1$", "2$", "3$", "4$", "5$", ...

USING GENERICS IN SWIFT func quicksort(var elements: [T]) -> [T] { if elements.count > 1 {

} }

let pivot = elements.removeAtIndex(0) return quicksort(elements.filter { $0 <= pivot }) + [pivot] + quicksort(elements.filter { $0 > pivot })

return elements

FUNCTIONAL REACTIVE PROGRAMMING, WHAT THE HELL?!?

IN A NUTSHELL Reactive Programming is about propagating changes while declaring what to do to achieve a certain behavior when the given value changed, rather than how to achieve the desired behavior

▸ Stream ▸ Transformations ▸ Bindings

A SINGLE API TO RULE THEM ALL: REACTIVEX.IO

RXSWIFT - OBSERVABLES ▸ Observable is a generic class which conforms to ObservableType protocol ▸ Observable defines methods such as subscribe() and empty() ▸ Observables produce a stream of events

[1, 2, 3, 4, 5, 6] .toObservable() .subscribeNext { print($0) // 1 2 3 4 5 6 }

OBSERVABLE AND ARRAY [1, 2, 3, 4, 5, 6] .toObservable() .subscribeNext { print($0) // 1 2 3 4 5 6 }

OBSERVABLE OF MULTIPLE VALUES Observable.of("a", 2, 3, 4, 5) .subscribeNext { print($0) // a 1 2 3 4 5 } .dispose()

CLEAN ON DEINIT let disposeBag = DisposeBag() [1, 2, 3].toObservable() .subscribeNext { print($0) // 1 2 3 } .addDisposableTo(disposeBag)

MODIFIABLE OBSERVABLE SEQUENCE (BEHAVIORSUBJECT) ▸ It’s a sequence you can add new values on to ▸ The subscribers receive next events containing those newly added values ▸ BehaviorSubject is simply representing a value that is updated or changed over time let string = BehaviorSubject(value: "Hello") string.subscribe { print($0) // Next(Hello) Next(World!) } string.on(.Next("World!"))

RX VARIABLES ▸ A Variable is a wrapper around BehaviorSubject ▸ It exposes its BehaviorSubject’s observable sequence via the asObservable operator ▸ It is guaranteed to not emit error events, and it will emit a completed event when it’s about to be disposed of let number = Variable(1) number.asObservable() .subscribe { print($0) // Next(1) Next(12) Next(1234567) Completed } number.value = 12 number.value = 1_234_567

THAT’S JUST MAGIC!

NO, IT’S ALL ABOUT SIMPLICITY! class EmailValidationViewController: UIViewController { @IBOutlet weak var emailField: UIField! @IBOutlet weak var submitButton: UIButton! override func viewDidLoad() { super.viewDidLoad()

}

}

emailField.rx_ >- map (isEmail) >- submitButton.rx_subscribeEnabledTo

AND READABILITY! // MARK: - LoginInteractorInput implementation func aunthenticate(user: User) { services.auhtenticate(user).subscribe {

}

}

self.presenter?.loginDidSucceeded()

ADDITIONAL RESOURCES 
 AND LIBRARIES

RX EXTENSIONS (GITHUB.COM/RXSWIFTCOMMUNITY) ▸ RxCoreData
 RxSwift extensions for Core Data ▸ RxAlamofire
 Wrapper around the elegant HTTP networking in Swift Alamofire ▸ RxRealm
 Wrapper for Realm's collection types ▸ RxCoreMotion
 Provides an easy and straight-forward way to use Apple iOS _CoreMotion_ responses as Rx Observables. ▸ RxSegue

LINKS AND TUTORIALS ▸ Couple of misconceptions to play with https://sideeffects.xyz/ 2015/the-functional-reactive-misconception/ ▸ The simplicity of functional reactive programming syntax https://realm.io/news/nacho-soto-functional-reactiveprogramming/ ▸ ReSwift in a nutshell https://realm.io/news/benji-enczunidirectional-data-flow-swift ▸ Redux for Swift http://reswift.github.io/ReSwift/master/gettingstarted-guide.html

THANKS!