ReactiveCocoa and its Recent State

ReactiveCocoa and its Recent State

Realm meetup #15での発表資料です #realm_jp

http://realm.connpass.com/event/31968/

7ddcca09c00a2744b983974225447d19?s=128

Sho Ikeda

May 27, 2016
Tweet

Transcript

  1. ReactiveCocoa and its Recent State @ikesyo Realm meetup #15, 2016-05-27

    Fri @Sansan #realm_jp
  2. @ikesyo • ͍͚͠ΐʔʗ஑ా ᠳ • ͸ͯͳ@ژ౎ • https://twitter.com/ikesyo • https://github.com/ikesyo

  3. ✨ Contributions ✨ • ReactiveCocoa • Carthage (Commandant, ReactiveTask) •

    Result • Himotoki • APIKit
  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. None
  12. Recent State of ReactiveCocoa

  13. New Member • @RuiAAPeres Issues • Reduced from around 250

    to 80+ (as of 2016-04-26) • 28 issues as of 2016-05-26
  14. None
  15. Moving Rex to the ReactiveCocoa org #2790 1. Discoverability 2.

    Credibility 3. Expansion 4. Easier to manage
  16. Split Objective-C and Swift APIs going forward #2807 Correct me

    if I'm wrong, but maintaining both an Objective-C and Swift API for RAC is a bit of a burden, right? Perhaps going forward it's worth thinking about slimming ReactiveCocoa down to just support Swift. I'm sure you guys feel the pull of writing things in Swift as much as I do ! Obviously there's a lot of people reliant on the Objective-C APIs (myself included, for some rather large projects) but if there's support for this idea, splitting the language support up into separate repos/projects might be a good first step? What do you think?
  17. Remove Objective-C APIs #2830 As discussed in #2807, we will

    move the Objective-C APIs to a separate repo and also move the Obj-C/Swift bridging to a separate repo. This will need to happen as part of the 5.0 release because it is a breaking change.
  18. We are heading to ... • ReactiveCocoa: ReactiveSwift + UI

    Bindings • ReactiveSwift: Swift only • ReactiveObjC: Objective-C only • ReactiveObjCBridge: Swift <-> ObjC Bridge
  19. Community • Community is really important ! • https://github.com/RACCommunity •

    ReactiveCocoa Community Projects • Inspired by the RxSwift Community • Let's expand the ecosystem! • Slack channel: #reactivecocoa • https://reactivex.slack.com/ • Contact to @ReactiveCocoa if you want to join!
  20. Utilities / Examples • RACurated: Curated list of ReactiveCocoa projects

    • Carthage/Carthage ! • MailOnline/Reactor • ... • RACCommunity/RACNest • inamiy/ReactiveCocoaCatalog
  21. Reactive Swift Meetup (2016-04-13) • http://wantedly.connpass.com/event/29039/ • ReactiveCocoaೖ໳ by @Nirma

    • Getting Started with ReactiveCocoa v4.1.0 by @inamiy • ...
  22. None
  23. Carthage and ReactiveCocoa

  24. None
  25. Streams of Events over time

  26. • Create enclosing xcworkspace, add ReactiveCocoa by jspahrsummers · Pull

    Request #24 · Carthage/Carthage • Create Task module for shelling out by jspahrsummers · Pull Request #25 · Carthage/Carthage • Build project directories using xcodebuild by jspahrsummers · Pull Request #26 · Carthage/Carthage • Update to the new RAC.Swift API by jspahrsummers · Pull Request #31 · Carthage/Carthage • Upgrade dependencies by jspahrsummers · Pull Request #368 · Carthage/Carthage
  27. • Promise, ColdSignalͳͲࠓ͸ͳ͖ܕୡ • Signal/SignalProducer΁ͷมભ • Carthageͷ։ൃΛ௨ͯ͠RAC 3.0ͷAPIΛςετɺચ࿅ 1 1

    γϯϓϧͳύοέʔδ؅ཧπʔϧ Carthage - Realm is a mobile database: a replacement for SQLite & Core Data
  28. ඇಉظॲཧͷఆܕԽ • APIຖʹελΠϧ͕ҟͳͬͯ໎Θͳ͍Α͏ʹ͢Δ • delegate, target-action, callback(Ҿ਺΋༷ʑ), ... • ඇಉظॲཧͷνΣʔϯ

    • ! Callback Hell • flatMap • flatten ίʔυϕʔεશମͰ࢖͏ͷ͕ޮՌతʢϩοΫΠϯ !ʣ
  29. signal.observe { event in switch event { ... } }

    // observeNext, observeCompleted, ... producer.start { event in switch event { ... } } // startWithNext, startWithCompleted, ... aProducer .flatMap(.Merge) { self.b($0) } .flatMap(.Concat) { self.c($0) } .start(Observer(completed: { ... }, next: { value in ... }))
  30. // ReactiveTask public struct Task { public var launchPath: String

    public var arguments: [String] public var workingDirectoryPath: String? public var environment: [String: String]? public init( _ launchPath: String, arguments: [String] = [], workingDirectoryPath: String? = nil, environment: [String: String]? = nil) { self.launchPath = launchPath self.arguments = arguments self.workingDirectoryPath = workingDirectoryPath self.environment = environment } } public func launchTask( task: Task, standardInput: SignalProducer<NSData, NoError>? = nil ) -> SignalProducer<TaskEvent<NSData>, TaskError>
  31. // Tentacle public final class Client { public func releasesInRepository(

    repository: Repository, page: UInt = 1, perPage: UInt = 30 ) -> SignalProducer<(Response, [Release]), Error> public func releaseForTag( tag: String, inRepository repository: Repository ) -> SignalProducer<(Response, Release), Error> public func downloadAsset( asset: Release.Asset ) -> SignalProducer<NSURL, Error> }
  32. ඇಉظॲཧͷఆܕԽ • ಉظతAPI • ςετɺCLIϓϩάϥϜ • RxSwift: RxBlocking // SignalProducer

    func first() -> Result<Value, Error>? func single() -> Result<Value, Error>? func last() -> Result<Value, Error>? func wait() -> Result<(), Error>
  33. // Commandant public protocol CommandType { associatedtype Options: OptionsType associatedtype

    ClientError: ClientErrorType = Options.ClientError var verb: String { get } var function: String { get } func run(options: Options) -> Result<(), ClientError> }
  34. // CopyFrameworks.swift public struct CopyFrameworksCommand: CommandType { public func run(options:

    NoOptions<CarthageError>) -> Result<(), CarthageError> { return inputFiles() .flatMap(.Concat) { frameworkPath -> SignalProducer<(), CarthageError> in let frameworkName = (frameworkPath as NSString).lastPathComponent let source = Result(..) let target = frameworksFolder().map { $0.URLByAppendingPathComponent(frameworkName, isDirectory: true) } return combineLatest( SignalProducer(result: source), SignalProducer(result: target), SignalProducer(result: validArchitectures()) ) .flatMap(.Merge) { (source, target, validArchitectures) -> SignalProducer<(), CarthageError> in let copyFrameworks = copyFramework(...) let copydSYMs = copyDebugSymbolsForFramework(...) return combineLatest(copyFrameworks, copydSYMs) .then(.empty) } } .waitOnCommand() } }
  35. ಉظॲཧͷϥοϓ • ಉظॲཧ݁ՌͷϋϯυϦϯά΋ඇಉظॲཧͷ৔߹ͱڞ௨Խ • ಉظͱඇಉظΛҙࣝ͠ͳ͍ • ಺෦ॲཧ͕ಉظ͔ΒඇಉظʹมΘͬͯ΋ݺͼग़͠ଆʹ͸Ө ڹ͕ͳ͍ • ඇಉظॲཧͱͷ߹੒

  36. // Project.swift public func loadResolvedCartfile() -> SignalProducer<ResolvedCartfile, CarthageError> { return

    SignalProducer.attempt { do { let resolvedCartfileContents = try NSString( contentsOfURL: self.resolvedCartfileURL, encoding: NSUTF8StringEncoding ) return ResolvedCartfile.fromString(resolvedCartfileContents as String) } catch let error as NSError { return .Failure(.ReadFailed(self.resolvedCartfileURL, error)) } } }
  37. // Project.swift public func updatedResolvedCartfile... { let resolver = Resolver(...)

    let resolvedCartfile: SignalProducer<ResolvedCartfile?, CarthageError> resolvedCartfile = loadResolvedCartfile() .map(Optional.init) .flatMapError { _ in .init(value: nil) } return zip(loadCombinedCartfile(), resolvedCartfile) .flatMap(.Merge) { cartfile, resolvedCartfile in return resolver.resolveDependenciesInCartfile( cartfile, lastResolved: resolvedCartfile, dependenciesToUpdate: dependenciesToUpdate ) } .collect() .map(ResolvedCartfile.init) }
  38. ܕ෇͚͞ΕͨΤϥʔ • Signal<Value, Error>, SignalProducer<Value, Error> • Result<Value, Error> ͔Βͷੜ੒:

    attempt, attemptMap • throwsΛѻ͏࣌͸໌നͳܕ΁ͷΩϟετɺ΋͘͠͸NSErrorͱ ͯ͠ѻ͏ʢResultͷAPIʣ • RAC: Atomic, MutablePropertyΛআ͍ͯthrows͸࢖ΘΕ͍ͯͳ ͍ • Carthage: throws͸࢖ΘΕ͍ͯͳ͍
  39. // Errors.swift public enum CarthageError: ErrorType, Equatable { ... ///

    Failed to check out a repository. case RepositoryCheckoutFailed(workingDirectoryURL: NSURL, reason: String, underlyingError: NSError?) /// Failed to read a file or directory at the given URL. case ReadFailed(NSURL, NSError?) /// Failed to write a file or directory at the given URL. case WriteFailed(NSURL, NSError?) ... /// A request to the GitHub API failed. case GitHubAPIRequestFailed(Client.Error) /// An error occurred while shelling out. case TaskError(ReactiveTask.TaskError) }
  40. ܕ෇͚͞ΕͨΤϥʔ • Result.NoError: Πϯελϯεੜ੒Ͱ͖ͳ͍Τϥʔܕ • ࣦഊ͢ΔՄೳੑ͕ͳ͍͜ͱΛAPIͷγάωνϟʔͱͯ͠දݱͰ͖ Δ • <~ΦϖϨʔλʔʹΑΔMutableProperty΁ͷSignal, SignalProducerͷόΠϯσΟϯάͷͨΊʹ͸NoErrorͰͳ͚Ε

    ͹ͳΒͳ͍ 2 2 https://github.com/ReactiveCocoa/ReactiveCocoa/blob/v4.1.0/ReactiveCocoa/Swift/Property.swift#L241-L302
  41. // Git.swift internal func branchExistsInRepository(repositoryFileURL: NSURL, pattern: String) -> SignalProducer<Bool,

    NoError> { return ensureDirectoryExistsAtURL(repositoryFileURL) .succeeded() .flatMap(.Concat) { exists -> SignalProducer<Bool, NoError> in if !exists { return .init(value: false) } return zip( launchGitTask( [ "show-ref", pattern ], repositoryFileURL: repositoryFileURL ).succeeded(), launchGitTask( [ "show-ref", "--tags", pattern ], repositoryFileURL: repositoryFileURL ).succeeded() ) .map { branch, tag in return branch && !tag } } }
  42. ࢀߟURL • Visual Identity Refresh for 4.0 · Issue #2190

    · ReactiveCocoa/ReactiveCocoa • ReactiveCocoa for a better world • [ON HOLD] ReactiveCocoa 3.0 by jspahrsummers · Pull Request #966 · ReactiveCocoa/ReactiveCocoa • jspahrsummers/RxSwift: Proof-of-concept for implementing Rx primitives in Swift • Release v3.0 · ReactiveCocoa/ReactiveCocoa • Release v4.0 · ReactiveCocoa/ReactiveCocoa • γϯϓϧͳύοέʔδ؅ཧπʔϧ Carthage - Realm is a mobile database: a replacement for SQLite & Core Data • The introduction to Reactive Programming you've been missing • ʲ຋༁ʳ͋ͳ͕ͨٻΊ͍ͯͨϦΞΫςΟϒϓϩάϥϛϯάೖ໳ • RxͷHotͱColdʹ͍ͭͯ - Qiita
  43. !" Happy Reactive Coding!! "!

  44. Thank you❗"