-
Notifications
You must be signed in to change notification settings - Fork 299
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
21b2805
commit 335b7b8
Showing
64 changed files
with
4,437 additions
and
0 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
...odular-dependency-injection-pt1/PrimeTime/ComposableArchitecture/ComposableArchitecture.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// ComposableArchitecture.h | ||
// ComposableArchitecture | ||
// | ||
// Created by Stephen Celis on 9/8/19. | ||
// Copyright © 2019 Point-Free. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
//! Project version number for ComposableArchitecture. | ||
FOUNDATION_EXPORT double ComposableArchitectureVersionNumber; | ||
|
||
//! Project version string for ComposableArchitecture. | ||
FOUNDATION_EXPORT const unsigned char ComposableArchitectureVersionString[]; | ||
|
||
// In this header, you should import all the public headers of your framework using statements like #import <ComposableArchitecture/PublicHeader.h> | ||
|
||
|
105 changes: 105 additions & 0 deletions
105
...ar-dependency-injection-pt1/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import CasePaths | ||
import Combine | ||
import SwiftUI | ||
|
||
public typealias Reducer<Value, Action, Environment> = (inout Value, Action, Environment) -> [Effect<Action>] | ||
//public typealias Reducer<Value, Action, Environment> = (inout Value, Action) -> (Environment) -> [Effect<Action>] | ||
|
||
public func combine<Value, Action, Environment>( | ||
_ reducers: Reducer<Value, Action, Environment>... | ||
) -> Reducer<Value, Action, Environment> { | ||
return { value, action, environment in | ||
let effects = reducers.flatMap { $0(&value, action, environment) } | ||
return effects | ||
} | ||
} | ||
|
||
public func pullback<LocalValue, GlobalValue, LocalAction, GlobalAction, LocalEnvironment, GlobalEnvironment>( | ||
_ reducer: @escaping Reducer<LocalValue, LocalAction, LocalEnvironment>, | ||
value: WritableKeyPath<GlobalValue, LocalValue>, | ||
action: CasePath<GlobalAction, LocalAction>, | ||
environment: @escaping (GlobalEnvironment) -> LocalEnvironment | ||
) -> Reducer<GlobalValue, GlobalAction, GlobalEnvironment> { | ||
return { globalValue, globalAction, globalEnvironment in | ||
guard let localAction = action.extract(from: globalAction) else { return [] } | ||
let localEffects = reducer(&globalValue[keyPath: value], localAction, environment(globalEnvironment)) | ||
|
||
return localEffects.map { localEffect in | ||
localEffect.map(action.embed) | ||
.eraseToEffect() | ||
} | ||
} | ||
} | ||
|
||
public func logging<Value, Action, Environment>( | ||
_ reducer: @escaping Reducer<Value, Action, Environment> | ||
) -> Reducer<Value, Action, Environment> { | ||
return { value, action, environment in | ||
let effects = reducer(&value, action, environment) | ||
let newValue = value | ||
return [.fireAndForget { | ||
print("Action: \(action)") | ||
print("Value:") | ||
dump(newValue) | ||
print("---") | ||
}] + effects | ||
} | ||
} | ||
|
||
public final class Store<Value, Action>: ObservableObject { | ||
private let reducer: Reducer<Value, Action, Any> | ||
private let environment: Any | ||
@Published public private(set) var value: Value | ||
private var viewCancellable: Cancellable? | ||
private var effectCancellables: Set<AnyCancellable> = [] | ||
|
||
public init<Environment>( | ||
initialValue: Value, | ||
reducer: @escaping Reducer<Value, Action, Environment>, | ||
environment: Environment | ||
) { | ||
self.reducer = { value, action, environment in | ||
reducer(&value, action, environment as! Environment) | ||
} | ||
self.value = initialValue | ||
self.environment = environment | ||
} | ||
|
||
public func send(_ action: Action) { | ||
let effects = self.reducer(&self.value, action, self.environment) | ||
effects.forEach { effect in | ||
var effectCancellable: AnyCancellable? | ||
var didComplete = false | ||
effectCancellable = effect.sink( | ||
receiveCompletion: { [weak self] _ in | ||
didComplete = true | ||
guard let effectCancellable = effectCancellable else { return } | ||
self?.effectCancellables.remove(effectCancellable) | ||
}, | ||
receiveValue: self.send | ||
) | ||
if !didComplete, let effectCancellable = effectCancellable { | ||
self.effectCancellables.insert(effectCancellable) | ||
} | ||
} | ||
} | ||
|
||
public func view<LocalValue, LocalAction>( | ||
value toLocalValue: @escaping (Value) -> LocalValue, | ||
action toGlobalAction: @escaping (LocalAction) -> Action | ||
) -> Store<LocalValue, LocalAction> { | ||
let localStore = Store<LocalValue, LocalAction>( | ||
initialValue: toLocalValue(self.value), | ||
reducer: { localValue, localAction, _ in | ||
self.send(toGlobalAction(localAction)) | ||
localValue = toLocalValue(self.value) | ||
return [] | ||
}, | ||
environment: self.environment | ||
) | ||
localStore.viewCancellable = self.$value.sink { [weak localStore] newValue in | ||
localStore?.value = toLocalValue(newValue) | ||
} | ||
return localStore | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
0091-modular-dependency-injection-pt1/PrimeTime/ComposableArchitecture/Effect.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import Combine | ||
|
||
public struct Effect<Output>: Publisher { | ||
public typealias Failure = Never | ||
|
||
let publisher: AnyPublisher<Output, Failure> | ||
|
||
public func receive<S>( | ||
subscriber: S | ||
) where S: Subscriber, Failure == S.Failure, Output == S.Input { | ||
self.publisher.receive(subscriber: subscriber) | ||
} | ||
} | ||
|
||
extension Effect { | ||
public static func fireAndForget(work: @escaping () -> Void) -> Effect { | ||
return Deferred { () -> Empty<Output, Never> in | ||
work() | ||
return Empty(completeImmediately: true) | ||
}.eraseToEffect() | ||
} | ||
|
||
public static func sync(work: @escaping () -> Output) -> Effect { | ||
return Deferred { | ||
Just(work()) | ||
}.eraseToEffect() | ||
} | ||
} | ||
|
||
extension Publisher where Failure == Never { | ||
public func eraseToEffect() -> Effect<Output> { | ||
return Effect(publisher: self.eraseToAnyPublisher()) | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
0091-modular-dependency-injection-pt1/PrimeTime/ComposableArchitecture/Info.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
</dict> | ||
</plist> |
19 changes: 19 additions & 0 deletions
19
...ction-pt1/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// ComposableArchitectureTestSupport.h | ||
// ComposableArchitectureTestSupport | ||
// | ||
// Created by Stephen Celis on 2/6/20. | ||
// Copyright © 2020 Point-Free. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
//! Project version number for ComposableArchitectureTestSupport. | ||
FOUNDATION_EXPORT double ComposableArchitectureTestSupportVersionNumber; | ||
|
||
//! Project version string for ComposableArchitectureTestSupport. | ||
FOUNDATION_EXPORT const unsigned char ComposableArchitectureTestSupportVersionString[]; | ||
|
||
// In this header, you should import all the public headers of your framework using statements like #import <ComposableArchitectureTestSupport/PublicHeader.h> | ||
|
||
|
78 changes: 78 additions & 0 deletions
78
...n-pt1/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import ComposableArchitecture | ||
import XCTest | ||
|
||
public enum StepType { | ||
case send | ||
case receive | ||
} | ||
|
||
public struct Step<Value, Action> { | ||
let type: StepType | ||
let action: Action | ||
let update: (inout Value) -> Void | ||
let file: StaticString | ||
let line: UInt | ||
|
||
public init( | ||
_ type: StepType, | ||
_ action: Action, | ||
file: StaticString = #file, | ||
line: UInt = #line, | ||
_ update: @escaping (inout Value) -> Void | ||
) { | ||
self.type = type | ||
self.action = action | ||
self.update = update | ||
self.file = file | ||
self.line = line | ||
} | ||
} | ||
|
||
public func assert<Value: Equatable, Action: Equatable>( | ||
initialValue: Value, | ||
reducer: Reducer<Value, Action>, | ||
steps: Step<Value, Action>..., | ||
file: StaticString = #file, | ||
line: UInt = #line | ||
) { | ||
var state = initialValue | ||
var effects: [Effect<Action>] = [] | ||
|
||
steps.forEach { step in | ||
var expected = state | ||
|
||
switch step.type { | ||
case .send: | ||
if !effects.isEmpty { | ||
XCTFail("Action sent before handling \(effects.count) pending effect(s)", file: step.file, line: step.line) | ||
} | ||
effects.append(contentsOf: reducer(&state, step.action)) | ||
|
||
case .receive: | ||
guard !effects.isEmpty else { | ||
XCTFail("No pending effects to receive from", file: step.file, line: step.line) | ||
break | ||
} | ||
let effect = effects.removeFirst() | ||
var action: Action! | ||
let receivedCompletion = XCTestExpectation(description: "receivedCompletion") | ||
let e = effect.sink( | ||
receiveCompletion: { _ in | ||
receivedCompletion.fulfill() | ||
}, | ||
receiveValue: { action = $0 } | ||
) | ||
if XCTWaiter.wait(for: [receivedCompletion], timeout: 0.01) != .completed { | ||
XCTFail("Timed out waiting for the effect to complete", file: step.file, line: step.line) | ||
} | ||
XCTAssertEqual(action, step.action, file: step.file, line: step.line) | ||
effects.append(contentsOf: reducer(&state, action)) | ||
} | ||
|
||
step.update(&expected) | ||
XCTAssertEqual(state, expected, file: step.file, line: step.line) | ||
} | ||
if !effects.isEmpty { | ||
XCTFail("Assertion failed to handle \(effects.count) pending effect(s)", file: file, line: line) | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
0091-modular-dependency-injection-pt1/PrimeTime/ComposableArchitectureTestSupport/Info.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
</dict> | ||
</plist> |
5 changes: 5 additions & 0 deletions
5
...ncy-injection-pt1/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import XCTest | ||
@testable import ComposableArchitecture | ||
|
||
class ComposableArchitectureTests: XCTestCase { | ||
} |
22 changes: 22 additions & 0 deletions
22
0091-modular-dependency-injection-pt1/PrimeTime/ComposableArchitectureTests/Info.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>CFBundleDevelopmentRegion</key> | ||
<string>$(DEVELOPMENT_LANGUAGE)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleInfoDictionaryVersion</key> | ||
<string>6.0</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>1.0</string> | ||
<key>CFBundleVersion</key> | ||
<string>1</string> | ||
</dict> | ||
</plist> |
19 changes: 19 additions & 0 deletions
19
0091-modular-dependency-injection-pt1/PrimeTime/Counter/Counter.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// Counter.h | ||
// Counter | ||
// | ||
// Created by Stephen Celis on 9/8/19. | ||
// Copyright © 2019 Point-Free. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
//! Project version number for Counter. | ||
FOUNDATION_EXPORT double CounterVersionNumber; | ||
|
||
//! Project version string for Counter. | ||
FOUNDATION_EXPORT const unsigned char CounterVersionString[]; | ||
|
||
// In this header, you should import all the public headers of your framework using statements like #import <Counter/PublicHeader.h> | ||
|
||
|
Oops, something went wrong.