diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.h b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.h new file mode 100644 index 00000000..38201c51 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.h @@ -0,0 +1,19 @@ +// +// ComposableArchitecture.h +// ComposableArchitecture +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +#import + +//! 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 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift new file mode 100644 index 00000000..9dfef930 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift @@ -0,0 +1,105 @@ +import CasePaths +import Combine +import SwiftUI + +public typealias Reducer = (inout Value, Action, Environment) -> [Effect] +//public typealias Reducer = (inout Value, Action) -> (Environment) -> [Effect] + +public func combine( + _ reducers: Reducer... +) -> Reducer { + return { value, action, environment in + let effects = reducers.flatMap { $0(&value, action, environment) } + return effects + } +} + +public func pullback( + _ reducer: @escaping Reducer, + value: WritableKeyPath, + action: CasePath, + environment: @escaping (GlobalEnvironment) -> LocalEnvironment +) -> Reducer { + 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( + _ reducer: @escaping Reducer +) -> Reducer { + 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: ObservableObject { + private let reducer: Reducer + private let environment: Any + @Published public private(set) var value: Value + private var viewCancellable: Cancellable? + private var effectCancellables: Set = [] + + public init( + initialValue: Value, + reducer: @escaping Reducer, + 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( + value toLocalValue: @escaping (Value) -> LocalValue, + action toGlobalAction: @escaping (LocalAction) -> Action + ) -> Store { + let localStore = Store( + 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 + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/Effect.swift b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/Effect.swift new file mode 100644 index 00000000..0dcb9932 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/Effect.swift @@ -0,0 +1,34 @@ +import Combine + +public struct Effect: Publisher { + public typealias Failure = Never + + let publisher: AnyPublisher + + public func receive( + 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 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 { + return Effect(publisher: self.eraseToAnyPublisher()) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitecture/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.h b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.h new file mode 100644 index 00000000..476d0b04 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.h @@ -0,0 +1,19 @@ +// +// ComposableArchitectureTestSupport.h +// ComposableArchitectureTestSupport +// +// Created by Stephen Celis on 2/6/20. +// Copyright © 2020 Point-Free. All rights reserved. +// + +#import + +//! 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 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.swift b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.swift new file mode 100644 index 00000000..175f2863 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.swift @@ -0,0 +1,79 @@ +import ComposableArchitecture +import XCTest + +public enum StepType { + case send + case receive +} + +public struct Step { + 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 = { _ in } + ) { + self.type = type + self.action = action + self.update = update + self.file = file + self.line = line + } +} + +public func assert( + initialValue: Value, + reducer: Reducer, + environment: Environment, + steps: Step..., + file: StaticString = #file, + line: UInt = #line +) { + var state = initialValue + var effects: [Effect] = [] + + 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, environment)) + + 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, environment)) + } + + 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) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTestSupport/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift new file mode 100644 index 00000000..c2aa34cb --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift @@ -0,0 +1,5 @@ +import XCTest +@testable import ComposableArchitecture + +class ComposableArchitectureTests: XCTestCase { +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTests/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/ComposableArchitectureTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Counter.h b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Counter.h new file mode 100644 index 00000000..ba74c646 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Counter.h @@ -0,0 +1,19 @@ +// +// Counter.h +// Counter +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +#import + +//! 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 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Counter.swift b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Counter.swift new file mode 100644 index 00000000..76935a97 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Counter.swift @@ -0,0 +1,205 @@ +import CasePaths +import Combine +import ComposableArchitecture +import PrimeModal +import SwiftUI + +public enum CounterAction: Equatable { + case decrTapped + case incrTapped + case nthPrimeButtonTapped + case nthPrimeResponse(n: Int, prime: Int?) + case alertDismissButtonTapped + case isPrimeButtonTapped + case primeModalDismissed +} + +public typealias CounterState = ( + alertNthPrime: PrimeAlert?, + count: Int, + isNthPrimeButtonDisabled: Bool, + isPrimeModalShown: Bool +) + +public func counterReducer( + state: inout CounterState, + action: CounterAction, + environment: CounterEnvironment +) -> [Effect] { + switch action { + case .decrTapped: + state.count -= 1 + let count = state.count + return [ +// .fireAndForget { +// print(count) +// }, +// +// Just(CounterAction.incrTapped) +// .delay(for: 1, scheduler: DispatchQueue.main) +// .eraseToEffect() + ] + + case .incrTapped: + state.count += 1 + return [] + + case .nthPrimeButtonTapped: + state.isNthPrimeButtonDisabled = true + let n = state.count + return [ + environment(state.count) + .map { CounterAction.nthPrimeResponse(n: n, prime: $0) } + .receive(on: DispatchQueue.main) + .eraseToEffect() + ] + + case let .nthPrimeResponse(n, prime): + state.alertNthPrime = prime.map { PrimeAlert(n: n, prime: $0) } + state.isNthPrimeButtonDisabled = false + return [] + + case .alertDismissButtonTapped: + state.alertNthPrime = nil + return [] + + case .isPrimeButtonTapped: + state.isPrimeModalShown = true + return [] + + case .primeModalDismissed: + state.isPrimeModalShown = false + return [] + } +} + +//public struct CounterEnvironment { +// var nthPrime: (Int) -> Effect +//} + +public typealias CounterEnvironment = (Int) -> Effect + +//extension CounterEnvironment { +// public static let live = CounterEnvironment(nthPrime: Counter.nthPrime) +//} + +//var Current = CounterEnvironment.live + +//extension CounterEnvironment { +// static let mock = CounterEnvironment(nthPrime: { _ in .sync { 17 }}) +//} + +public let counterViewReducer: Reducer = combine( + pullback( + counterReducer, + value: \CounterViewState.counter, + action: /CounterViewAction.counter, + environment: { $0 } + ), + pullback( + primeModalReducer, + value: \.primeModal, + action: /CounterViewAction.primeModal, + environment: { _ in () } + ) +) + +public struct PrimeAlert: Equatable, Identifiable { + public let n: Int + public let prime: Int + public var id: Int { self.prime } + + public init(n: Int, prime: Int) { + self.n = n + self.prime = prime + } +} + +public struct CounterViewState: Equatable { + public var alertNthPrime: PrimeAlert? + public var count: Int + public var favoritePrimes: [Int] + public var isNthPrimeButtonDisabled: Bool + public var isPrimeModalShown: Bool + + public init( + alertNthPrime: PrimeAlert? = nil, + count: Int = 0, + favoritePrimes: [Int] = [], + isNthPrimeButtonDisabled: Bool = false, + isPrimeModalShown: Bool = false + ) { + self.alertNthPrime = alertNthPrime + self.count = count + self.favoritePrimes = favoritePrimes + self.isNthPrimeButtonDisabled = isNthPrimeButtonDisabled + self.isPrimeModalShown = isPrimeModalShown + } + + var counter: CounterState { + get { (self.alertNthPrime, self.count, self.isNthPrimeButtonDisabled, self.isPrimeModalShown) } + set { (self.alertNthPrime, self.count, self.isNthPrimeButtonDisabled, self.isPrimeModalShown) = newValue } + } + + var primeModal: PrimeModalState { + get { (self.count, self.favoritePrimes) } + set { (self.count, self.favoritePrimes) = newValue } + } +} + +public enum CounterViewAction: Equatable { + case counter(CounterAction) + case primeModal(PrimeModalAction) +} + +public struct CounterView: View { + @ObservedObject var store: Store + + public init(store: Store) { + self.store = store + } + + public var body: some View { + VStack { + HStack { + Button("-") { self.store.send(.counter(.decrTapped)) } + Text("\(self.store.value.count)") + Button("+") { self.store.send(.counter(.incrTapped)) } + } + Button("Is this prime?") { self.store.send(.counter(.isPrimeButtonTapped)) } + Button("What is the \(ordinal(self.store.value.count)) prime?") { + self.store.send(.counter(.nthPrimeButtonTapped)) + } + .disabled(self.store.value.isNthPrimeButtonDisabled) + } + .font(.title) + .navigationBarTitle("Counter demo") + .sheet( + isPresented: .constant(self.store.value.isPrimeModalShown), + onDismiss: { self.store.send(.counter(.primeModalDismissed)) } + ) { + IsPrimeModalView( + store: self.store.view( + value: { ($0.count, $0.favoritePrimes) }, + action: { .primeModal($0) } + ) + ) + } + .alert( + item: .constant(self.store.value.alertNthPrime) + ) { alert in + Alert( + title: Text("The \(ordinal(self.store.value.count)) prime is \(alert.prime)"), + dismissButton: .default(Text("Ok")) { + self.store.send(.counter(.alertDismissButtonTapped)) + } + ) + } + } +} + +func ordinal(_ n: Int) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .ordinal + return formatter.string(for: n) ?? "" +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/Counter/WolframAlpha.swift b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/WolframAlpha.swift new file mode 100644 index 00000000..c57308de --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/Counter/WolframAlpha.swift @@ -0,0 +1,69 @@ +import Combine +import ComposableArchitecture +import Foundation + +private let wolframAlphaApiKey = "6H69Q3-828TKQJ4EP" + +struct WolframAlphaResult: Decodable { + let queryresult: QueryResult + + struct QueryResult: Decodable { + let pods: [Pod] + + struct Pod: Decodable { + let primary: Bool? + let subpods: [SubPod] + + struct SubPod: Decodable { + let plaintext: String + } + } + } +} + +public func nthPrime(_ n: Int) -> Effect { + return wolframAlpha(query: "prime \(n)").map { result in + result + .flatMap { + $0.queryresult + .pods + .first(where: { $0.primary == .some(true) })? + .subpods + .first? + .plaintext + } + .flatMap(Int.init) + } + .eraseToEffect() +} + +func wolframAlpha(query: String) -> Effect { + var components = URLComponents(string: "https://api.wolframalpha.com/v2/query")! + components.queryItems = [ + URLQueryItem(name: "input", value: query), + URLQueryItem(name: "format", value: "plaintext"), + URLQueryItem(name: "output", value: "JSON"), + URLQueryItem(name: "appid", value: wolframAlphaApiKey), + ] + + return URLSession.shared + .dataTaskPublisher(for: components.url(relativeTo: nil)!) + .map { data, _ in data } + .decode(type: WolframAlphaResult?.self, decoder: JSONDecoder()) + .replaceError(with: nil) + .eraseToEffect() +} + +//return [Effect { callback in +// nthPrime(count) { prime in +// DispatchQueue.main.async { +// callback(.nthPrimeResponse(prime)) +// } +// } +//}] + +extension Publisher { + public var hush: Publishers.ReplaceError>> { + return self.map(Optional.some).replaceError(with: nil) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/ComposableArchitectureSnapshotTesting.swift b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/ComposableArchitectureSnapshotTesting.swift new file mode 100644 index 00000000..2fbd1b5c --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/ComposableArchitectureSnapshotTesting.swift @@ -0,0 +1,21 @@ +import SnapshotTesting +import XCTest + +extension Snapshotting where Value: UIViewController, Format == UIImage { + static var windowedImage: Snapshotting { + return Snapshotting.image.asyncPullback { vc in + Async { callback in + UIView.setAnimationsEnabled(false) + let window = UIApplication.shared.windows.first! + window.rootViewController = vc + DispatchQueue.main.async { + let image = UIGraphicsImageRenderer(bounds: window.bounds).image { ctx in + window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) + } + callback(image) + UIView.setAnimationsEnabled(true) + } + } + } + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/CounterTests.swift b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/CounterTests.swift new file mode 100644 index 00000000..8fc7aa26 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/CounterTests.swift @@ -0,0 +1,137 @@ +import XCTest +@testable import Counter +import ComposableArchitecture +import ComposableArchitectureTestSupport +import SnapshotTesting +import SwiftUI + +class CounterTests: XCTestCase { +// override func setUp() { +// super.setUp() +// Current = .mock +// } + +// func testSnapshots() { +// let store = Store(initialValue: CounterViewState(), reducer: counterViewReducer, environment: { _ in .sync { 17 } }) +// let view = CounterView(store: store) +// +// let vc = UIHostingController(rootView: view) +// vc.view.frame = UIScreen.main.bounds +// +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.counter(.incrTapped)) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.counter(.incrTapped)) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.counter(.nthPrimeButtonTapped)) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// var expectation = self.expectation(description: "wait") +// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { +// expectation.fulfill() +// } +// self.wait(for: [expectation], timeout: 0.5) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.counter(.alertDismissButtonTapped)) +// expectation = self.expectation(description: "wait") +// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { +// expectation.fulfill() +// } +// self.wait(for: [expectation], timeout: 0.5) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.counter(.isPrimeButtonTapped)) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.primeModal(.saveFavoritePrimeTapped)) +// assertSnapshot(matching: vc, as: .windowedImage) +// +// store.send(.counter(.primeModalDismissed)) +// assertSnapshot(matching: vc, as: .windowedImage) +// } + + func testIncrDecrButtonTapped() { + assert( + initialValue: CounterViewState(count: 2), + reducer: counterViewReducer, + environment: { _ in .sync { 17 } }, + steps: + Step(.send, .counter(.incrTapped)) { $0.count = 3 }, + Step(.send, .counter(.incrTapped)) { $0.count = 4 }, + Step(.send, .counter(.decrTapped)) { $0.count = 3 } + ) + } + + func testNthPrimeButtonHappyFlow() { +// Current.nthPrime = + + assert( + initialValue: CounterViewState( + alertNthPrime: nil, + count: 7, + isNthPrimeButtonDisabled: false + ), + reducer: counterViewReducer, + environment: { _ in .sync { 17 } }, + steps: + Step(.send, .counter(.nthPrimeButtonTapped)) { + $0.isNthPrimeButtonDisabled = true + }, + Step(.receive, .counter(.nthPrimeResponse(n: 7, prime: 17))) { + $0.alertNthPrime = PrimeAlert(n: $0.count, prime: 17) + $0.isNthPrimeButtonDisabled = false + }, + Step(.send, .counter(.alertDismissButtonTapped)) { + $0.alertNthPrime = nil + } + ) + } + + func testNthPrimeButtonUnhappyFlow() { +// Current.nthPrime = + + assert( + initialValue: CounterViewState( + alertNthPrime: nil, + count: 7, + isNthPrimeButtonDisabled: false + ), + reducer: counterViewReducer, + environment: { _ in .sync { nil } }, + steps: + Step(.send, .counter(.nthPrimeButtonTapped)) { + $0.isNthPrimeButtonDisabled = true + }, + Step(.receive, .counter(.nthPrimeResponse(n: 7, prime: nil))) { + $0.isNthPrimeButtonDisabled = false + } + ) + } + + func testPrimeModal() { +// Current = .mock + + assert( + initialValue: CounterViewState( + count: 1, + favoritePrimes: [3, 5] + ), + reducer: counterViewReducer, + environment: { _ in .sync { 17 } }, + steps: + Step(.send, .counter(.incrTapped)) { + $0.count = 2 + }, + Step(.send, .primeModal(.saveFavoritePrimeTapped)) { + $0.favoritePrimes = [3, 5, 2] + }, + Step(.send, .primeModal(.removeFavoritePrimeTapped)) { + $0.favoritePrimes = [3, 5] + } + ) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.1.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.1.png new file mode 100644 index 00000000..bd0e1ba0 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.1.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.2.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.2.png new file mode 100644 index 00000000..8bda1233 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.2.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.3.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.3.png new file mode 100644 index 00000000..d54ac962 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.3.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.4.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.4.png new file mode 100644 index 00000000..ed1a854e Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.4.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.5.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.5.png new file mode 100644 index 00000000..f5a3fe45 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.5.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.6.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.6.png new file mode 100644 index 00000000..d54ac962 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.6.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.7.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.7.png new file mode 100644 index 00000000..321ee3ef Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.7.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.8.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.8.png new file mode 100644 index 00000000..df4853c5 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.8.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.9.png b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.9.png new file mode 100644 index 00000000..d54ac962 Binary files /dev/null and b/0092-modular-dependency-injection-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.9.png differ diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.h b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.h new file mode 100644 index 00000000..b0a43669 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.h @@ -0,0 +1,19 @@ +// +// FavoritePrimes.h +// FavoritePrimes +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +#import + +//! Project version number for FavoritePrimes. +FOUNDATION_EXPORT double FavoritePrimesVersionNumber; + +//! Project version string for FavoritePrimes. +FOUNDATION_EXPORT const unsigned char FavoritePrimesVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.swift b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.swift new file mode 100644 index 00000000..8badbdec --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.swift @@ -0,0 +1,194 @@ +import ComposableArchitecture +import SwiftUI + +public enum FavoritePrimesAction: Equatable { + case deleteFavoritePrimes(IndexSet) + case loadButtonTapped + case loadedFavoritePrimes([Int]) + case saveButtonTapped +} + +public func favoritePrimesReducer( + state: inout [Int], + action: FavoritePrimesAction, + environment: FavoritePrimesEnvironment +) -> [Effect] { + switch action { + case let .deleteFavoritePrimes(indexSet): + for index in indexSet { + state.remove(at: index) + } + return [ + ] + + case let .loadedFavoritePrimes(favoritePrimes): + state = favoritePrimes + return [] + + case .saveButtonTapped: + return [ + environment.save("favorite-primes.json", try! JSONEncoder().encode(state)) + .fireAndForget() +// saveEffect(favoritePrimes: state) + ] + + case .loadButtonTapped: + return [ + environment.load("favorite-primes.json") + .compactMap { $0 } + .decode(type: [Int].self, decoder: JSONDecoder()) + .catch { error in Empty(completeImmediately: true) } + .map(FavoritePrimesAction.loadedFavoritePrimes) +// .merge(with: Just(FavoritePrimesAction.loadedFavoritePrimes([2, 31]))) + .eraseToEffect() +// loadEffect +// .compactMap { $0 } +// .eraseToEffect() + ] + } +} + +// (Never) -> A + +import Combine + +extension Publisher where Output == Never, Failure == Never { + func fireAndForget() -> Effect { + return self.map(absurd).eraseToEffect() + } +} + + +func absurd(_ never: Never) -> A {} + + +public struct FileClient { + var load: (String) -> Effect + var save: (String, Data) -> Effect +} +extension FileClient { + public static let live = FileClient( + load: { fileName -> Effect in + .sync { + let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + let documentsUrl = URL(fileURLWithPath: documentsPath) + let favoritePrimesUrl = documentsUrl.appendingPathComponent(fileName) + return try? Data(contentsOf: favoritePrimesUrl) + } + }, + save: { fileName, data in + return .fireAndForget { + let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + let documentsUrl = URL(fileURLWithPath: documentsPath) + let favoritePrimesUrl = documentsUrl.appendingPathComponent(fileName) + try! data.write(to: favoritePrimesUrl) + } + } + ) +} + +//public struct FavoritePrimesEnvironment { +// var fileClient: FileClient +//} + +public typealias FavoritePrimesEnvironment = FileClient + +//extension FavoritePrimesEnvironment { +// public static let live = FavoritePrimesEnvironment(fileClient: .live) +//} + +//var Current = FavoritePrimesEnvironment.live + +#if DEBUG +extension FileClient { + static let mock = FileClient( + load: { _ in Effect.sync { + try! JSONEncoder().encode([2, 31]) + } }, + save: { _, _ in .fireAndForget {} } + ) +} +#endif + +//struct Environment { +// var date: () -> Date +//} +//extension Environment { +// static let live = Environment(date: Date.init) +//} +// +//extension Environment { +// static let mock = Environment(date: { Date.init(timeIntervalSince1970: 1234567890) }) +//} +// +////Current = .mock +// +//struct GitHubClient { +// var fetchRepos: (@escaping (Result<[Repo], Error>) -> Void) -> Void +// +// struct Repo: Decodable { +// var archived: Bool +// var description: String? +// var htmlUrl: URL +// var name: String +// var pushedAt: Date? +// } +//} +// +//#if DEBUG +//var Current = Environment.live +//#else +//let Current = Environment.live +//#endif + +//private func saveEffect(favoritePrimes: [Int]) -> Effect { +// return .fireAndForget { +//// Current.date() +// let data = try! JSONEncoder().encode(favoritePrimes) +// let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] +// let documentsUrl = URL(fileURLWithPath: documentsPath) +// let favoritePrimesUrl = documentsUrl.appendingPathComponent("favorite-primes.json") +// try! data.write(to: favoritePrimesUrl) +// } +//} + +//private let loadEffect = Effect.sync { +// let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] +// let documentsUrl = URL(fileURLWithPath: documentsPath) +// let favoritePrimesUrl = documentsUrl.appendingPathComponent("favorite-primes.json") +// guard +// let data = try? Data(contentsOf: favoritePrimesUrl), +// let favoritePrimes = try? JSONDecoder().decode([Int].self, from: data) +// else { return nil } +// return .loadedFavoritePrimes(favoritePrimes) +//} + +public struct FavoritePrimesView: View { + @ObservedObject var store: Store<[Int], FavoritePrimesAction> + + public init(store: Store<[Int], FavoritePrimesAction>) { + self.store = store + } + + public var body: some View { + List { + ForEach(self.store.value, id: \.self) { prime in + Text("\(prime)") + } + .onDelete { indexSet in + self.store.send(.deleteFavoritePrimes(indexSet)) + } + } + .navigationBarTitle("Favorite primes") + .navigationBarItems( + trailing: HStack { + Button("Save") { + self.store.send(.saveButtonTapped) + } + Button("Load") { + self.store.send(.loadButtonTapped) + } + } + ) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimes/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift new file mode 100644 index 00000000..6fd4cf66 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift @@ -0,0 +1,71 @@ +import XCTest +@testable import FavoritePrimes + +class FavoritePrimesTests: XCTestCase { +// var environment: + + override func setUp() { + super.setUp() +// Current = .mock + +// self.environment = ( +// ) + } + + func testDeleteFavoritePrimes() { + var state = [2, 3, 5, 7] + let effects = favoritePrimesReducer(state: &state, action: .deleteFavoritePrimes([2]), environment: .mock) + + XCTAssertEqual(state, [2, 3, 7]) + XCTAssert(effects.isEmpty) + } + + func testSaveButtonTapped() { + var didSave = false + var environment = FileClient.mock + environment.save = { _, data in + .fireAndForget { + didSave = true + } + } + + var state = [2, 3, 5, 7] + let effects = favoritePrimesReducer(state: &state, action: .saveButtonTapped, environment: environment) + + XCTAssertEqual(state, [2, 3, 5, 7]) + XCTAssertEqual(effects.count, 1) + + effects[0].sink { _ in XCTFail() } + + XCTAssert(didSave) + } + + func testLoadFavoritePrimesFlow() { + var environment = FileClient.mock + environment.load = { _ in .sync { try! JSONEncoder().encode([2, 31]) } } + + var state = [2, 3, 5, 7] + var effects = favoritePrimesReducer(state: &state, action: .loadButtonTapped, environment: environment) + + XCTAssertEqual(state, [2, 3, 5, 7]) + XCTAssertEqual(effects.count, 1) + + var nextAction: FavoritePrimesAction! + let receivedCompletion = self.expectation(description: "receivedCompletion") + effects[0].sink( + receiveCompletion: { _ in + receivedCompletion.fulfill() + }, + receiveValue: { action in + XCTAssertEqual(action, .loadedFavoritePrimes([2, 31])) + nextAction = action + }) + self.wait(for: [receivedCompletion], timeout: 0) + + effects = favoritePrimesReducer(state: &state, action: nextAction, environment: environment) + + XCTAssertEqual(state, [2, 31]) + XCTAssert(effects.isEmpty) + } + +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimesTests/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimesTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/FavoritePrimesTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/Package.swift b/0092-modular-dependency-injection-pt2/PrimeTime/Package.swift new file mode 100644 index 00000000..89f8b7a3 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/Package.swift @@ -0,0 +1,9 @@ +// swift-tools-version:4.2 +import PackageDescription + +let package = Package( + name: "ComposableArchitecture", + dependencies: [ + .package(url: "https://github.com/pointfreeco/swift-enum-properties.git", from: "0.1.0") + ] +) diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/PrimeModal.h b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/PrimeModal.h new file mode 100644 index 00000000..5bdc9d8f --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/PrimeModal.h @@ -0,0 +1,19 @@ +// +// PrimeModal.h +// PrimeModal +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +#import + +//! Project version number for PrimeModal. +FOUNDATION_EXPORT double PrimeModalVersionNumber; + +//! Project version string for PrimeModal. +FOUNDATION_EXPORT const unsigned char PrimeModalVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/PrimeModal.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/PrimeModal.swift new file mode 100644 index 00000000..ddbd8834 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModal/PrimeModal.swift @@ -0,0 +1,61 @@ +import ComposableArchitecture +import SwiftUI + +public typealias PrimeModalState = (count: Int, favoritePrimes: [Int]) + +public enum PrimeModalAction: Equatable { + case saveFavoritePrimeTapped + case removeFavoritePrimeTapped +} + +public func primeModalReducer( + state: inout PrimeModalState, + action: PrimeModalAction, + environment: Void +) -> [Effect] { + switch action { + case .removeFavoritePrimeTapped: + state.favoritePrimes.removeAll(where: { $0 == state.count }) + return [] + + case .saveFavoritePrimeTapped: + state.favoritePrimes.append(state.count) + return [] + } +} + +public struct IsPrimeModalView: View { + @ObservedObject var store: Store + + public init(store: Store) { + self.store = store + } + + public var body: some View { + VStack { + if isPrime(self.store.value.count) { + Text("\(self.store.value.count) is prime 🎉") + if self.store.value.favoritePrimes.contains(self.store.value.count) { + Button("Remove from favorite primes") { + self.store.send(.removeFavoritePrimeTapped) + } + } else { + Button("Save to favorite primes") { + self.store.send(.saveFavoritePrimeTapped) + } + } + } else { + Text("\(self.store.value.count) is not prime :(") + } + } + } +} + +func isPrime(_ p: Int) -> Bool { + if p <= 1 { return false } + if p <= 3 { return true } + for i in 2...Int(sqrtf(Float(p))) { + if p % i == 0 { return false } + } + return true +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModalTests/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModalTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModalTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModalTests/PrimeModalTests.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModalTests/PrimeModalTests.swift new file mode 100644 index 00000000..b5b5f27c --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeModalTests/PrimeModalTests.swift @@ -0,0 +1,24 @@ +import XCTest +@testable import PrimeModal + +class PrimeModalTests: XCTestCase { + func testSaveFavoritesPrimesTapped() { + var state = (count: 2, favoritePrimes: [3, 5]) + let effects = primeModalReducer(state: &state, action: .saveFavoritePrimeTapped, environment: ()) + + let (count, favoritePrimes) = state + XCTAssertEqual(count, 2) + XCTAssertEqual(favoritePrimes, [3, 5, 2]) + XCTAssert(effects.isEmpty) + } + + func testRemoveFavoritesPrimesTapped() { + var state = (count: 3, favoritePrimes: [3, 5]) + let effects = primeModalReducer(state: &state, action: .removeFavoritePrimeTapped, environment: ()) + + let (count, favoritePrimes) = state + XCTAssertEqual(count, 3) + XCTAssertEqual(favoritePrimes, [5]) + XCTAssert(effects.isEmpty) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Combine.xcplaygroundpage/Contents.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Combine.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..6bcd8a29 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Combine.xcplaygroundpage/Contents.swift @@ -0,0 +1,82 @@ + +public struct Effect { + public let run: (@escaping (A) -> Void) -> Void + + public func map(_ f: @escaping (A) -> B) -> Effect { + return Effect { callback in self.run { a in callback(f(a)) } } + } +} + +import Dispatch + +let anIntInTwoSeconds = Effect { callback in + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + callback(42) + callback(1729) + } +} + +anIntInTwoSeconds.run { int in print(int) } + +//anIntInTwoSeconds.map { $0 * $0 }.run { int in print(int) } + +import Combine + +//Publisher.init + +//AnyPublisher.init(<#T##publisher: Publisher##Publisher#>) + + +var count = 0 +let iterator = AnyIterator.init { + count += 1 + return count +} +Array(iterator.prefix(10)) + +let aFutureInt = Deferred { + Future { callback in + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + print("Hello from the future") + callback(.success(42)) + callback(.success(1729)) + } + } +} + +//aFutureInt.subscribe(AnySubscriber.init( +// receiveSubscription: { subscription in +// print("subscription") +// subscription.cancel() +// subscription.request(.unlimited) +//}, +// receiveValue: { value -> Subscribers.Demand in +// print("value", value) +// return .unlimited +//}, +// receiveCompletion: { completion in +// print("completion", completion) +//} +//)) + +let cancellable = aFutureInt.sink { int in + print(int) +} +//cancellable.cancel() + +//Subject.init + +let passthrough = PassthroughSubject.init() +let currentValue = CurrentValueSubject.init(2) + +let c1 = passthrough.sink { x in + print("passthrough", x) +} +let c2 = currentValue.sink { x in + print("currentValue", x) +} + +passthrough.send(42) +currentValue.send(1729) +passthrough.send(42) +currentValue.send(1729) diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..9aeae6da --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift @@ -0,0 +1,22 @@ +import ComposableArchitecture +@testable import Counter +import PlaygroundSupport +import SwiftUI + +var environment = CounterEnvironment.mock +environment.nthPrime = { _ in .sync { 7236893748932 }} + +PlaygroundPage.current.liveView = UIHostingController( + rootView: CounterView( + store: Store( + initialValue: CounterViewState( + alertNthPrime: nil, + count: 0, + favoritePrimes: [], + isNthPrimeButtonDisabled: false + ), + reducer: logging(counterViewReducer), + environment: environment + ) + ) +) diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..d921576e --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift @@ -0,0 +1,21 @@ +import ComposableArchitecture +@testable import FavoritePrimes +import PlaygroundSupport +import SwiftUI + +var environment = FavoritePrimesEnvironment.mock +environment.fileClient.load = { _ in + Effect.sync { try! JSONEncoder().encode(Array(1...10)) } +} + +PlaygroundPage.current.liveView = UIHostingController( + rootView: NavigationView { + FavoritePrimesView( + store: Store<[Int], FavoritePrimesAction>( + initialValue: [2, 3, 5, 7, 11], + reducer: favoritePrimesReducer, + environment: environment + ) + ) + } +) diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..98e2e625 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift @@ -0,0 +1,13 @@ +import ComposableArchitecture +import PlaygroundSupport +import PrimeModal +import SwiftUI + +PlaygroundPage.current.liveView = UIHostingController( + rootView: IsPrimeModalView( + store: Store( + initialValue: (2, [2, 3, 5]), + reducer: primeModalReducer + ) + ) +) diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Scratch.xcplaygroundpage/Contents.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Scratch.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..40ea3216 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/Pages/Scratch.xcplaygroundpage/Contents.swift @@ -0,0 +1,24 @@ +import ComposableArchitecture + +func pullback( + reducer: @escaping Reducer, + value: WritableKeyPath, + action: WritableKeyPath +) -> Reducer { + return { globalValue, globalAction in + guard let localAction = globalAction[keyPath: action] else { return [] } + let localEffects = reducer(&globalValue[keyPath: value], localAction) + return localEffects + .map { localEffect in + localEffect + .map { localAction in + var globalAction = globalAction + globalAction[keyPath: action] = localAction + return globalAction + } + .eraseToEffect() + } + } +} + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/contents.xcplayground b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/contents.xcplayground new file mode 100644 index 00000000..93344358 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.playground/contents.xcplayground @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.pbxproj b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.pbxproj new file mode 100644 index 00000000..e09bed43 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.pbxproj @@ -0,0 +1,2085 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + CA79FC28239C158C0096D881 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = CA79FC27239C158C0096D881 /* SnapshotTesting */; }; + CA79FC30239C23310096D881 /* PrimeTimeUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA79FC2F239C23310096D881 /* PrimeTimeUITests.swift */; }; + CAD67E2023329887000C7787 /* WolframAlpha.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC29217322FB231F006090DF /* WolframAlpha.swift */; }; + CADB167323DCC4A30052C18C /* CasePaths in Frameworks */ = {isa = PBXBuildFile; productRef = CADB167223DCC4A30052C18C /* CasePaths */; }; + DC36ED17234CD8040027F7A1 /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; + DC90717022FA102900B38B42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90716F22FA102900B38B42 /* AppDelegate.swift */; }; + DC90717222FA102900B38B42 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90717122FA102900B38B42 /* SceneDelegate.swift */; }; + DC90717422FA102900B38B42 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90717322FA102900B38B42 /* ContentView.swift */; }; + DC90717622FA102A00B38B42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC90717522FA102A00B38B42 /* Assets.xcassets */; }; + DC90717922FA102A00B38B42 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC90717822FA102A00B38B42 /* Preview Assets.xcassets */; }; + DC90717C22FA102A00B38B42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC90717A22FA102A00B38B42 /* LaunchScreen.storyboard */; }; + DC90718722FA102B00B38B42 /* PrimeTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90718622FA102B00B38B42 /* PrimeTimeTests.swift */; }; + DC90719222FA151D00B38B42 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90719122FA151D00B38B42 /* Util.swift */; }; + DCF0C5A42326032B008B45A0 /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; + DCF0C5AB2326032B008B45A0 /* ComposableArchitectureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C5AA2326032B008B45A0 /* ComposableArchitectureTests.swift */; }; + DCF0C5AD2326032B008B45A0 /* ComposableArchitecture.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C59D2326032B008B45A0 /* ComposableArchitecture.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C5B22326032B008B45A0 /* ComposableArchitecture.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C5C723260348008B45A0 /* Counter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5BE23260348008B45A0 /* Counter.framework */; }; + DCF0C5CE23260348008B45A0 /* CounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C5CD23260348008B45A0 /* CounterTests.swift */; }; + DCF0C5D023260348008B45A0 /* Counter.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C5C023260348008B45A0 /* Counter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C5D323260348008B45A0 /* Counter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5BE23260348008B45A0 /* Counter.framework */; }; + DCF0C5D423260348008B45A0 /* Counter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5BE23260348008B45A0 /* Counter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C5E92326035C008B45A0 /* PrimeModal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5E02326035C008B45A0 /* PrimeModal.framework */; }; + DCF0C5F02326035C008B45A0 /* PrimeModalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C5EF2326035C008B45A0 /* PrimeModalTests.swift */; }; + DCF0C5F22326035C008B45A0 /* PrimeModal.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C5E22326035C008B45A0 /* PrimeModal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C5F62326035C008B45A0 /* PrimeModal.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5E02326035C008B45A0 /* PrimeModal.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C60B2326036F008B45A0 /* FavoritePrimes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; }; + DCF0C6122326036F008B45A0 /* FavoritePrimesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C6112326036F008B45A0 /* FavoritePrimesTests.swift */; }; + DCF0C6142326036F008B45A0 /* FavoritePrimes.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C6042326036F008B45A0 /* FavoritePrimes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C6172326036F008B45A0 /* FavoritePrimes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; }; + DCF0C6182326036F008B45A0 /* FavoritePrimes.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C628232603B7008B45A0 /* ComposableArchitecture.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C627232603B7008B45A0 /* ComposableArchitecture.swift */; }; + DCF0C62A23260405008B45A0 /* FavoritePrimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C62923260405008B45A0 /* FavoritePrimes.swift */; }; + DCF0C62C2326040D008B45A0 /* PrimeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C62B2326040D008B45A0 /* PrimeModal.swift */; }; + DCF0C62E23260414008B45A0 /* Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C62D23260414008B45A0 /* Counter.swift */; }; + DCF6C51E23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF6C51C23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF6C52223ECD10100A54EE0 /* ComposableArchitectureTestSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA675A55238072DB00724A38 /* ComposableArchitectureTestSupport.swift */; }; + DCF6C52423ECD11200A54EE0 /* ComposableArchitectureSnapshotTesting.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF6C52323ECD11200A54EE0 /* ComposableArchitectureSnapshotTesting.swift */; }; + DCF6C52723ECD13600A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */; }; + DCF6C52A23ECD13D00A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */; }; + DCF6C52D23ECD14400A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */; }; + DCF6C53123ECD33000A54EE0 /* Effect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF6C53023ECD33000A54EE0 /* Effect.swift */; }; + DCF6C54523ECD8E800A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */; }; + DCF88EFF234D4A20001BA79A /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; + DCF88F00234D4A28001BA79A /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; + DCF88F01234D4A28001BA79A /* PrimeModal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5E02326035C008B45A0 /* PrimeModal.framework */; }; + DCF88F02234D4A3B001BA79A /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + CA79FC32239C23310096D881 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + CAD67E1C2332982E000C7787 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5DF2326035C008B45A0; + remoteInfo = PrimeModal; + }; + DC90718322FA102B00B38B42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C5A52326032B008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C5A72326032B008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C5AE2326032B008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C5C823260348008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5BD23260348008B45A0; + remoteInfo = Counter; + }; + DCF0C5CA23260348008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C5D123260348008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5BD23260348008B45A0; + remoteInfo = Counter; + }; + DCF0C5EA2326035C008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5DF2326035C008B45A0; + remoteInfo = PrimeModal; + }; + DCF0C5EC2326035C008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C60C2326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C6012326036F008B45A0; + remoteInfo = FavoritePrimes; + }; + DCF0C60E2326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C6152326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C6012326036F008B45A0; + remoteInfo = FavoritePrimes; + }; + DCF0C61F23260383008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C62123260387008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C6232326038A008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF6C52523ECD13200A54EE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF6C51923ECD08A00A54EE0; + remoteInfo = ComposableArchitectureTestSupport; + }; + DCF6C52823ECD13B00A54EE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF6C51923ECD08A00A54EE0; + remoteInfo = ComposableArchitectureTestSupport; + }; + DCF6C52B23ECD14100A54EE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF6C51923ECD08A00A54EE0; + remoteInfo = ComposableArchitectureTestSupport; + }; + DCF6C52E23ECD16A00A54EE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF6C54323ECD8DB00A54EE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF6C51923ECD08A00A54EE0; + remoteInfo = ComposableArchitectureTestSupport; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + DCF0C5B12326032B008B45A0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + DCF0C6182326036F008B45A0 /* FavoritePrimes.framework in Embed Frameworks */, + DCF0C5B22326032B008B45A0 /* ComposableArchitecture.framework in Embed Frameworks */, + DCF0C5D423260348008B45A0 /* Counter.framework in Embed Frameworks */, + DCF0C5F62326035C008B45A0 /* PrimeModal.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + CA675A55238072DB00724A38 /* ComposableArchitectureTestSupport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableArchitectureTestSupport.swift; sourceTree = ""; }; + CA79FC2D239C23310096D881 /* PrimeTimeUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrimeTimeUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + CA79FC2F239C23310096D881 /* PrimeTimeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeTimeUITests.swift; sourceTree = ""; }; + CA79FC31239C23310096D881 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DC29217322FB231F006090DF /* WolframAlpha.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WolframAlpha.swift; sourceTree = ""; }; + DC39325F230241AF005A0B0A /* PrimeTime.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = PrimeTime.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + DC90716C22FA102900B38B42 /* PrimeTime.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PrimeTime.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DC90716F22FA102900B38B42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + DC90717122FA102900B38B42 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + DC90717322FA102900B38B42 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + DC90717522FA102A00B38B42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DC90717822FA102A00B38B42 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + DC90717B22FA102A00B38B42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + DC90717D22FA102A00B38B42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DC90718222FA102B00B38B42 /* PrimeTimeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrimeTimeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DC90718622FA102B00B38B42 /* PrimeTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeTimeTests.swift; sourceTree = ""; }; + DC90718822FA102B00B38B42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DC90719122FA151D00B38B42 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; }; + DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ComposableArchitecture.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C59D2326032B008B45A0 /* ComposableArchitecture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ComposableArchitecture.h; sourceTree = ""; }; + DCF0C59E2326032B008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5A32326032B008B45A0 /* ComposableArchitectureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ComposableArchitectureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5AA2326032B008B45A0 /* ComposableArchitectureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableArchitectureTests.swift; sourceTree = ""; }; + DCF0C5AC2326032B008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5BE23260348008B45A0 /* Counter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Counter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5C023260348008B45A0 /* Counter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Counter.h; sourceTree = ""; }; + DCF0C5C123260348008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5C623260348008B45A0 /* CounterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CounterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5CD23260348008B45A0 /* CounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterTests.swift; sourceTree = ""; }; + DCF0C5CF23260348008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5E02326035C008B45A0 /* PrimeModal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrimeModal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5E22326035C008B45A0 /* PrimeModal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrimeModal.h; sourceTree = ""; }; + DCF0C5E32326035C008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5E82326035C008B45A0 /* PrimeModalTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrimeModalTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5EF2326035C008B45A0 /* PrimeModalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeModalTests.swift; sourceTree = ""; }; + DCF0C5F12326035C008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FavoritePrimes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C6042326036F008B45A0 /* FavoritePrimes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FavoritePrimes.h; sourceTree = ""; }; + DCF0C6052326036F008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C60A2326036F008B45A0 /* FavoritePrimesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FavoritePrimesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C6112326036F008B45A0 /* FavoritePrimesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritePrimesTests.swift; sourceTree = ""; }; + DCF0C6132326036F008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C627232603B7008B45A0 /* ComposableArchitecture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableArchitecture.swift; sourceTree = ""; }; + DCF0C62923260405008B45A0 /* FavoritePrimes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritePrimes.swift; sourceTree = ""; }; + DCF0C62B2326040D008B45A0 /* PrimeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeModal.swift; sourceTree = ""; }; + DCF0C62D23260414008B45A0 /* Counter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Counter.swift; sourceTree = ""; }; + DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ComposableArchitectureTestSupport.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF6C51C23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ComposableArchitectureTestSupport.h; sourceTree = ""; }; + DCF6C51D23ECD08A00A54EE0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF6C52323ECD11200A54EE0 /* ComposableArchitectureSnapshotTesting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableArchitectureSnapshotTesting.swift; sourceTree = ""; }; + DCF6C53023ECD33000A54EE0 /* Effect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Effect.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CA79FC2A239C23310096D881 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90716922FA102900B38B42 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF88F02234D4A3B001BA79A /* ComposableArchitecture.framework in Frameworks */, + DCF0C5D323260348008B45A0 /* Counter.framework in Frameworks */, + DCF0C6172326036F008B45A0 /* FavoritePrimes.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90717F22FA102B00B38B42 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C54523ECD8E800A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5982326032B008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CADB167323DCC4A30052C18C /* CasePaths in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5A02326032B008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5A42326032B008B45A0 /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5BB23260348008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF88F01234D4A28001BA79A /* PrimeModal.framework in Frameworks */, + DCF88F00234D4A28001BA79A /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5C323260348008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C52723ECD13600A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */, + DCF0C5C723260348008B45A0 /* Counter.framework in Frameworks */, + CA79FC28239C158C0096D881 /* SnapshotTesting in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DD2326035C008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF88EFF234D4A20001BA79A /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5E52326035C008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C52A23ECD13D00A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */, + DCF0C5E92326035C008B45A0 /* PrimeModal.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5FF2326036F008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DC36ED17234CD8040027F7A1 /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6072326036F008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C52D23ECD14400A54EE0 /* ComposableArchitectureTestSupport.framework in Frameworks */, + DCF0C60B2326036F008B45A0 /* FavoritePrimes.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF6C51723ECD08A00A54EE0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CA79FC2E239C23310096D881 /* PrimeTimeUITests */ = { + isa = PBXGroup; + children = ( + CA79FC31239C23310096D881 /* Info.plist */, + CA79FC2F239C23310096D881 /* PrimeTimeUITests.swift */, + ); + path = PrimeTimeUITests; + sourceTree = ""; + }; + DC36ED16234CD8040027F7A1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + DC90716322FA102900B38B42 = { + isa = PBXGroup; + children = ( + DC39325F230241AF005A0B0A /* PrimeTime.playground */, + DC90716E22FA102900B38B42 /* PrimeTime */, + DC90718522FA102B00B38B42 /* PrimeTimeTests */, + CA79FC2E239C23310096D881 /* PrimeTimeUITests */, + DCF0C59C2326032B008B45A0 /* ComposableArchitecture */, + DCF0C5A92326032B008B45A0 /* ComposableArchitectureTests */, + DCF6C51B23ECD08A00A54EE0 /* ComposableArchitectureTestSupport */, + DCF0C5BF23260348008B45A0 /* Counter */, + DCF0C5CC23260348008B45A0 /* CounterTests */, + DCF0C5E12326035C008B45A0 /* PrimeModal */, + DCF0C5EE2326035C008B45A0 /* PrimeModalTests */, + DCF0C6032326036F008B45A0 /* FavoritePrimes */, + DCF0C6102326036F008B45A0 /* FavoritePrimesTests */, + DC90716D22FA102900B38B42 /* Products */, + DC36ED16234CD8040027F7A1 /* Frameworks */, + ); + sourceTree = ""; + }; + DC90716D22FA102900B38B42 /* Products */ = { + isa = PBXGroup; + children = ( + DC90716C22FA102900B38B42 /* PrimeTime.app */, + DC90718222FA102B00B38B42 /* PrimeTimeTests.xctest */, + DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */, + DCF0C5A32326032B008B45A0 /* ComposableArchitectureTests.xctest */, + DCF0C5BE23260348008B45A0 /* Counter.framework */, + DCF0C5C623260348008B45A0 /* CounterTests.xctest */, + DCF0C5E02326035C008B45A0 /* PrimeModal.framework */, + DCF0C5E82326035C008B45A0 /* PrimeModalTests.xctest */, + DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */, + DCF0C60A2326036F008B45A0 /* FavoritePrimesTests.xctest */, + CA79FC2D239C23310096D881 /* PrimeTimeUITests.xctest */, + DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */, + ); + name = Products; + sourceTree = ""; + }; + DC90716E22FA102900B38B42 /* PrimeTime */ = { + isa = PBXGroup; + children = ( + DC90716F22FA102900B38B42 /* AppDelegate.swift */, + DC90717122FA102900B38B42 /* SceneDelegate.swift */, + DC90717322FA102900B38B42 /* ContentView.swift */, + DC90719122FA151D00B38B42 /* Util.swift */, + DC90717522FA102A00B38B42 /* Assets.xcassets */, + DC90717A22FA102A00B38B42 /* LaunchScreen.storyboard */, + DC90717D22FA102A00B38B42 /* Info.plist */, + DC90717722FA102A00B38B42 /* Preview Content */, + ); + path = PrimeTime; + sourceTree = ""; + }; + DC90717722FA102A00B38B42 /* Preview Content */ = { + isa = PBXGroup; + children = ( + DC90717822FA102A00B38B42 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + DC90718522FA102B00B38B42 /* PrimeTimeTests */ = { + isa = PBXGroup; + children = ( + DC90718822FA102B00B38B42 /* Info.plist */, + DC90718622FA102B00B38B42 /* PrimeTimeTests.swift */, + ); + path = PrimeTimeTests; + sourceTree = ""; + }; + DCF0C59C2326032B008B45A0 /* ComposableArchitecture */ = { + isa = PBXGroup; + children = ( + DCF0C59D2326032B008B45A0 /* ComposableArchitecture.h */, + DCF0C59E2326032B008B45A0 /* Info.plist */, + DCF0C627232603B7008B45A0 /* ComposableArchitecture.swift */, + DCF6C53023ECD33000A54EE0 /* Effect.swift */, + ); + path = ComposableArchitecture; + sourceTree = ""; + }; + DCF0C5A92326032B008B45A0 /* ComposableArchitectureTests */ = { + isa = PBXGroup; + children = ( + DCF0C5AC2326032B008B45A0 /* Info.plist */, + DCF0C5AA2326032B008B45A0 /* ComposableArchitectureTests.swift */, + ); + path = ComposableArchitectureTests; + sourceTree = ""; + }; + DCF0C5BF23260348008B45A0 /* Counter */ = { + isa = PBXGroup; + children = ( + DCF0C5C023260348008B45A0 /* Counter.h */, + DCF0C5C123260348008B45A0 /* Info.plist */, + DCF0C62D23260414008B45A0 /* Counter.swift */, + DC29217322FB231F006090DF /* WolframAlpha.swift */, + ); + path = Counter; + sourceTree = ""; + }; + DCF0C5CC23260348008B45A0 /* CounterTests */ = { + isa = PBXGroup; + children = ( + DCF0C5CD23260348008B45A0 /* CounterTests.swift */, + DCF0C5CF23260348008B45A0 /* Info.plist */, + DCF6C52323ECD11200A54EE0 /* ComposableArchitectureSnapshotTesting.swift */, + ); + path = CounterTests; + sourceTree = ""; + }; + DCF0C5E12326035C008B45A0 /* PrimeModal */ = { + isa = PBXGroup; + children = ( + DCF0C5E22326035C008B45A0 /* PrimeModal.h */, + DCF0C5E32326035C008B45A0 /* Info.plist */, + DCF0C62B2326040D008B45A0 /* PrimeModal.swift */, + ); + path = PrimeModal; + sourceTree = ""; + }; + DCF0C5EE2326035C008B45A0 /* PrimeModalTests */ = { + isa = PBXGroup; + children = ( + DCF0C5F12326035C008B45A0 /* Info.plist */, + DCF0C5EF2326035C008B45A0 /* PrimeModalTests.swift */, + ); + path = PrimeModalTests; + sourceTree = ""; + }; + DCF0C6032326036F008B45A0 /* FavoritePrimes */ = { + isa = PBXGroup; + children = ( + DCF0C6042326036F008B45A0 /* FavoritePrimes.h */, + DCF0C6052326036F008B45A0 /* Info.plist */, + DCF0C62923260405008B45A0 /* FavoritePrimes.swift */, + ); + path = FavoritePrimes; + sourceTree = ""; + }; + DCF0C6102326036F008B45A0 /* FavoritePrimesTests */ = { + isa = PBXGroup; + children = ( + DCF0C6132326036F008B45A0 /* Info.plist */, + DCF0C6112326036F008B45A0 /* FavoritePrimesTests.swift */, + ); + path = FavoritePrimesTests; + sourceTree = ""; + }; + DCF6C51B23ECD08A00A54EE0 /* ComposableArchitectureTestSupport */ = { + isa = PBXGroup; + children = ( + DCF6C51C23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.h */, + DCF6C51D23ECD08A00A54EE0 /* Info.plist */, + CA675A55238072DB00724A38 /* ComposableArchitectureTestSupport.swift */, + ); + path = ComposableArchitectureTestSupport; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + DCF0C5962326032B008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5AD2326032B008B45A0 /* ComposableArchitecture.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5B923260348008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5D023260348008B45A0 /* Counter.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DB2326035C008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5F22326035C008B45A0 /* PrimeModal.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5FD2326036F008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C6142326036F008B45A0 /* FavoritePrimes.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF6C51523ECD08A00A54EE0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C51E23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CA79FC2C239C23310096D881 /* PrimeTimeUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CA79FC34239C23310096D881 /* Build configuration list for PBXNativeTarget "PrimeTimeUITests" */; + buildPhases = ( + CA79FC29239C23310096D881 /* Sources */, + CA79FC2A239C23310096D881 /* Frameworks */, + CA79FC2B239C23310096D881 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CA79FC33239C23310096D881 /* PBXTargetDependency */, + ); + name = PrimeTimeUITests; + productName = PrimeTimeUITests; + productReference = CA79FC2D239C23310096D881 /* PrimeTimeUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + DC90716B22FA102900B38B42 /* PrimeTime */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC90718B22FA102B00B38B42 /* Build configuration list for PBXNativeTarget "PrimeTime" */; + buildPhases = ( + DC90716822FA102900B38B42 /* Sources */, + DC90716922FA102900B38B42 /* Frameworks */, + DC90716A22FA102900B38B42 /* Resources */, + DCF0C5B12326032B008B45A0 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C5AF2326032B008B45A0 /* PBXTargetDependency */, + DCF0C5D223260348008B45A0 /* PBXTargetDependency */, + DCF0C6162326036F008B45A0 /* PBXTargetDependency */, + ); + name = PrimeTime; + productName = PrimeTime; + productReference = DC90716C22FA102900B38B42 /* PrimeTime.app */; + productType = "com.apple.product-type.application"; + }; + DC90718122FA102B00B38B42 /* PrimeTimeTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC90718E22FA102B00B38B42 /* Build configuration list for PBXNativeTarget "PrimeTimeTests" */; + buildPhases = ( + DC90717E22FA102B00B38B42 /* Sources */, + DC90717F22FA102B00B38B42 /* Frameworks */, + DC90718022FA102B00B38B42 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF6C54423ECD8DB00A54EE0 /* PBXTargetDependency */, + DC90718422FA102B00B38B42 /* PBXTargetDependency */, + ); + name = PrimeTimeTests; + productName = PrimeTimeTests; + productReference = DC90718222FA102B00B38B42 /* PrimeTimeTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF0C59A2326032B008B45A0 /* ComposableArchitecture */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5B72326032B008B45A0 /* Build configuration list for PBXNativeTarget "ComposableArchitecture" */; + buildPhases = ( + DCF0C5962326032B008B45A0 /* Headers */, + DCF0C5972326032B008B45A0 /* Sources */, + DCF0C5982326032B008B45A0 /* Frameworks */, + DCF0C5992326032B008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ComposableArchitecture; + packageProductDependencies = ( + CADB167223DCC4A30052C18C /* CasePaths */, + ); + productName = ComposableArchitecture; + productReference = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C5A22326032B008B45A0 /* ComposableArchitectureTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5B82326032B008B45A0 /* Build configuration list for PBXNativeTarget "ComposableArchitectureTests" */; + buildPhases = ( + DCF0C59F2326032B008B45A0 /* Sources */, + DCF0C5A02326032B008B45A0 /* Frameworks */, + DCF0C5A12326032B008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C5A62326032B008B45A0 /* PBXTargetDependency */, + DCF0C5A82326032B008B45A0 /* PBXTargetDependency */, + ); + name = ComposableArchitectureTests; + productName = ComposableArchitectureTests; + productReference = DCF0C5A32326032B008B45A0 /* ComposableArchitectureTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF0C5BD23260348008B45A0 /* Counter */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5D523260348008B45A0 /* Build configuration list for PBXNativeTarget "Counter" */; + buildPhases = ( + DCF0C5B923260348008B45A0 /* Headers */, + DCF0C5BA23260348008B45A0 /* Sources */, + DCF0C5BB23260348008B45A0 /* Frameworks */, + DCF0C5BC23260348008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CAD67E1D2332982E000C7787 /* PBXTargetDependency */, + DCF0C6242326038A008B45A0 /* PBXTargetDependency */, + ); + name = Counter; + productName = Counter; + productReference = DCF0C5BE23260348008B45A0 /* Counter.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C5C523260348008B45A0 /* CounterTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5D823260348008B45A0 /* Build configuration list for PBXNativeTarget "CounterTests" */; + buildPhases = ( + DCF0C5C223260348008B45A0 /* Sources */, + DCF0C5C323260348008B45A0 /* Frameworks */, + DCF0C5C423260348008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF6C52623ECD13200A54EE0 /* PBXTargetDependency */, + DCF0C5C923260348008B45A0 /* PBXTargetDependency */, + DCF0C5CB23260348008B45A0 /* PBXTargetDependency */, + ); + name = CounterTests; + packageProductDependencies = ( + CA79FC27239C158C0096D881 /* SnapshotTesting */, + ); + productName = CounterTests; + productReference = DCF0C5C623260348008B45A0 /* CounterTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF0C5DF2326035C008B45A0 /* PrimeModal */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5F72326035C008B45A0 /* Build configuration list for PBXNativeTarget "PrimeModal" */; + buildPhases = ( + DCF0C5DB2326035C008B45A0 /* Headers */, + DCF0C5DC2326035C008B45A0 /* Sources */, + DCF0C5DD2326035C008B45A0 /* Frameworks */, + DCF0C5DE2326035C008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C62223260387008B45A0 /* PBXTargetDependency */, + ); + name = PrimeModal; + productName = PrimeModal; + productReference = DCF0C5E02326035C008B45A0 /* PrimeModal.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C5E72326035C008B45A0 /* PrimeModalTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5FA2326035C008B45A0 /* Build configuration list for PBXNativeTarget "PrimeModalTests" */; + buildPhases = ( + DCF0C5E42326035C008B45A0 /* Sources */, + DCF0C5E52326035C008B45A0 /* Frameworks */, + DCF0C5E62326035C008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF6C52923ECD13B00A54EE0 /* PBXTargetDependency */, + DCF0C5EB2326035C008B45A0 /* PBXTargetDependency */, + DCF0C5ED2326035C008B45A0 /* PBXTargetDependency */, + ); + name = PrimeModalTests; + productName = PrimeModalTests; + productReference = DCF0C5E82326035C008B45A0 /* PrimeModalTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF0C6012326036F008B45A0 /* FavoritePrimes */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C6192326036F008B45A0 /* Build configuration list for PBXNativeTarget "FavoritePrimes" */; + buildPhases = ( + DCF0C5FD2326036F008B45A0 /* Headers */, + DCF0C5FE2326036F008B45A0 /* Sources */, + DCF0C5FF2326036F008B45A0 /* Frameworks */, + DCF0C6002326036F008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C62023260383008B45A0 /* PBXTargetDependency */, + ); + name = FavoritePrimes; + productName = FavoritePrimes; + productReference = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C6092326036F008B45A0 /* FavoritePrimesTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C61C2326036F008B45A0 /* Build configuration list for PBXNativeTarget "FavoritePrimesTests" */; + buildPhases = ( + DCF0C6062326036F008B45A0 /* Sources */, + DCF0C6072326036F008B45A0 /* Frameworks */, + DCF0C6082326036F008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF6C52C23ECD14100A54EE0 /* PBXTargetDependency */, + DCF0C60D2326036F008B45A0 /* PBXTargetDependency */, + DCF0C60F2326036F008B45A0 /* PBXTargetDependency */, + ); + name = FavoritePrimesTests; + productName = FavoritePrimesTests; + productReference = DCF0C60A2326036F008B45A0 /* FavoritePrimesTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF6C51923ECD08A00A54EE0 /* ComposableArchitectureTestSupport */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF6C52123ECD08A00A54EE0 /* Build configuration list for PBXNativeTarget "ComposableArchitectureTestSupport" */; + buildPhases = ( + DCF6C51523ECD08A00A54EE0 /* Headers */, + DCF6C51623ECD08A00A54EE0 /* Sources */, + DCF6C51723ECD08A00A54EE0 /* Frameworks */, + DCF6C51823ECD08A00A54EE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF6C52F23ECD16A00A54EE0 /* PBXTargetDependency */, + ); + name = ComposableArchitectureTestSupport; + productName = ComposableArchitectureTestSupport; + productReference = DCF6C51A23ECD08A00A54EE0 /* ComposableArchitectureTestSupport.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DC90716422FA102900B38B42 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1120; + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "Point-Free"; + TargetAttributes = { + CA79FC2C239C23310096D881 = { + CreatedOnToolsVersion = 11.2.1; + TestTargetID = DC90716B22FA102900B38B42; + }; + DC90716B22FA102900B38B42 = { + CreatedOnToolsVersion = 11.0; + }; + DC90718122FA102B00B38B42 = { + CreatedOnToolsVersion = 11.0; + TestTargetID = DC90716B22FA102900B38B42; + }; + DCF0C59A2326032B008B45A0 = { + CreatedOnToolsVersion = 11.0; + LastSwiftMigration = 1100; + }; + DCF0C5A22326032B008B45A0 = { + CreatedOnToolsVersion = 11.0; + TestTargetID = DC90716B22FA102900B38B42; + }; + DCF0C5BD23260348008B45A0 = { + CreatedOnToolsVersion = 11.0; + LastSwiftMigration = 1100; + }; + DCF0C5C523260348008B45A0 = { + CreatedOnToolsVersion = 11.0; + TestTargetID = DC90716B22FA102900B38B42; + }; + DCF0C5DF2326035C008B45A0 = { + CreatedOnToolsVersion = 11.0; + LastSwiftMigration = 1100; + }; + DCF0C5E72326035C008B45A0 = { + CreatedOnToolsVersion = 11.0; + }; + DCF0C6012326036F008B45A0 = { + CreatedOnToolsVersion = 11.0; + LastSwiftMigration = 1100; + }; + DCF0C6092326036F008B45A0 = { + CreatedOnToolsVersion = 11.0; + }; + DCF6C51923ECD08A00A54EE0 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = DC90716722FA102900B38B42 /* Build configuration list for PBXProject "PrimeTime" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DC90716322FA102900B38B42; + packageReferences = ( + CA79FC26239C158C0096D881 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, + CADB167123DCC4A30052C18C /* XCRemoteSwiftPackageReference "swift-case-paths" */, + ); + productRefGroup = DC90716D22FA102900B38B42 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DC90716B22FA102900B38B42 /* PrimeTime */, + DC90718122FA102B00B38B42 /* PrimeTimeTests */, + CA79FC2C239C23310096D881 /* PrimeTimeUITests */, + DCF0C59A2326032B008B45A0 /* ComposableArchitecture */, + DCF0C5A22326032B008B45A0 /* ComposableArchitectureTests */, + DCF6C51923ECD08A00A54EE0 /* ComposableArchitectureTestSupport */, + DCF0C5BD23260348008B45A0 /* Counter */, + DCF0C5C523260348008B45A0 /* CounterTests */, + DCF0C5DF2326035C008B45A0 /* PrimeModal */, + DCF0C5E72326035C008B45A0 /* PrimeModalTests */, + DCF0C6012326036F008B45A0 /* FavoritePrimes */, + DCF0C6092326036F008B45A0 /* FavoritePrimesTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CA79FC2B239C23310096D881 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90716A22FA102900B38B42 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC90717C22FA102A00B38B42 /* LaunchScreen.storyboard in Resources */, + DC90717922FA102A00B38B42 /* Preview Assets.xcassets in Resources */, + DC90717622FA102A00B38B42 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90718022FA102B00B38B42 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5992326032B008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5A12326032B008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5BC23260348008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5C423260348008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DE2326035C008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5E62326035C008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6002326036F008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6082326036F008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF6C51823ECD08A00A54EE0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + CA79FC29239C23310096D881 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CA79FC30239C23310096D881 /* PrimeTimeUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90716822FA102900B38B42 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC90717022FA102900B38B42 /* AppDelegate.swift in Sources */, + DC90717222FA102900B38B42 /* SceneDelegate.swift in Sources */, + DC90717422FA102900B38B42 /* ContentView.swift in Sources */, + DC90719222FA151D00B38B42 /* Util.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90717E22FA102B00B38B42 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC90718722FA102B00B38B42 /* PrimeTimeTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5972326032B008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C53123ECD33000A54EE0 /* Effect.swift in Sources */, + DCF0C628232603B7008B45A0 /* ComposableArchitecture.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C59F2326032B008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5AB2326032B008B45A0 /* ComposableArchitectureTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5BA23260348008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C62E23260414008B45A0 /* Counter.swift in Sources */, + CAD67E2023329887000C7787 /* WolframAlpha.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5C223260348008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C52423ECD11200A54EE0 /* ComposableArchitectureSnapshotTesting.swift in Sources */, + DCF0C5CE23260348008B45A0 /* CounterTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DC2326035C008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C62C2326040D008B45A0 /* PrimeModal.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5E42326035C008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5F02326035C008B45A0 /* PrimeModalTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5FE2326036F008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C62A23260405008B45A0 /* FavoritePrimes.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6062326036F008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C6122326036F008B45A0 /* FavoritePrimesTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF6C51623ECD08A00A54EE0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF6C52223ECD10100A54EE0 /* ComposableArchitectureTestSupport.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + CA79FC33239C23310096D881 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = CA79FC32239C23310096D881 /* PBXContainerItemProxy */; + }; + CAD67E1D2332982E000C7787 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5DF2326035C008B45A0 /* PrimeModal */; + targetProxy = CAD67E1C2332982E000C7787 /* PBXContainerItemProxy */; + }; + DC90718422FA102B00B38B42 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DC90718322FA102B00B38B42 /* PBXContainerItemProxy */; + }; + DCF0C5A62326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C5A52326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5A82326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C5A72326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5AF2326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C5AE2326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5C923260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5BD23260348008B45A0 /* Counter */; + targetProxy = DCF0C5C823260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5CB23260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C5CA23260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5D223260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5BD23260348008B45A0 /* Counter */; + targetProxy = DCF0C5D123260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5EB2326035C008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5DF2326035C008B45A0 /* PrimeModal */; + targetProxy = DCF0C5EA2326035C008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5ED2326035C008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C5EC2326035C008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C60D2326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C6012326036F008B45A0 /* FavoritePrimes */; + targetProxy = DCF0C60C2326036F008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C60F2326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C60E2326036F008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C6162326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C6012326036F008B45A0 /* FavoritePrimes */; + targetProxy = DCF0C6152326036F008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C62023260383008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C61F23260383008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C62223260387008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C62123260387008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C6242326038A008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C6232326038A008B45A0 /* PBXContainerItemProxy */; + }; + DCF6C52623ECD13200A54EE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF6C51923ECD08A00A54EE0 /* ComposableArchitectureTestSupport */; + targetProxy = DCF6C52523ECD13200A54EE0 /* PBXContainerItemProxy */; + }; + DCF6C52923ECD13B00A54EE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF6C51923ECD08A00A54EE0 /* ComposableArchitectureTestSupport */; + targetProxy = DCF6C52823ECD13B00A54EE0 /* PBXContainerItemProxy */; + }; + DCF6C52C23ECD14100A54EE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF6C51923ECD08A00A54EE0 /* ComposableArchitectureTestSupport */; + targetProxy = DCF6C52B23ECD14100A54EE0 /* PBXContainerItemProxy */; + }; + DCF6C52F23ECD16A00A54EE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF6C52E23ECD16A00A54EE0 /* PBXContainerItemProxy */; + }; + DCF6C54423ECD8DB00A54EE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF6C51923ECD08A00A54EE0 /* ComposableArchitectureTestSupport */; + targetProxy = DCF6C54323ECD8DB00A54EE0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + DC90717A22FA102A00B38B42 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DC90717B22FA102A00B38B42 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + CA79FC35239C23310096D881 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeTimeUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.PrimeTimeUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = PrimeTime; + }; + name = Debug; + }; + CA79FC36239C23310096D881 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeTimeUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.PrimeTimeUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = PrimeTime; + }; + name = Release; + }; + DC90718922FA102B00B38B42 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DC90718A22FA102B00B38B42 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + DC90718C22FA102B00B38B42 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "PrimeTime/Preview\\ Content"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = PrimeTime/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTime; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DC90718D22FA102B00B38B42 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "PrimeTime/Preview\\ Content"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = PrimeTime/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTime; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + DC90718F22FA102B00B38B42 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeTimeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTimeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DC90719022FA102B00B38B42 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeTimeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTimeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C5B32326032B008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = ComposableArchitecture/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitecture; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C5B42326032B008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = ComposableArchitecture/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitecture; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C5B52326032B008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ComposableArchitectureTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitectureTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DCF0C5B62326032B008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ComposableArchitectureTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitectureTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C5D623260348008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Counter/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.Counter; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C5D723260348008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Counter/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.Counter; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C5D923260348008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CounterTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.CounterTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DCF0C5DA23260348008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CounterTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.CounterTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C5F82326035C008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = PrimeModal/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModal; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C5F92326035C008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = PrimeModal/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModal; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C5FB2326035C008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeModalTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModalTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DCF0C5FC2326035C008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeModalTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModalTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + DCF0C61A2326036F008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FavoritePrimes/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimes; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C61B2326036F008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FavoritePrimes/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimes; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C61D2326036F008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = FavoritePrimesTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DCF0C61E2326036F008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = FavoritePrimesTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + DCF6C51F23ECD08A00A54EE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + INFOPLIST_FILE = ComposableArchitectureTestSupport/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-weak_framework", + XCTest, + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitectureTestSupport; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF6C52023ECD08A00A54EE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(PLATFORM_DIR)/Developer/Library/Frameworks", + ); + INFOPLIST_FILE = ComposableArchitectureTestSupport/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-weak_framework", + XCTest, + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitectureTestSupport; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CA79FC34239C23310096D881 /* Build configuration list for PBXNativeTarget "PrimeTimeUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CA79FC35239C23310096D881 /* Debug */, + CA79FC36239C23310096D881 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC90716722FA102900B38B42 /* Build configuration list for PBXProject "PrimeTime" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC90718922FA102B00B38B42 /* Debug */, + DC90718A22FA102B00B38B42 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC90718B22FA102B00B38B42 /* Build configuration list for PBXNativeTarget "PrimeTime" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC90718C22FA102B00B38B42 /* Debug */, + DC90718D22FA102B00B38B42 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DC90718E22FA102B00B38B42 /* Build configuration list for PBXNativeTarget "PrimeTimeTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC90718F22FA102B00B38B42 /* Debug */, + DC90719022FA102B00B38B42 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C5B72326032B008B45A0 /* Build configuration list for PBXNativeTarget "ComposableArchitecture" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C5B32326032B008B45A0 /* Debug */, + DCF0C5B42326032B008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C5B82326032B008B45A0 /* Build configuration list for PBXNativeTarget "ComposableArchitectureTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C5B52326032B008B45A0 /* Debug */, + DCF0C5B62326032B008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C5D523260348008B45A0 /* Build configuration list for PBXNativeTarget "Counter" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C5D623260348008B45A0 /* Debug */, + DCF0C5D723260348008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C5D823260348008B45A0 /* Build configuration list for PBXNativeTarget "CounterTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C5D923260348008B45A0 /* Debug */, + DCF0C5DA23260348008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C5F72326035C008B45A0 /* Build configuration list for PBXNativeTarget "PrimeModal" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C5F82326035C008B45A0 /* Debug */, + DCF0C5F92326035C008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C5FA2326035C008B45A0 /* Build configuration list for PBXNativeTarget "PrimeModalTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C5FB2326035C008B45A0 /* Debug */, + DCF0C5FC2326035C008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C6192326036F008B45A0 /* Build configuration list for PBXNativeTarget "FavoritePrimes" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C61A2326036F008B45A0 /* Debug */, + DCF0C61B2326036F008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF0C61C2326036F008B45A0 /* Build configuration list for PBXNativeTarget "FavoritePrimesTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF0C61D2326036F008B45A0 /* Debug */, + DCF0C61E2326036F008B45A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCF6C52123ECD08A00A54EE0 /* Build configuration list for PBXNativeTarget "ComposableArchitectureTestSupport" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCF6C51F23ECD08A00A54EE0 /* Debug */, + DCF6C52023ECD08A00A54EE0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CA79FC26239C158C0096D881 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.6.0; + }; + }; + CADB167123DCC4A30052C18C /* XCRemoteSwiftPackageReference "swift-case-paths" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-case-paths"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.1.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CA79FC27239C158C0096D881 /* SnapshotTesting */ = { + isa = XCSwiftPackageProductDependency; + package = CA79FC26239C158C0096D881 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; + productName = SnapshotTesting; + }; + CADB167223DCC4A30052C18C /* CasePaths */ = { + isa = XCSwiftPackageProductDependency; + package = CADB167123DCC4A30052C18C /* XCRemoteSwiftPackageReference "swift-case-paths" */; + productName = CasePaths; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = DC90716422FA102900B38B42 /* Project object */; +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/ComposableArchitecture.xcscheme b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/ComposableArchitecture.xcscheme new file mode 100644 index 00000000..58aa1d24 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/ComposableArchitecture.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/Counter.xcscheme b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/Counter.xcscheme new file mode 100644 index 00000000..e0fc8fb9 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/Counter.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/FavoritePrimes.xcscheme b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/FavoritePrimes.xcscheme new file mode 100644 index 00000000..02e7ebd3 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/FavoritePrimes.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeModal.xcscheme b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeModal.xcscheme new file mode 100644 index 00000000..73d691d1 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeModal.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeTime.xcscheme b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeTime.xcscheme new file mode 100644 index 00000000..d64f316c --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeTime.xcscheme @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/AppDelegate.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/AppDelegate.swift new file mode 100644 index 00000000..3ba17b06 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/AppDelegate.swift @@ -0,0 +1,19 @@ +import UIKit +@testable import Counter + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Assets.xcassets/Contents.json b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/ContentView.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/ContentView.swift new file mode 100644 index 00000000..6a078915 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/ContentView.swift @@ -0,0 +1,140 @@ +import CasePaths +import Combine +import ComposableArchitecture +import Counter +import FavoritePrimes +import SwiftUI + +struct AppState: Equatable { + var count = 0 + var favoritePrimes: [Int] = [] + var loggedInUser: User? = nil + var activityFeed: [Activity] = [] + var alertNthPrime: PrimeAlert? = nil + var isNthPrimeButtonDisabled: Bool = false + var isPrimeModalShown: Bool = false + + struct Activity: Equatable { + let timestamp: Date + let type: ActivityType + + enum ActivityType: Equatable { + case addedFavoritePrime(Int) + case removedFavoritePrime(Int) + } + } + + struct User: Equatable { + let id: Int + let name: String + let bio: String + } +} + +enum AppAction: Equatable { + case counterView(CounterViewAction) + case favoritePrimes(FavoritePrimesAction) +} + +extension AppState { + var counterView: CounterViewState { + get { + CounterViewState( + alertNthPrime: self.alertNthPrime, + count: self.count, + favoritePrimes: self.favoritePrimes, + isNthPrimeButtonDisabled: self.isNthPrimeButtonDisabled, + isPrimeModalShown: self.isPrimeModalShown + ) + } + set { + self.alertNthPrime = newValue.alertNthPrime + self.count = newValue.count + self.favoritePrimes = newValue.favoritePrimes + self.isNthPrimeButtonDisabled = newValue.isNthPrimeButtonDisabled + self.isPrimeModalShown = newValue.isPrimeModalShown + } + } +} + +//struct AppEnvironment { +// var counter: CounterEnvironment +// var favoritePrimes: FavoritePrimesEnvironment +//} + +typealias AppEnvironment = ( + fileClient: FileClient, + nthPrime: (Int) -> Effect +) + +let appReducer: Reducer = combine( + pullback( + counterViewReducer, + value: \AppState.counterView, + action: /AppAction.counterView, + environment: { $0.nthPrime } + ), + pullback( + favoritePrimesReducer, + value: \.favoritePrimes, + action: /AppAction.favoritePrimes, + environment: { $0.fileClient } + ) +) + +func activityFeed( + _ reducer: @escaping Reducer +) -> Reducer { + + return { state, action, environment in + switch action { + case .counterView(.counter), + .favoritePrimes(.loadedFavoritePrimes), + .favoritePrimes(.loadButtonTapped), + .favoritePrimes(.saveButtonTapped): + break + case .counterView(.primeModal(.removeFavoritePrimeTapped)): + state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.count))) + + case .counterView(.primeModal(.saveFavoritePrimeTapped)): + state.activityFeed.append(.init(timestamp: Date(), type: .addedFavoritePrime(state.count))) + + case let .favoritePrimes(.deleteFavoritePrimes(indexSet)): + for index in indexSet { + state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.favoritePrimes[index]))) + } + } + + return reducer(&state, action, environment) + } +} + +struct ContentView: View { + @ObservedObject var store: Store + + var body: some View { + NavigationView { + List { + NavigationLink( + "Counter demo", + destination: CounterView( + store: self.store.view( + value: { $0.counterView }, + action: { .counterView($0) } + ) + ) + ) + NavigationLink( + "Favorite primes", + destination: FavoritePrimesView( + store: self.store.view( + value: { $0.favoritePrimes }, + action: { .favoritePrimes($0) } + ) + ) + ) + } + .navigationBarTitle("State management") + } + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Info.plist new file mode 100644 index 00000000..9742bf0f --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/SceneDelegate.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/SceneDelegate.swift new file mode 100644 index 00000000..b73516cf --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/SceneDelegate.swift @@ -0,0 +1,36 @@ +import ComposableArchitecture +import Counter +import SwiftUI +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController( + rootView: ContentView( + store: Store( + initialValue: AppState(), + reducer: with( + appReducer, + compose( + logging, + activityFeed + ) + ), + environment: AppEnvironment( + fileClient: .live, + nthPrime: Counter.nthPrime +// counter: .live, +// favoritePrimes: .live + ) + ) + ) + ) + self.window = window + window.makeKeyAndVisible() + } + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Util.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Util.swift new file mode 100644 index 00000000..b9509ff8 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTime/Util.swift @@ -0,0 +1,14 @@ +func compose( + _ f: @escaping (B) -> C, + _ g: @escaping (A) -> B + ) + -> (A) -> C { + + return { (a: A) -> C in + f(g(a)) + } +} + +func with(_ a: A, _ f: (A) throws -> B) rethrows -> B { + return try f(a) +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeTests/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift new file mode 100644 index 00000000..3df438c3 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift @@ -0,0 +1,40 @@ +import XCTest +@testable import PrimeTime +import ComposableArchitecture +@testable import Counter +@testable import FavoritePrimes +@testable import PrimeModal +import ComposableArchitectureTestSupport + +class PrimeTimeTests: XCTestCase { + func testIntegration() { +// Counter.Current = .mock +// FavoritePrimes.Current = .mock + + var fileClient = FileClient.mock + fileClient.load = { _ in Effect.sync { + try! JSONEncoder().encode([2, 31, 7]) + } } + + assert( + initialValue: AppState(count: 4), + reducer: appReducer, + environment: ( + fileClient: fileClient, + nthPrime: { _ in .sync { 17 } } + ), + steps: + Step(.send, .counterView(.counter(.nthPrimeButtonTapped))) { + $0.isNthPrimeButtonDisabled = true + }, + Step(.receive, .counterView(.counter(.nthPrimeResponse(n: 4, prime: 17)))) { + $0.isNthPrimeButtonDisabled = false + $0.alertNthPrime = PrimeAlert(n: 4, prime: 17) + }, + Step(.send, .favoritePrimes(.loadButtonTapped)), + Step(.receive, .favoritePrimes(.loadedFavoritePrimes([2, 31, 7]))) { + $0.favoritePrimes = [2, 31, 7] + } + ) + } +} diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeUITests/Info.plist b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeUITests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeUITests/PrimeTimeUITests.swift b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeUITests/PrimeTimeUITests.swift new file mode 100644 index 00000000..bac2c351 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/PrimeTime/PrimeTimeUITests/PrimeTimeUITests.swift @@ -0,0 +1,31 @@ +//import XCTest +//@testable import Counter +// +//class PrimeTimeUITests: XCTestCase { +// override func setUp() { +// continueAfterFailure = false +// Current = .mock +// } +// +// func testExample() { +// let app = XCUIApplication() +// app.launchEnvironment["UI_TESTS"] = "1" +// app.launchEnvironment["UNHAPPY_PATHS"] = "1" +// app.launch() +// +// app.tables.buttons["Counter demo"].tap() +// +// let button = app.buttons["+"] +// button.tap() +// XCTAssert(app.staticTexts["1"].exists) +// button.tap() +// XCTAssert(app.staticTexts["2"].exists) +// app.buttons["What is the 2nd prime?"].tap() +// let alert = app.alerts["The 2nd prime is 3"] +// XCTAssert(alert.waitForExistence(timeout: 5)) +// alert.scrollViews.otherElements.buttons["Ok"].tap() +// app.buttons["Is this prime?"].tap() +// app.buttons["Save to favorite primes"].tap() +// app.children(matching: .window).element(boundBy: 0).children(matching: .other).element.children(matching: .other).element(boundBy: 0).swipeDown() +// } +//} diff --git a/0092-modular-dependency-injection-pt2/README.md b/0092-modular-dependency-injection-pt2/README.md new file mode 100644 index 00000000..715afe36 --- /dev/null +++ b/0092-modular-dependency-injection-pt2/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Dependency Injection Made Modular](https://www.pointfree.co/episodes/ep92-dependency-injection-made-modular) +> +> Now that we’ve baked the “environment” of dependencies directly into the composable architecture, we’re ready to refactor our app’s frameworks and tests to work with them in a modular and more lightweight way. diff --git a/README.md b/README.md index f3dff778..469c9365 100644 --- a/README.md +++ b/README.md @@ -93,3 +93,5 @@ This repository is the home of code written on episodes of 1. [The Case for Case Paths: Properties](0088-the-case-for-case-paths-pt2) 1. [Case Paths for Free](0089-the-case-for-case-paths-pt3) 1. [Composing Architecture with Case Paths](0090-composing-architecture-with-case-paths) +1. [Dependency Injection Made Composable](0091-modular-dependency-injection-pt1) +1. [Dependency Injection Made Modular](0092-modular-dependency-injection-pt2)