Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Carthage and ReactiveCocoa

7ddcca09c00a2744b983974225447d19?s=47 Sho Ikeda
April 13, 2016

Carthage and ReactiveCocoa

Reactive Swift Meetupでの発表資料です

http://wantedly.connpass.com/event/29039/

7ddcca09c00a2744b983974225447d19?s=128

Sho Ikeda

April 13, 2016
Tweet

Transcript

  1. Carthage and ReactiveCocoa @ikesyo Reactive Swift Meetup, 2016-04-13 Wed @wantedly

    #Reactive_Swift_Meetup
  2. @ikesyo • ͍͚͠ΐʔʗ஑ా ᠳ • Reactiveਓࡐ • ͸ͯͳ@ژ౎ • https://twitter.com/ikesyo

    • https://github.com/ikesyo
  3. None
  4. ✨ Contributions ✨ • ReactiveCocoa • Carthage (Commandant, ReactiveTask) •

    Result • Himotoki • APIKit
  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. Carthage and ReactiveCocoa

  12. • 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
  13. • Promise, ColdSignalͳͲࠓ͸ͳ͖ܕୡ • Signal/SignalProducer΁ͷมભ • Carthageͷ։ൃΛ௨ͯ͠RAC 3.0ͷAPIΛςετɺચ࿅ 1 1

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

    • ! Callback Hell • flatMap • flatten ίʔυϕʔεશମͰ࢖͏ͷ͕ޮՌతʢϩοΫΠϯ !ʣ
  15. 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 ... }))
  16. // 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>
  17. // 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> }
  18. ඇಉظॲཧͷఆܕԽ • ಉظతAPI • ςετɺCLIϓϩάϥϜ • RxSwift: RxBlocking // SignalProducer

    func first() -> Result<Value, Error>? func single() -> Result<Value, Error>? func last() -> Result<Value, Error>? func wait() -> Result<(), Error>
  19. // 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> }
  20. // 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() } }
  21. ಉظॲཧͷϥοϓ • ಉظॲཧ݁ՌͷϋϯυϦϯά΋ඇಉظॲཧͷ৔߹ͱڞ௨Խ • ಉظͱඇಉظΛҙࣝ͠ͳ͍ • ಺෦ॲཧ͕ಉظ͔ΒඇಉظʹมΘͬͯ΋ݺͼग़͠ଆʹ͸Ө ڹ͕ͳ͍ • ඇಉظॲཧͱͷ߹੒

  22. // 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)) } } }
  23. // 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) }
  24. ܕ෇͚͞ΕͨΤϥʔ • Signal<Value, Error>, SignalProducer<Value, Error> • Result<Value, Error> ͔Βͷੜ੒:

    attempt, attemptMap • throwsΛѻ͏࣌͸໌നͳܕ΁ͷΩϟετɺ΋͘͠͸NSErrorͱ ͯ͠ѻ͏ʢResultͷAPIʣ • RAC: Atomic, MutablePropertyΛআ͍ͯthrows͸࢖ΘΕ͍ͯͳ ͍ • Carthage: throws͸࢖ΘΕ͍ͯͳ͍
  25. // 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) }
  26. ܕ෇͚͞ΕͨΤϥʔ • Result.NoError: Πϯελϯεੜ੒Ͱ͖ͳ͍Τϥʔܕ • ࣦഊ͢ΔՄೳੑ͕ͳ͍͜ͱΛAPIͷγάωνϟʔͱͯ͠දݱͰ͖ Δ • <~ΦϖϨʔλʔʹΑΔMutableProperty΁ͷSignal, SignalProducerͷόΠϯσΟϯάͷͨΊʹ͸NoErrorͰͳ͚Ε

    ͹ͳΒͳ͍ 2 2 https://github.com/ReactiveCocoa/ReactiveCocoa/blob/v4.1.0/ReactiveCocoa/Swift/Property.swift#L241-L302
  27. // 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 } } }
  28. ͓·͚

  29. thoughtbot/Delta

  30. Delta • https://github.com/thoughtbot/Delta • ReactiveCocoa, RxSwiftͱͷ૊Έ߹Θ͕ͤͰ͖Δʢਪ঑͍ͯ͠Δʣ Managing state is hard.

    Delta aims to make it simple. Delta takes an app that has custom state management spread throughout all the VCs and simplifies it by providing a simple interface to change state and subscribe to its changes. It can be used standalone or with your choice of reactive framework plugged in. We recommend using a reactive framework to get the most value.
  31. Delta public protocol ObservablePropertyType { typealias ValueType var value: ValueType

    { get set } } public protocol StoreType { typealias ObservableState: ObservablePropertyType var state: ObservableState { get set } mutating func dispatch <Action: ActionType where Action.StateValueType == ObservableState.ValueType>(action: Action) func dispatch <DynamicAction: DynamicActionType>(action: DynamicAction) -> DynamicAction.ResponseType }
  32. Delta with RAC extension MutableProperty: ObservablePropertyType { typealias ValueType =

    Value } struct AppState { let userId: MutableProperty<Int?>(.None) } struct Store: StoreType { var state: MutableProperty<AppState> } let initialState = AppState() var store = Store(state: MutableProperty(initialState))
  33. Delta with RxSwift extension Variable: ObservablePropertyType { typealias ValueType =

    Element } struct AppState { let userId: Variable<Int?>(.None) } struct Store: StoreType { var state: Variable<AppState> } let initialState = AppState() var store = Store(state: Variable(initialState))
  34. ࢀߟURL • 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 • γϯϓϧͳύοέʔδ؅ཧπʔϧ Carthage - Realm is a mobile database: a replacement for SQLite & Core Data • RxͷHotͱColdʹ͍ͭͯ - Qiita • Reactivity • Delta/reactive-extensions.md at v0.1.0 · thoughtbot/Delta
  35. !" Happy Reactive Coding!! "!

  36. Thank you❗"