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

Carthage and ReactiveCocoa

Sho Ikeda
April 13, 2016

Carthage and ReactiveCocoa

Reactive Swift Meetupでの発表資料です

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

Sho Ikeda

April 13, 2016
Tweet

More Decks by Sho Ikeda

Other Decks in Programming

Transcript

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

    γϯϓϧͳύοέʔδ؅ཧπʔϧ Carthage - Realm is a mobile database: a replacement for SQLite & Core Data
  3. 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 ... }))
  4. // 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>
  5. // 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> }
  6. ඇಉظॲཧͷఆܕԽ • ಉظతAPI • ςετɺCLIϓϩάϥϜ • RxSwift: RxBlocking // SignalProducer

    func first() -> Result<Value, Error>? func single() -> Result<Value, Error>? func last() -> Result<Value, Error>? func wait() -> Result<(), Error>
  7. // 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> }
  8. // 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() } }
  9. // 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)) } } }
  10. // 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) }
  11. ܕ෇͚͞ΕͨΤϥʔ • Signal<Value, Error>, SignalProducer<Value, Error> • Result<Value, Error> ͔Βͷੜ੒:

    attempt, attemptMap • throwsΛѻ͏࣌͸໌നͳܕ΁ͷΩϟετɺ΋͘͠͸NSErrorͱ ͯ͠ѻ͏ʢResultͷAPIʣ • RAC: Atomic, MutablePropertyΛআ͍ͯthrows͸࢖ΘΕ͍ͯͳ ͍ • Carthage: throws͸࢖ΘΕ͍ͯͳ͍
  12. // 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) }
  13. // 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 } } }
  14. 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.
  15. 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 }
  16. 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))
  17. 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))
  18. ࢀߟ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