Slide 1

Slide 1 text

Carthage and ReactiveCocoa @ikesyo Reactive Swift Meetup, 2016-04-13 Wed @wantedly #Reactive_Swift_Meetup

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

✨ Contributions ✨ • ReactiveCocoa • Carthage (Commandant, ReactiveTask) • Result • Himotoki • APIKit

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Carthage and ReactiveCocoa

Slide 12

Slide 12 text

• 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

Slide 13

Slide 13 text

• Promise, ColdSignalͳͲࠓ͸ͳ͖ܕୡ • Signal/SignalProducer΁ͷมભ • Carthageͷ։ൃΛ௨ͯ͠RAC 3.0ͷAPIΛςετɺચ࿅ 1 1 γϯϓϧͳύοέʔδ؅ཧπʔϧ Carthage - Realm is a mobile database: a replacement for SQLite & Core Data

Slide 14

Slide 14 text

ඇಉظॲཧͷఆܕԽ • APIຖʹελΠϧ͕ҟͳͬͯ໎Θͳ͍Α͏ʹ͢Δ • delegate, target-action, callback(Ҿ਺΋༷ʑ), ... • ඇಉظॲཧͷνΣʔϯ • ! Callback Hell • flatMap • flatten ίʔυϕʔεશମͰ࢖͏ͷ͕ޮՌతʢϩοΫΠϯ !ʣ

Slide 15

Slide 15 text

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 ... }))

Slide 16

Slide 16 text

// 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? = nil ) -> SignalProducer, TaskError>

Slide 17

Slide 17 text

// 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 }

Slide 18

Slide 18 text

ඇಉظॲཧͷఆܕԽ • ಉظతAPI • ςετɺCLIϓϩάϥϜ • RxSwift: RxBlocking // SignalProducer func first() -> Result? func single() -> Result? func last() -> Result? func wait() -> Result<(), Error>

Slide 19

Slide 19 text

// 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> }

Slide 20

Slide 20 text

// CopyFrameworks.swift public struct CopyFrameworksCommand: CommandType { public func run(options: NoOptions) -> 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() } }

Slide 21

Slide 21 text

ಉظॲཧͷϥοϓ • ಉظॲཧ݁ՌͷϋϯυϦϯά΋ඇಉظॲཧͷ৔߹ͱڞ௨Խ • ಉظͱඇಉظΛҙࣝ͠ͳ͍ • ಺෦ॲཧ͕ಉظ͔ΒඇಉظʹมΘͬͯ΋ݺͼग़͠ଆʹ͸Ө ڹ͕ͳ͍ • ඇಉظॲཧͱͷ߹੒

Slide 22

Slide 22 text

// Project.swift public func loadResolvedCartfile() -> SignalProducer { 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)) } } }

Slide 23

Slide 23 text

// Project.swift public func updatedResolvedCartfile... { let resolver = Resolver(...) let resolvedCartfile: SignalProducer 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) }

Slide 24

Slide 24 text

ܕ෇͚͞ΕͨΤϥʔ • Signal, SignalProducer • Result ͔Βͷੜ੒: attempt, attemptMap • throwsΛѻ͏࣌͸໌നͳܕ΁ͷΩϟετɺ΋͘͠͸NSErrorͱ ͯ͠ѻ͏ʢResultͷAPIʣ • RAC: Atomic, MutablePropertyΛআ͍ͯthrows͸࢖ΘΕ͍ͯͳ ͍ • Carthage: throws͸࢖ΘΕ͍ͯͳ͍

Slide 25

Slide 25 text

// 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) }

Slide 26

Slide 26 text

ܕ෇͚͞ΕͨΤϥʔ • Result.NoError: Πϯελϯεੜ੒Ͱ͖ͳ͍Τϥʔܕ • ࣦഊ͢ΔՄೳੑ͕ͳ͍͜ͱΛAPIͷγάωνϟʔͱͯ͠දݱͰ͖ Δ • <~ΦϖϨʔλʔʹΑΔMutableProperty΁ͷSignal, SignalProducerͷόΠϯσΟϯάͷͨΊʹ͸NoErrorͰͳ͚Ε ͹ͳΒͳ͍ 2 2 https://github.com/ReactiveCocoa/ReactiveCocoa/blob/v4.1.0/ReactiveCocoa/Swift/Property.swift#L241-L302

Slide 27

Slide 27 text

// Git.swift internal func branchExistsInRepository(repositoryFileURL: NSURL, pattern: String) -> SignalProducer { return ensureDirectoryExistsAtURL(repositoryFileURL) .succeeded() .flatMap(.Concat) { exists -> SignalProducer 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 } } }

Slide 28

Slide 28 text

͓·͚

Slide 29

Slide 29 text

thoughtbot/Delta

Slide 30

Slide 30 text

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.

Slide 31

Slide 31 text

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: Action) func dispatch (action: DynamicAction) -> DynamicAction.ResponseType }

Slide 32

Slide 32 text

Delta with RAC extension MutableProperty: ObservablePropertyType { typealias ValueType = Value } struct AppState { let userId: MutableProperty(.None) } struct Store: StoreType { var state: MutableProperty } let initialState = AppState() var store = Store(state: MutableProperty(initialState))

Slide 33

Slide 33 text

Delta with RxSwift extension Variable: ObservablePropertyType { typealias ValueType = Element } struct AppState { let userId: Variable(.None) } struct Store: StoreType { var state: Variable } let initialState = AppState() var store = Store(state: Variable(initialState))

Slide 34

Slide 34 text

ࢀߟ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

Slide 35

Slide 35 text

!" Happy Reactive Coding!! "!

Slide 36

Slide 36 text

Thank you❗"