diff --git a/0098-ergonomic-state-management-pt1/README.md b/0098-ergonomic-state-management-pt1/README.md index df680332..fad2e198 100644 --- a/0098-ergonomic-state-management-pt1/README.md +++ b/0098-ergonomic-state-management-pt1/README.md @@ -1,5 +1,5 @@ ## [Point-Free](https://www.pointfree.co) -> #### This directory contains code from Point-Free Episode: [Adaptive State Management: The Point](https://www.pointfree.co/episodes/ep98-ergonomic-state-management-part-1) +> #### This directory contains code from Point-Free Episode: [Ergonomic State Management: Part 1](https://www.pointfree.co/episodes/ep98-ergonomic-state-management-part-1) > -> Over many episodes we've now built out an architecture that not only solves the problems we set out to solve, but we've made it performant and adaptable. But before sharing it with everyone, let's polish it with a focus on ergonomics. +> The Composable Architecture is robust and solves all of the problems we set out to solve (and more), but we haven't given enough attention to ergonomics. We will enhance one of its core units to be a little friendlier to use and extend, which will bring us one step closing to being ready for production. diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.h b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.h new file mode 100644 index 00000000..38201c51 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift new file mode 100644 index 00000000..eac0d483 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift @@ -0,0 +1,225 @@ +import CasePaths +import Combine +import SwiftUI + +//(inout RandomNumberGenerator) -> A +struct Gen { + let run: (inout RandomNumberGenerator) -> A +} + +//(inout Substring) -> A? +struct Parser { + let run: (inout Substring) -> A? +} + +//(@escaping (A) -> Void) -> Void +//struct Effect { +// let run: (@escaping (A) -> Void) -> Void +//} + +//public typealias Reducer = (inout Value, Action, Environment) -> [Effect] +public struct Reducer { + let reducer: (inout Value, Action, Environment) -> [Effect] + + public init(_ reducer: @escaping (inout Value, Action, Environment) -> [Effect]) { + self.reducer = reducer + } +} + +extension Reducer { + public func callAsFunction(_ value: inout Value, _ action: Action, _ environment: Environment) -> [Effect] { + self.reducer(&value, action, environment) + } +} + +extension Reducer { + public static func combine(_ reducers: Reducer...) -> Reducer { + .init { value, action, environment in + let effects = reducers.flatMap { $0(&value, action, environment) } + return effects + } + } +} + +//public func combine( +// _ reducers: Reducer... +//) -> Reducer { +// .init { value, action, environment in +// let effects = reducers.flatMap { $0(&value, action, environment) } +// return effects +// } +//} + +extension Reducer { + public func pullback( + value: WritableKeyPath, + action: CasePath, + environment: @escaping (GlobalEnvironment) -> Environment + ) -> Reducer { + .init { globalValue, globalAction, globalEnvironment in + guard let localAction = action.extract(from: globalAction) else { return [] } + let localEffects = self(&globalValue[keyPath: value], localAction, environment(globalEnvironment)) + + return localEffects.map { localEffect in + localEffect.map(action.embed) + .eraseToEffect() + } + } + } +} + +//public func pullback( +// _ reducer: Reducer, +// value: WritableKeyPath, +// action: CasePath, +// environment: @escaping (GlobalEnvironment) -> LocalEnvironment +//) -> Reducer { +// return .init { 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() +// } +// } +//} + +extension Reducer { + public func logging( + printer: @escaping (Environment) -> (String) -> Void = { _ in { print($0) } } + ) -> Reducer { + .init { value, action, environment in + let effects = self(&value, action, environment) + let newValue = value + let print = printer(environment) + return [.fireAndForget { + print("Action: \(action)") + print("Value:") + var dumpedNewValue = "" + dump(newValue, to: &dumpedNewValue) + print(dumpedNewValue) + print("---") + }] + effects + } + } +} + +//public func logging( +// _ reducer: Reducer +//) -> Reducer { +// return .init { 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 { + private let reducer: Reducer + private let environment: Any + @Published private var value: Value + private var viewCancellable: Cancellable? + private var effectCancellables: Set = [] + + public init( + initialValue: Value, + reducer: Reducer, + environment: Environment + ) { + self.reducer = .init { value, action, environment in + reducer(&value, action, environment as! Environment) + } + self.value = initialValue + self.environment = environment + } + + private 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, weak effectCancellable] _ in + didComplete = true + guard let effectCancellable = effectCancellable else { return } + self?.effectCancellables.remove(effectCancellable) + }, + receiveValue: { [weak self] in self?.send($0) } + ) + if !didComplete, let effectCancellable = effectCancellable { + self.effectCancellables.insert(effectCancellable) + } + } + } + + public func scope( + value toLocalValue: @escaping (Value) -> LocalValue, + action toGlobalAction: @escaping (LocalAction) -> Action + ) -> Store { + let localStore = Store( + initialValue: toLocalValue(self.value), + reducer: .init { localValue, localAction, _ in + self.send(toGlobalAction(localAction)) + localValue = toLocalValue(self.value) + return [] + }, + environment: self.environment + ) + localStore.viewCancellable = self.$value + .map(toLocalValue) + .sink { [weak localStore] newValue in + localStore?.value = newValue + } + return localStore + } +} + +@dynamicMemberLookup +public final class ViewStore: ObservableObject { + @Published public fileprivate(set) var value: Value + fileprivate var cancellable: Cancellable? + public let send: (Action) -> Void + + public subscript(dynamicMember keyPath: KeyPath) -> LocalValue { + self.value[keyPath: keyPath] + } + + public init( + initialValue value: Value, + send: @escaping (Action) -> Void + ) { + self.value = value + self.send = send + } +} + +extension Store where Value: Equatable { + public var view: ViewStore { + self.view(removeDuplicates: ==) + } +} + +extension Store { + public func view( + removeDuplicates predicate: @escaping (Value, Value) -> Bool + ) -> ViewStore { + let viewStore = ViewStore( + initialValue: self.value, + send: self.send + ) + + viewStore.cancellable = self.$value + .removeDuplicates(by: predicate) + .sink(receiveValue: { [weak viewStore] value in + viewStore?.value = value + }) + + return viewStore + } +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/Effect.swift b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/Effect.swift new file mode 100644 index 00000000..daac38ba --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/Effect.swift @@ -0,0 +1,42 @@ +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()) + } +} + +extension Publisher where Output == Never, Failure == Never { + public func fireAndForget() -> Effect { + return self.map(absurd).eraseToEffect() + } +} + +private func absurd(_ never: Never) -> A {} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitecture/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.h b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.h new file mode 100644 index 00000000..476d0b04 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.swift b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTestSupport/ComposableArchitectureTestSupport.swift new file mode 100644 index 00000000..175f2863 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTestSupport/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTestSupport/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift new file mode 100644 index 00000000..c2aa34cb --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift @@ -0,0 +1,5 @@ +import XCTest +@testable import ComposableArchitecture + +class ComposableArchitectureTests: XCTestCase { +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTests/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/ComposableArchitectureTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Counter.h b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Counter.h new file mode 100644 index 00000000..ba74c646 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Counter.swift b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Counter.swift new file mode 100644 index 00000000..5e097550 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Counter.swift @@ -0,0 +1,120 @@ +import CasePaths +import Combine +import ComposableArchitecture +import PrimeAlert +import PrimeModal +import SwiftUI + +public typealias CounterState = ( + alertNthPrime: PrimeAlert?, + count: Int, + isNthPrimeRequestInFlight: Bool, + isPrimeDetailShown: Bool +) + +public enum CounterAction: Equatable { + case decrTapped + case incrTapped + case requestNthPrime + case nthPrimeResponse(n: Int, prime: Int?) + case alertDismissButtonTapped + case isPrimeButtonTapped + case primeDetailDismissed +} + +public typealias CounterEnvironment = (Int) -> Effect + +//public func counterReducer( +// state: inout CounterState, +// action: CounterAction, +// environment: CounterEnvironment +//) -> [Effect] { +public let counterReducer = Reducer { state, action, environment in + switch action { + case .decrTapped: + state.count -= 1 + return [] + + case .incrTapped: + state.count += 1 + return [] + + case .requestNthPrime: + state.isNthPrimeRequestInFlight = 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.isNthPrimeRequestInFlight = false + return [] + + case .alertDismissButtonTapped: + state.alertNthPrime = nil + return [] + + case .isPrimeButtonTapped: + state.isPrimeDetailShown = true + return [] + + case .primeDetailDismissed: + state.isPrimeDetailShown = false + return [] + } +} +.logging() + +public let counterFeatureReducer = Reducer.combine( + counterReducer.pullback( + value: \CounterFeatureState.counter, + action: /CounterFeatureAction.counter, + environment: { $0 } + ), + primeModalReducer.pullback( + value: \.primeModal, + action: /CounterFeatureAction.primeModal, + environment: { _ in () } + ) +) + +public struct CounterFeatureState: Equatable { + public var alertNthPrime: PrimeAlert? + public var count: Int + public var favoritePrimes: [Int] + public var isNthPrimeRequestInFlight: Bool + public var isPrimeDetailShown: Bool + + public init( + alertNthPrime: PrimeAlert? = nil, + count: Int = 0, + favoritePrimes: [Int] = [], + isNthPrimeRequestInFlight: Bool = false, + isPrimeDetailShown: Bool = false + ) { + self.alertNthPrime = alertNthPrime + self.count = count + self.favoritePrimes = favoritePrimes + self.isNthPrimeRequestInFlight = isNthPrimeRequestInFlight + self.isPrimeDetailShown = isPrimeDetailShown + } + + var counter: CounterState { + get { (self.alertNthPrime, self.count, self.isNthPrimeRequestInFlight, self.isPrimeDetailShown) } + set { (self.alertNthPrime, self.count, self.isNthPrimeRequestInFlight, self.isPrimeDetailShown) = newValue } + } + + var primeModal: PrimeModalState { + get { (self.count, self.favoritePrimes) } + set { (self.count, self.favoritePrimes) = newValue } + } +} + +public enum CounterFeatureAction: Equatable { + case counter(CounterAction) + case primeModal(PrimeModalAction) +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/Counter/CounterView_iOS.swift b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/CounterView_iOS.swift new file mode 100644 index 00000000..2c447d7e --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/CounterView_iOS.swift @@ -0,0 +1,121 @@ +#if os(iOS) +import Combine +import ComposableArchitecture +import PrimeAlert +import PrimeModal +import SwiftUI + +public struct CounterView: View { + struct State: Equatable { + let alertNthPrime: PrimeAlert? + let count: Int + let isNthPrimeButtonDisabled: Bool + let isPrimeModalShown: Bool + let isIncrementButtonDisabled: Bool + let isDecrementButtonDisabled: Bool + let nthPrimeButtonTitle: String + } + public enum Action { + case decrTapped + case incrTapped + case nthPrimeButtonTapped + case alertDismissButtonTapped + case isPrimeButtonTapped + case primeModalDismissed + case doubleTap + } + + let store: Store + @ObservedObject var viewStore: ViewStore + + public init(store: Store) { + print("CounterView.init") + self.store = store + self.viewStore = self.store + .scope( + value: State.init, + action: CounterFeatureAction.init + ) + .view + } + + public var body: some View { + print("CounterView.body") + return VStack { + HStack { + Button("-") { self.viewStore.send(.decrTapped) } + .disabled(self.viewStore.isDecrementButtonDisabled) + Text("\(self.viewStore.count)") + Button("+") { self.viewStore.send(.incrTapped) } + .disabled(self.viewStore.isIncrementButtonDisabled) + } + Button("Is this prime?") { self.viewStore.send(.isPrimeButtonTapped) } + Button(self.viewStore.nthPrimeButtonTitle) { + self.viewStore.send(.nthPrimeButtonTapped) + } + .disabled(self.viewStore.isNthPrimeButtonDisabled) + } + .font(.title) + .navigationBarTitle("Counter demo") + .sheet( + isPresented: .constant(self.viewStore.isPrimeModalShown), + onDismiss: { self.viewStore.send(.primeModalDismissed) } + ) { + IsPrimeModalView( + store: self.store.scope( + value: { ($0.count, $0.favoritePrimes) }, + action: { .primeModal($0) } + ) + ) + } + .alert( + item: .constant(self.viewStore.alertNthPrime) + ) { alert in + Alert( + title: Text(alert.title), + dismissButton: .default(Text("Ok")) { + self.viewStore.send(.alertDismissButtonTapped) + } + ) + } + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) + .background(Color.white) + .onTapGesture(count: 2) { + self.viewStore.send(.doubleTap) + } + } +} + +extension CounterView.State { + init(counterFeatureState: CounterFeatureState) { + self.alertNthPrime = counterFeatureState.alertNthPrime + self.count = counterFeatureState.count + self.isNthPrimeButtonDisabled = counterFeatureState.isNthPrimeRequestInFlight + self.isPrimeModalShown = counterFeatureState.isPrimeDetailShown + self.isIncrementButtonDisabled = counterFeatureState.isNthPrimeRequestInFlight + self.isDecrementButtonDisabled = counterFeatureState.isNthPrimeRequestInFlight + self.nthPrimeButtonTitle = "What is the \(ordinal(counterFeatureState.count)) prime?" + } +} + +extension CounterFeatureAction { + init(action: CounterView.Action) { + switch action { + case .decrTapped: + self = .counter(.decrTapped) + case .incrTapped: + self = .counter(.incrTapped) + case .nthPrimeButtonTapped: + self = .counter(.requestNthPrime) + case .alertDismissButtonTapped: + self = .counter(.alertDismissButtonTapped) + case .isPrimeButtonTapped: + self = .counter(.isPrimeButtonTapped) + case .primeModalDismissed: + self = .counter(.primeDetailDismissed) + case .doubleTap: + self = .counter(.requestNthPrime) + } + } +} +#endif diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/Counter/CounterView_macOS.swift b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/CounterView_macOS.swift new file mode 100644 index 00000000..8bc3eeb5 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/CounterView_macOS.swift @@ -0,0 +1,113 @@ +#if os(macOS) +import Combine +import ComposableArchitecture +import PrimeAlert +import PrimeModal +import SwiftUI + +public struct CounterView: View { + struct State: Equatable { + let alertNthPrime: PrimeAlert? + let count: Int + let isNthPrimeButtonDisabled: Bool + let isPrimePopoverShown: Bool + let isIncrementButtonDisabled: Bool + let isDecrementButtonDisabled: Bool + let nthPrimeButtonTitle: String + } + public enum Action { + case decrTapped + case incrTapped + case nthPrimeButtonTapped + case alertDismissButtonTapped + case isPrimeButtonTapped + case primePopoverDismissed + } + + let store: Store + @ObservedObject var viewStore: ViewStore + + public init(store: Store) { + print("CounterView.init") + self.store = store + self.viewStore = self.store + .scope( + value: State.init, + action: CounterFeatureAction.init + ) + .view + } + + public var body: some View { + print("CounterView.body") + return VStack { + HStack { + Button("-") { self.viewStore.send(.decrTapped) } + .disabled(self.viewStore.isDecrementButtonDisabled) + Text("\(self.viewStore.count)") + Button("+") { self.viewStore.send(.incrTapped) } + .disabled(self.viewStore.isIncrementButtonDisabled) + } + Button("Is this prime?") { self.viewStore.send(.isPrimeButtonTapped) } + Button(self.viewStore.nthPrimeButtonTitle) { + self.viewStore.send(.nthPrimeButtonTapped) + } + .disabled(self.viewStore.isNthPrimeButtonDisabled) + } + .popover( + isPresented: Binding( + get: { self.viewStore.isPrimePopoverShown }, + set: { _ in self.viewStore.send(.primePopoverDismissed) } + ) + ) { + IsPrimeModalView( + store: self.store.scope( + value: { ($0.count, $0.favoritePrimes) }, + action: { .primeModal($0) } + ) + ) + } + .alert( + item: .constant(self.viewStore.alertNthPrime) + ) { alert in + Alert( + title: Text(alert.title), + dismissButton: .default(Text("Ok")) { + self.viewStore.send(.alertDismissButtonTapped) + } + ) + } + } +} + +extension CounterView.State { + init(counterFeatureState: CounterFeatureState) { + self.alertNthPrime = counterFeatureState.alertNthPrime + self.count = counterFeatureState.count + self.isNthPrimeButtonDisabled = counterFeatureState.isNthPrimeRequestInFlight + self.isPrimePopoverShown = counterFeatureState.isPrimeDetailShown + self.isIncrementButtonDisabled = counterFeatureState.isNthPrimeRequestInFlight + self.isDecrementButtonDisabled = counterFeatureState.isNthPrimeRequestInFlight + self.nthPrimeButtonTitle = "What is the \(ordinal(counterFeatureState.count)) prime?" + } +} + +extension CounterFeatureAction { + init(action: CounterView.Action) { + switch action { + case .decrTapped: + self = .counter(.decrTapped) + case .incrTapped: + self = .counter(.incrTapped) + case .nthPrimeButtonTapped: + self = .counter(.requestNthPrime) + case .alertDismissButtonTapped: + self = .counter(.alertDismissButtonTapped) + case .isPrimeButtonTapped: + self = .counter(.isPrimeButtonTapped) + case .primePopoverDismissed: + self = .counter(.primeDetailDismissed) + } + } +} +#endif diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/Counter/WolframAlpha.swift b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/WolframAlpha.swift new file mode 100644 index 00000000..ecc1dc04 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Counter/WolframAlpha.swift @@ -0,0 +1,94 @@ +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() +} + +public func offlineNthPrime(_ n: Int) -> Effect { + Future { callback in + var nthPrime = 1 + var count = 0 + while count < n { + nthPrime += 1 + if isPrime(nthPrime) { + count += 1 + } + } + callback(.success(nthPrime)) + } + .eraseToEffect() +} + +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 +} + + +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/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/ComposableArchitectureSnapshotTesting.swift b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/ComposableArchitectureSnapshotTesting.swift new file mode 100644 index 00000000..2fbd1b5c --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/CounterTests.swift b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/CounterTests.swift new file mode 100644 index 00000000..254bc375 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/CounterTests.swift @@ -0,0 +1,131 @@ +import ComposableArchitecture +import ComposableArchitectureTestSupport +@testable import Counter +import PrimeAlert +import SnapshotTesting +import SwiftUI +import XCTest + +class CounterTests: XCTestCase { + func testSnapshots() { + let store = Store( + initialValue: CounterFeatureState(), + reducer: counterFeatureReducer, + 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) + + view.viewStore.send(.incrTapped) + assertSnapshot(matching: vc, as: .windowedImage) + + view.viewStore.send(.incrTapped) + assertSnapshot(matching: vc, as: .windowedImage) + + view.viewStore.send(.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) + + view.viewStore.send(.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) + + view.viewStore.send(.isPrimeButtonTapped) + assertSnapshot(matching: vc, as: .windowedImage) + + store.view.send(.primeModal(.saveFavoritePrimeTapped)) + assertSnapshot(matching: vc, as: .windowedImage) + + view.viewStore.send(.primeModalDismissed) + assertSnapshot(matching: vc, as: .windowedImage) + } + + func testIncrDecrButtonTapped() { + assert( + initialValue: CounterFeatureState(count: 2), + reducer: counterFeatureReducer, + 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() { + assert( + initialValue: CounterFeatureState( + alertNthPrime: nil, + count: 7, + isNthPrimeRequestInFlight: false + ), + reducer: counterFeatureReducer, + environment: { _ in .sync { 17 } }, + steps: + Step(.send, .counter(CounterAction.requestNthPrime)) { + $0.isNthPrimeRequestInFlight = true + }, + Step(.receive, .counter(.nthPrimeResponse(n: 7, prime: 17))) { + $0.alertNthPrime = PrimeAlert(n: $0.count, prime: 17) + $0.isNthPrimeRequestInFlight = false + }, + Step(.send, .counter(.alertDismissButtonTapped)) { + $0.alertNthPrime = nil + } + ) + } + + func testNthPrimeButtonUnhappyFlow() { + assert( + initialValue: CounterFeatureState( + alertNthPrime: nil, + count: 7, + isNthPrimeRequestInFlight: false + ), + reducer: counterFeatureReducer, + environment: { _ in .sync { nil } }, + steps: + Step(.send, .counter(.requestNthPrime)) { + $0.isNthPrimeRequestInFlight = true + }, + Step(.receive, .counter(.nthPrimeResponse(n: 7, prime: nil))) { + $0.isNthPrimeRequestInFlight = false + } + ) + } + + func testPrimeModal() { + assert( + initialValue: CounterFeatureState( + count: 1, + favoritePrimes: [3, 5] + ), + reducer: counterFeatureReducer, + 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/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.1.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.1.png new file mode 100644 index 00000000..bd0e1ba0 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.1.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.2.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.2.png new file mode 100644 index 00000000..8bda1233 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.2.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.3.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.3.png new file mode 100644 index 00000000..d54ac962 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.3.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.4.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.4.png new file mode 100644 index 00000000..ed1a854e Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.4.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.5.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.5.png new file mode 100644 index 00000000..f5a3fe45 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.5.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.6.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.6.png new file mode 100644 index 00000000..d54ac962 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.6.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.7.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.7.png new file mode 100644 index 00000000..321ee3ef Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.7.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.8.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.8.png new file mode 100644 index 00000000..df4853c5 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.8.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.9.png b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.9.png new file mode 100644 index 00000000..d54ac962 Binary files /dev/null and b/0099-ergonomic-state-management-pt2/PrimeTime/CounterTests/__Snapshots__/CounterTests/testSnapshots.9.png differ diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.h b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.h new file mode 100644 index 00000000..b0a43669 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.swift b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.swift new file mode 100644 index 00000000..0291d007 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FavoritePrimes.swift @@ -0,0 +1,117 @@ +import Combine +import ComposableArchitecture +import PrimeAlert +import SwiftUI + +public typealias FavoritePrimesState = ( + alertNthPrime: PrimeAlert?, + favoritePrimes: [Int] +) + +public enum FavoritePrimesAction: Equatable { + case deleteFavoritePrimes(IndexSet) + case loadButtonTapped + case loadedFavoritePrimes([Int]) + case primeButtonTapped(Int) + case saveButtonTapped + case nthPrimeResponse(n: Int, prime: Int?) + case alertDismissButtonTapped +} + +public typealias FavoritePrimesEnvironment = ( + fileClient: FileClient, + nthPrime: (Int) -> Effect +) + +//public func favoritePrimesReducer( +// state: inout FavoritePrimesState, +// action: FavoritePrimesAction, +// environment: FavoritePrimesEnvironment +//) -> [Effect] { +public let favoritePrimesReducer = Reducer { state, action, environment in + switch action { + case let .deleteFavoritePrimes(indexSet): + for index in indexSet { + state.favoritePrimes.remove(at: index) + } + return [] + + case let .loadedFavoritePrimes(favoritePrimes): + state.favoritePrimes = favoritePrimes + return [] + + case .saveButtonTapped: + return [ + environment.fileClient + .save("favorite-primes.json", try! JSONEncoder().encode(state.favoritePrimes)) + .fireAndForget() + ] + + case .loadButtonTapped: + return [ + environment.fileClient.load("favorite-primes.json") + .compactMap { $0 } + .decode(type: [Int].self, decoder: JSONDecoder()) + .catch { error in Empty(completeImmediately: true) } + .map(FavoritePrimesAction.loadedFavoritePrimes) + .eraseToEffect() + ] + + case let .primeButtonTapped(n): + return [ + environment.nthPrime(n) + .map { FavoritePrimesAction.nthPrimeResponse(n: n, prime: $0) } + .receive(on: DispatchQueue.main) + .eraseToEffect() + ] + + case .nthPrimeResponse(let n, let prime): + state.alertNthPrime = prime.map { PrimeAlert(n: n, prime: $0) } + return [] + + case .alertDismissButtonTapped: + state.alertNthPrime = nil + return [] + } +} + +public struct FavoritePrimesView: View { + let store: Store + @ObservedObject var viewStore: ViewStore + + public init(store: Store) { + print("FavoritePrimesView.init") + self.store = store + self.viewStore = self.store.view(removeDuplicates: ==) + } + + public var body: some View { + print("FavoritePrimesView.body") + return List { + ForEach(self.viewStore.favoritePrimes, id: \.self) { prime in + Button("\(prime)") { + self.viewStore.send(.primeButtonTapped(prime)) + } + } + .onDelete { indexSet in + self.viewStore.send(.deleteFavoritePrimes(indexSet)) + } + } + .navigationBarTitle("Favorite primes") + .navigationBarItems( + trailing: HStack { + Button("Save") { + self.viewStore.send(.saveButtonTapped) + } + Button("Load") { + self.viewStore.send(.loadButtonTapped) + } + } + ) + .alert(item: .constant(self.viewStore.alertNthPrime)) { primeAlert in + Alert(title: Text(primeAlert.title), dismissButton: Alert.Button.default(Text("Ok"), action: { + self.viewStore.send(.alertDismissButtonTapped) + })) + } + } +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FileClient.swift b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FileClient.swift new file mode 100644 index 00000000..390db61f --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/FileClient.swift @@ -0,0 +1,39 @@ +import ComposableArchitecture +import Foundation + +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) + } + } + ) +} + +#if DEBUG +extension FileClient { + static let mock = FileClient( + load: { _ in Effect.sync { + try! JSONEncoder().encode([2, 31]) + } }, + save: { _, _ in .fireAndForget {} } + ) +} +#endif diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimes/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift new file mode 100644 index 00000000..4d2a829c --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift @@ -0,0 +1,67 @@ +import XCTest +@testable import FavoritePrimes + +class FavoritePrimesTests: XCTestCase { + func testDeleteFavoritePrimes() { + let environment = FavoritePrimesEnvironment(fileClient: .mock, nthPrime: { _ in .sync { 17 } }) + var state = FavoritePrimesState(alertNthPrime: nil, favoritePrimes: [2, 3, 5, 7]) + let effects = favoritePrimesReducer(state: &state, action: .deleteFavoritePrimes([2]), environment: environment) + + XCTAssertNil(state.alertNthPrime) + XCTAssertEqual(state.favoritePrimes, [2, 3, 7]) + XCTAssert(effects.isEmpty) + } + + func testSaveButtonTapped() { + var didSave = false + var fileClient = FileClient.mock + fileClient.save = { _, data in + .fireAndForget { + didSave = true + } + } + + let environment = FavoritePrimesEnvironment(fileClient: fileClient, nthPrime: { _ in .sync { 17 } }) + var state = FavoritePrimesState(alertNthPrime: nil, favoritePrimes: [2, 3, 5, 7]) + let effects = favoritePrimesReducer(state: &state, action: .saveButtonTapped, environment: environment) + + XCTAssertNil(state.alertNthPrime) + XCTAssertEqual(state.favoritePrimes, [2, 3, 5, 7]) + XCTAssertEqual(effects.count, 1) + + _ = effects[0].sink { _ in XCTFail() } + + XCTAssert(didSave) + } + + func testLoadFavoritePrimesFlow() { + var fileClient = FileClient.mock + fileClient.load = { _ in .sync { try! JSONEncoder().encode([2, 31]) } } + + let environment = FavoritePrimesEnvironment(fileClient: fileClient, nthPrime: { _ in .sync { 17 } }) + var state = FavoritePrimesState(alertNthPrime: nil, favoritePrimes: [2, 3, 5, 7]) + var effects = favoritePrimesReducer(state: &state, action: .loadButtonTapped, environment: environment) + + XCTAssertNil(state.alertNthPrime) + XCTAssertEqual(state.favoritePrimes, [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) + + XCTAssertNil(state.alertNthPrime) + XCTAssertEqual(state.favoritePrimes, [2, 31]) + XCTAssert(effects.isEmpty) + } +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimesTests/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/FavoritePrimesTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/Package.swift b/0099-ergonomic-state-management-pt2/PrimeTime/Package.swift new file mode 100644 index 00000000..89f8b7a3 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/PrimeAlert.h b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/PrimeAlert.h new file mode 100644 index 00000000..7aa04b24 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/PrimeAlert.h @@ -0,0 +1,19 @@ +// +// PrimeAlert.h +// PrimeAlert +// +// Created by Brandon Williams on 2/12/20. +// Copyright © 2020 Point-Free. All rights reserved. +// + +#import + +//! Project version number for PrimeAlert. +FOUNDATION_EXPORT double PrimeAlertVersionNumber; + +//! Project version string for PrimeAlert. +FOUNDATION_EXPORT const unsigned char PrimeAlertVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/PrimeAlert.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/PrimeAlert.swift new file mode 100644 index 00000000..1cf0142c --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeAlert/PrimeAlert.swift @@ -0,0 +1,20 @@ +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 var title: String { + return "The \(ordinal(self.n)) prime is \(self.prime)" + } +} + +public func ordinal(_ n: Int) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .ordinal + return formatter.string(for: n) ?? "" +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/PrimeModal.h b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/PrimeModal.h new file mode 100644 index 00000000..5bdc9d8f --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/PrimeModal.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/PrimeModal.swift new file mode 100644 index 00000000..23acbb88 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModal/PrimeModal.swift @@ -0,0 +1,80 @@ +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] { +public let primeModalReducer = Reducer { state, action, _ in + switch action { + case .removeFavoritePrimeTapped: + state.favoritePrimes.removeAll(where: { $0 == state.count }) + return [] + + case .saveFavoritePrimeTapped: + state.favoritePrimes.append(state.count) + return [] + } +} + +public struct IsPrimeModalView: View { + struct State: Equatable { + let count: Int + let isFavorite: Bool + } + + let store: Store + @ObservedObject var viewStore: ViewStore + + public init(store: Store) { + print("IsPrimeModalView.init") + self.store = store + self.viewStore = self.store + .scope(value: State.init(primeModalState:), action: { $0 }) + .view + } + + public var body: some View { + print("IsPrimeModalView.body") + return VStack { + if isPrime(self.viewStore.count) { + Text("\(self.viewStore.count) is prime 🎉") + if self.viewStore.isFavorite { + Button("Remove from favorite primes") { + self.viewStore.send(.removeFavoritePrimeTapped) + } + } else { + Button("Save to favorite primes") { + self.viewStore.send(.saveFavoritePrimeTapped) + } + } + } else { + Text("\(self.viewStore.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 +} + +extension IsPrimeModalView.State { + init(primeModalState state: PrimeModalState) { + self.count = state.count + self.isFavorite = state.favoritePrimes.contains(state.count) + } +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModalTests/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModalTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModalTests/PrimeModalTests.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeModalTests/PrimeModalTests.swift new file mode 100644 index 00000000..b5b5f27c --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Combine.xcplaygroundpage/Contents.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Combine.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..6bcd8a29 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..ee5ef199 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift @@ -0,0 +1,19 @@ +import ComposableArchitecture +@testable import Counter +import PlaygroundSupport +import SwiftUI + +PlaygroundPage.current.setLiveView( + CounterView( + store: Store( + initialValue: CounterFeatureState( + alertNthPrime: nil, + count: 0, + favoritePrimes: [], + isNthPrimeRequestInFlight: false + ), + reducer: logging(counterFeatureReducer), + environment: Counter.nthPrime + ) + ) +) diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..8d1d4d95 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift @@ -0,0 +1,27 @@ +import ComposableArchitecture +@testable import FavoritePrimes +import PlaygroundSupport +import SwiftUI + +var environment = FavoritePrimesEnvironment( + fileClient: .mock, + nthPrime: { _ in .sync { 17 } } +) +environment.fileClient.load = { _ in + Effect.sync { try! JSONEncoder().encode(Array(1...10)) } +} + +PlaygroundPage.current.liveView = UIHostingController( + rootView: NavigationView { + FavoritePrimesView( + store: Store( + initialValue: ( + alertNthPrime: nil, + favoritePrimes: [2, 3, 5, 7, 11] + ), + reducer: favoritePrimesReducer, + environment: environment + ) + ) + } +) diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..11a29b9e --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift @@ -0,0 +1,14 @@ +import ComposableArchitecture +import PlaygroundSupport +import PrimeModal +import SwiftUI + +PlaygroundPage.current.liveView = UIHostingController( + rootView: IsPrimeModalView( + store: Store( + initialValue: (2, [2, 3, 5]), + reducer: primeModalReducer, + environment: () + ) + ) +) diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Scratch.xcplaygroundpage/Contents.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Scratch.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..8dce7ba8 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/Pages/Scratch.xcplaygroundpage/Contents.swift @@ -0,0 +1,27 @@ +// +//struct User { +//// var admin: Bool +// var id: Int +// var name: String +// var bio: String +//} +// +//struct Admin { +// var user: User +//} +// +//let adminUser = User(admin: true, id: 1, name: "Blob", bio: "Blobbed around the world") +//let nonAdminUser = User(admin: false, id: 2, name: "Blob Jr.", bio: "Blobbed around the world") +// +//func doStuff(admin: Admin) { +// guard user.admin else { return } +// print("Do admin stuff for \(user.name)") +//} +// +//doStuff(user: adminUser) +//doStuff(user: nonAdminUser) + + + +12 + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/contents.xcplayground b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/contents.xcplayground new file mode 100644 index 00000000..de1478c1 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.playground/contents.xcplayground @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.pbxproj b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.pbxproj new file mode 100644 index 00000000..5f9a4259 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.pbxproj @@ -0,0 +1,2265 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 2A5AFE432423FCC600640A58 /* CounterView_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5AFE422423FCC600640A58 /* CounterView_iOS.swift */; }; + 2A5AFE452423FD2E00640A58 /* CounterView_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5AFE442423FD2E00640A58 /* CounterView_macOS.swift */; }; + CA304E7A23F48C1400E79D83 /* PrimeAlert.h in Headers */ = {isa = PBXBuildFile; fileRef = CA304E7823F48C1400E79D83 /* PrimeAlert.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CA304E7D23F48C1400E79D83 /* PrimeAlert.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA304E7623F48C1400E79D83 /* PrimeAlert.framework */; }; + CA304E7E23F48C1400E79D83 /* PrimeAlert.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CA304E7623F48C1400E79D83 /* PrimeAlert.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CA304E8323F48C2400E79D83 /* PrimeAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA304E8223F48C2400E79D83 /* PrimeAlert.swift */; }; + CA304E8623F48C3900E79D83 /* PrimeAlert.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA304E7623F48C1400E79D83 /* PrimeAlert.framework */; }; + CA304E8923F48C4400E79D83 /* PrimeAlert.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA304E7623F48C1400E79D83 /* PrimeAlert.framework */; }; + 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 */; }; + DC69E6402404865D00FCF3C2 /* Base.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = DC69E63D2404865D00FCF3C2 /* Base.xcconfig */; }; + DC69E6412404865D00FCF3C2 /* Framework.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; }; + DC69E6422404865D00FCF3C2 /* Test.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; }; + 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 */; }; + DCC168BD24044DED006A68B3 /* FileClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC168BC24044DED006A68B3 /* FileClient.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 */ + CA304E7B23F48C1400E79D83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CA304E7523F48C1400E79D83; + remoteInfo = PrimeAlert; + }; + CA304E8423F48C3400E79D83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CA304E7523F48C1400E79D83; + remoteInfo = PrimeAlert; + }; + CA304E8723F48C4000E79D83 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CA304E7523F48C1400E79D83; + remoteInfo = PrimeAlert; + }; + 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; + }; + 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; + }; + 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; + }; + DCF0C60C2326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C6012326036F008B45A0; + remoteInfo = FavoritePrimes; + }; + 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 */, + CA304E7E23F48C1400E79D83 /* PrimeAlert.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 */ + 2A5AFE422423FCC600640A58 /* CounterView_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterView_iOS.swift; sourceTree = ""; }; + 2A5AFE442423FD2E00640A58 /* CounterView_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterView_macOS.swift; sourceTree = ""; }; + CA304E7623F48C1400E79D83 /* PrimeAlert.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrimeAlert.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CA304E7823F48C1400E79D83 /* PrimeAlert.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrimeAlert.h; sourceTree = ""; }; + CA304E7923F48C1400E79D83 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CA304E8223F48C2400E79D83 /* PrimeAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeAlert.swift; sourceTree = ""; }; + 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; }; + DC69E63D2404865D00FCF3C2 /* Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = ""; }; + DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; + DC69E63F2404865D00FCF3C2 /* Test.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Test.xcconfig; sourceTree = ""; }; + 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 = ""; }; + DCC168BC24044DED006A68B3 /* FileClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileClient.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 */ + CA304E7323F48C1400E79D83 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CA79FC2A239C23310096D881 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90716922FA102900B38B42 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF88F02234D4A3B001BA79A /* ComposableArchitecture.framework in Frameworks */, + CA304E7D23F48C1400E79D83 /* PrimeAlert.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 = ( + CA304E8923F48C4400E79D83 /* PrimeAlert.framework in Frameworks */, + 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 = ( + CA304E8623F48C3900E79D83 /* PrimeAlert.framework in Frameworks */, + 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 */ + CA304E7723F48C1400E79D83 /* PrimeAlert */ = { + isa = PBXGroup; + children = ( + CA304E7823F48C1400E79D83 /* PrimeAlert.h */, + CA304E7923F48C1400E79D83 /* Info.plist */, + CA304E8223F48C2400E79D83 /* PrimeAlert.swift */, + ); + path = PrimeAlert; + sourceTree = ""; + }; + 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 */, + CA304E7723F48C1400E79D83 /* PrimeAlert */, + DCC168BE240484EC006A68B3 /* Shared */, + 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 */, + CA304E7623F48C1400E79D83 /* PrimeAlert.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 = ""; + }; + DCC168BE240484EC006A68B3 /* Shared */ = { + isa = PBXGroup; + children = ( + DC69E63D2404865D00FCF3C2 /* Base.xcconfig */, + DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */, + DC69E63F2404865D00FCF3C2 /* Test.xcconfig */, + ); + path = Shared; + 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 */, + 2A5AFE422423FCC600640A58 /* CounterView_iOS.swift */, + 2A5AFE442423FD2E00640A58 /* CounterView_macOS.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 */, + DCC168BC24044DED006A68B3 /* FileClient.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 */ + CA304E7123F48C1400E79D83 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CA304E7A23F48C1400E79D83 /* PrimeAlert.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 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 */ + CA304E7523F48C1400E79D83 /* PrimeAlert */ = { + isa = PBXNativeTarget; + buildConfigurationList = CA304E8123F48C1400E79D83 /* Build configuration list for PBXNativeTarget "PrimeAlert" */; + buildPhases = ( + CA304E7123F48C1400E79D83 /* Headers */, + CA304E7223F48C1400E79D83 /* Sources */, + CA304E7323F48C1400E79D83 /* Frameworks */, + CA304E7423F48C1400E79D83 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PrimeAlert; + productName = PrimeAlert; + productReference = CA304E7623F48C1400E79D83 /* PrimeAlert.framework */; + productType = "com.apple.product-type.framework"; + }; + 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 */, + CA304E7C23F48C1400E79D83 /* 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 */, + ); + 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 = ( + CA304E8823F48C4000E79D83 /* PBXTargetDependency */, + 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 */, + ); + 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 */, + ); + 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 = ( + CA304E8523F48C3400E79D83 /* PBXTargetDependency */, + 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 */, + ); + 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 = { + CA304E7523F48C1400E79D83 = { + CreatedOnToolsVersion = 11.3.1; + LastSwiftMigration = 1130; + }; + 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 */, + CA304E7523F48C1400E79D83 /* PrimeAlert */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + CA304E7423F48C1400E79D83 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CA79FC2B239C23310096D881 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90716A22FA102900B38B42 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC69E6412404865D00FCF3C2 /* Framework.xcconfig in Resources */, + DC90717C22FA102A00B38B42 /* LaunchScreen.storyboard in Resources */, + DC69E6422404865D00FCF3C2 /* Test.xcconfig in Resources */, + DC90717922FA102A00B38B42 /* Preview Assets.xcassets in Resources */, + DC90717622FA102A00B38B42 /* Assets.xcassets in Resources */, + DC69E6402404865D00FCF3C2 /* Base.xcconfig 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 */ + CA304E7223F48C1400E79D83 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CA304E8323F48C2400E79D83 /* PrimeAlert.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 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 */, + 2A5AFE432423FCC600640A58 /* CounterView_iOS.swift in Sources */, + 2A5AFE452423FD2E00640A58 /* CounterView_macOS.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 = ( + DCC168BD24044DED006A68B3 /* FileClient.swift in Sources */, + 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 */ + CA304E7C23F48C1400E79D83 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CA304E7523F48C1400E79D83 /* PrimeAlert */; + targetProxy = CA304E7B23F48C1400E79D83 /* PBXContainerItemProxy */; + }; + CA304E8523F48C3400E79D83 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CA304E7523F48C1400E79D83 /* PrimeAlert */; + targetProxy = CA304E8423F48C3400E79D83 /* PBXContainerItemProxy */; + }; + CA304E8823F48C4000E79D83 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CA304E7523F48C1400E79D83 /* PrimeAlert */; + targetProxy = CA304E8723F48C4000E79D83 /* PBXContainerItemProxy */; + }; + 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 */; + }; + DCF0C5AF2326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C5AE2326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5C923260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5BD23260348008B45A0 /* Counter */; + targetProxy = DCF0C5C823260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5D223260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5BD23260348008B45A0 /* Counter */; + targetProxy = DCF0C5D123260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5EB2326035C008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5DF2326035C008B45A0 /* PrimeModal */; + targetProxy = DCF0C5EA2326035C008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C60D2326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C6012326036F008B45A0 /* FavoritePrimes */; + targetProxy = DCF0C60C2326036F008B45A0 /* 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 */ + CA304E7F23F48C1400E79D83 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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 = PrimeAlert/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.PrimeAlert; + 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; + }; + CA304E8023F48C1400E79D83 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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 = PrimeAlert/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.PrimeAlert; + 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; + }; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63E2404865D00FCF3C2 /* Framework.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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; + baseConfigurationReference = DC69E63F2404865D00FCF3C2 /* Test.xcconfig */; + 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 */ + CA304E8123F48C1400E79D83 /* Build configuration list for PBXNativeTarget "PrimeAlert" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CA304E7F23F48C1400E79D83 /* Debug */, + CA304E8023F48C1400E79D83 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/ComposableArchitecture.xcscheme b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/ComposableArchitecture.xcscheme new file mode 100644 index 00000000..58aa1d24 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/ComposableArchitecture.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/Counter.xcscheme b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/Counter.xcscheme new file mode 100644 index 00000000..e0fc8fb9 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/Counter.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/FavoritePrimes.xcscheme b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/FavoritePrimes.xcscheme new file mode 100644 index 00000000..02e7ebd3 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/FavoritePrimes.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeAlert.xcscheme b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeAlert.xcscheme new file mode 100644 index 00000000..22e2deef --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeAlert.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeModal.xcscheme b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeModal.xcscheme new file mode 100644 index 00000000..73d691d1 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeModal.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeTime.xcscheme b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeTime.xcscheme new file mode 100644 index 00000000..d64f316c --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime.xcodeproj/xcshareddata/xcschemes/PrimeTime.xcscheme @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/AppDelegate.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/AppDelegate.swift new file mode 100644 index 00000000..3ba17b06 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Assets.xcassets/Contents.json b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/ContentView.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/ContentView.swift new file mode 100644 index 00000000..89bb3abc --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/ContentView.swift @@ -0,0 +1,214 @@ +import CasePaths +import Combine +import ComposableArchitecture +import Counter +import FavoritePrimes +import PrimeAlert +import SwiftUI + +struct AppState: Equatable { + var count = 0 + var favoritePrimes: [Int] = [] + var loggedInUser: User? = nil + var activityFeed: [Activity] = [] + var alertNthPrime: PrimeAlert? = nil + var isNthPrimeRequestInFlight: Bool = false + var isPrimeDetailShown: 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(CounterFeatureAction) + case offlineCounterView(CounterFeatureAction) + case favoritePrimes(FavoritePrimesAction) +} + +extension AppState { + var favoritePrimesState: FavoritePrimesState { + get { + (self.alertNthPrime, self.favoritePrimes) + } + set { + (self.alertNthPrime, self.favoritePrimes) = newValue + } + } + + var counterView: CounterFeatureState { + get { + CounterFeatureState( + alertNthPrime: self.alertNthPrime, + count: self.count, + favoritePrimes: self.favoritePrimes, + isNthPrimeRequestInFlight: self.isNthPrimeRequestInFlight, + isPrimeDetailShown: self.isPrimeDetailShown + ) + } + set { + self.alertNthPrime = newValue.alertNthPrime + self.count = newValue.count + self.favoritePrimes = newValue.favoritePrimes + self.isNthPrimeRequestInFlight = newValue.isNthPrimeRequestInFlight + self.isPrimeDetailShown = newValue.isPrimeDetailShown + } + } +} + +typealias AppEnvironment = ( + fileClient: FileClient, + nthPrime: (Int) -> Effect, + offlineNthPrime: (Int) -> Effect +) + +let appReducer: Reducer = .combine( + counterFeatureReducer.pullback( + value: \AppState.counterView, + action: /AppAction.counterView, + environment: { $0.nthPrime } + ), + counterFeatureReducer.pullback( + value: \AppState.counterView, + action: /AppAction.offlineCounterView, + environment: { $0.offlineNthPrime } + ), + favoritePrimesReducer.pullback( + value: \.favoritePrimesState, + action: /AppAction.favoritePrimes, + environment: { ($0.fileClient, $0.nthPrime) } + ) +) + +extension Reducer where Value == AppState, Action == AppAction, Environment == AppEnvironment { + func activityFeed() -> Reducer { + return .init { state, action, environment in + switch action { + case .counterView(.counter), + .offlineCounterView(.counter), + .favoritePrimes(.loadedFavoritePrimes), + .favoritePrimes(.loadButtonTapped), + .favoritePrimes(.saveButtonTapped), + .favoritePrimes(.primeButtonTapped(_)), + .favoritePrimes(.nthPrimeResponse), + .favoritePrimes(.alertDismissButtonTapped): + break + case .counterView(.primeModal(.removeFavoritePrimeTapped)), + .offlineCounterView(.primeModal(.removeFavoritePrimeTapped)): + state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.count))) + + case .counterView(.primeModal(.saveFavoritePrimeTapped)), + .offlineCounterView(.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 self(&state, action, environment) + } + } +} + +//func activityFeed( +// _ reducer: Reducer +//) -> Reducer { +// +// return .init { state, action, environment in +// switch action { +// case .counterView(.counter), +// .offlineCounterView(.counter), +// .favoritePrimes(.loadedFavoritePrimes), +// .favoritePrimes(.loadButtonTapped), +// .favoritePrimes(.saveButtonTapped), +// .favoritePrimes(.primeButtonTapped(_)), +// .favoritePrimes(.nthPrimeResponse), +// .favoritePrimes(.alertDismissButtonTapped): +// break +// case .counterView(.primeModal(.removeFavoritePrimeTapped)), +// .offlineCounterView(.primeModal(.removeFavoritePrimeTapped)): +// state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.count))) +// +// case .counterView(.primeModal(.saveFavoritePrimeTapped)), +// .offlineCounterView(.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) +// } +//} + +let isInExperiment = false //Bool.random() + +struct ContentView: View { + let store: Store +// @ObservedObject var viewStore: ViewStore + + init(store: Store) { + print("ContentView.init") + self.store = store + } + + var body: some View { + print("ContentView.body") + return NavigationView { + List { + if !isInExperiment { + NavigationLink( + "Counter demo", + destination: CounterView( + store: self.store.scope( + value: { $0.counterView }, + action: { .counterView($0) } + ) + ) + ) + } else { + NavigationLink( + "Offline counter demo", + destination: CounterView( + store: self.store.scope( + value: { $0.counterView }, + action: { .offlineCounterView($0) } + ) + ) + ) + } + NavigationLink( + "Favorite primes", + destination: FavoritePrimesView( + store: self.store.scope( + value: { $0.favoritePrimesState }, + action: { .favoritePrimes($0) } + ) + ) + ) + + ForEach(Array(1...500_000), id: \.self) { value in + Text("\(value)") + } + + } + .navigationBarTitle("State management") + } + } +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Info.plist new file mode 100644 index 00000000..9742bf0f --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/SceneDelegate.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/SceneDelegate.swift new file mode 100644 index 00000000..7dc5db3e --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/SceneDelegate.swift @@ -0,0 +1,39 @@ +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: appReducer + .activityFeed(), +// .logging(), +// reducer: with( +// appReducer, +// compose( +// logging, +// activityFeed +// ) +// ), +// reducer: logging(activityFeed(barEnhancer(fooEnhancer(appReducer)))), + environment: AppEnvironment( + fileClient: .live, + nthPrime: Counter.nthPrime, + offlineNthPrime: Counter.offlineNthPrime + ) + ) + ) + ) + self.window = window + window.makeKeyAndVisible() + } + } +} diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Util.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTime/Util.swift new file mode 100644 index 00000000..b9509ff8 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeTests/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift new file mode 100644 index 00000000..805f6198 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift @@ -0,0 +1,37 @@ +import ComposableArchitecture +import ComposableArchitectureTestSupport +@testable import Counter +@testable import FavoritePrimes +import PrimeAlert +@testable import PrimeModal +@testable import PrimeTime +import XCTest + +class PrimeTimeTests: XCTestCase { + func testIntegration() { + var fileClient = FileClient.mock + fileClient.load = { _ in .sync { try! JSONEncoder().encode([2, 31, 7]) } } + + assert( + initialValue: AppState(count: 4), + reducer: appReducer, + environment: ( + fileClient: fileClient, + nthPrime: { _ in .sync { 17 } }, + offlineNthPrime: { _ in .sync { 17 } } + ), + steps: + Step(.send, .counterView(.counter(.requestNthPrime))) { + $0.isNthPrimeRequestInFlight = true + }, + Step(.receive, .counterView(.counter(.nthPrimeResponse(n: 4, prime: 17)))) { + $0.isNthPrimeRequestInFlight = 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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeUITests/Info.plist b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeUITests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeUITests/PrimeTimeUITests.swift b/0099-ergonomic-state-management-pt2/PrimeTime/PrimeTimeUITests/PrimeTimeUITests.swift new file mode 100644 index 00000000..bac2c351 --- /dev/null +++ b/0099-ergonomic-state-management-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/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Base.xcconfig b/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Base.xcconfig new file mode 100644 index 00000000..f151cbae --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Base.xcconfig @@ -0,0 +1,17 @@ +SUPPORTED_PLATFORMS = appletvos appletvsimulator iphonesimulator iphoneos macosx watchos watchsimulator +VALID_ARCHS[sdk=iphoneos*] = arm64 armv7 armv7s +VALID_ARCHS[sdk=iphonesimulator*] = i386 x86_64 +VALID_ARCHS[sdk=macosx*] = i386 x86_64 +VALID_ARCHS[sdk=watchos*] = armv7k arm64_32 +VALID_ARCHS[sdk=watchsimulator*] = i386 +VALID_ARCHS[sdk=appletvos*] = arm64 +VALID_ARCHS[sdk=appletvsimulator*] = x86_64 + +LD_RUNPATH_SEARCH_PATHS[sdk=appletvos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=appletvsimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=iphoneos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=iphonesimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=watchos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=watchsimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' + \ No newline at end of file diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Framework.xcconfig b/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Framework.xcconfig new file mode 100644 index 00000000..488ae1ae --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Framework.xcconfig @@ -0,0 +1,16 @@ +#include "Base.xcconfig" + +CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer +COMBINE_HIDPI_IMAGES[sdk=macosx*] = YES +ENABLE_BITCODE[sdk=macosx*] = NO +ENABLE_BITCODE[sdk=watchsimulator*] = YES +ENABLE_BITCODE[sdk=watch*] = YES +ENABLE_BITCODE[sdk=iphonesimulator*] = YES +ENABLE_BITCODE[sdk=iphone*] = YES +ENABLE_BITCODE[sdk=appletvsimulator*] = YES +ENABLE_BITCODE[sdk=appletv*] = YES +FRAMEWORK_VERSION[sdk=macosx*] = A +TARGETED_DEVICE_FAMILY[sdk=appletv*] = 3 +TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2 +TARGETED_DEVICE_FAMILY[sdk=watch*] = 4 + \ No newline at end of file diff --git a/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Test.xcconfig b/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Test.xcconfig new file mode 100644 index 00000000..be110ad7 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/PrimeTime/Shared/Test.xcconfig @@ -0,0 +1,10 @@ +#include "Base.xcconfig" + +FRAMEWORK_SEARCH_PATHS = $(inherited) '$(PLATFORM_DIR)/Developer/Library/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) '@executable_path/../Frameworks' '@loader_path/../Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=iphoneos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=iphonesimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=watchos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=watchsimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=appletvos*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +LD_RUNPATH_SEARCH_PATHS[sdk=appletvsimulator*] = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' diff --git a/0099-ergonomic-state-management-pt2/README.md b/0099-ergonomic-state-management-pt2/README.md new file mode 100644 index 00000000..d3c6b0e4 --- /dev/null +++ b/0099-ergonomic-state-management-pt2/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Ergonomic State Management: Part 2](https://www.pointfree.co/episodes/ep99-ergonomic-state-management-part-2) +> +> We've made creating and enhancing reducers, but we still haven't given much attention to the ergonomics of the view layer of the Composable Architecture. This week we'll make the Store much nicer to use by taking advantage of a new Swift feature and by enhancing it with a SwiftUI helper. diff --git a/README.md b/README.md index f2343602..7d10c392 100644 --- a/README.md +++ b/README.md @@ -100,3 +100,5 @@ This repository is the home of code written on episodes of 1. [Adaptive State Management: State](0095-adaptive-state-management-pt2) 1. [Adaptive State Management: Actions](0096-adaptive-state-management-pt3) 1. [Adaptive State Management: The Point](0097-adaptive-state-management-pt4) +1. [Ergonomic State Management: Part 1](0098-ergonomic-state-management-pt1) +1. [Ergonomic State Management: Part 2](0099-ergonomic-state-management-pt2)