From c37e20ff4e6543d56006da4e0f6c16d8c7657044 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Sun, 3 Nov 2019 22:31:34 -0500 Subject: [PATCH] 79 --- .../ComposableArchitecture.swift | 77 +- .../PrimeTime/Counter/Counter.swift | 15 +- .../FavoritePrimes/FavoritePrimes.swift | 7 +- .../README.md | 2 +- .../ComposableArchitecture.h | 19 + .../ComposableArchitecture.swift | 133 ++ .../ComposableArchitecture/Effects.swift | 29 + .../ComposableArchitecture/Info.plist | 22 + .../ComposableArchitectureTests.swift | 34 + .../ComposableArchitectureTests/Info.plist | 22 + .../PrimeTime/Counter/Counter.h | 19 + .../PrimeTime/Counter/Counter.swift | 193 ++ .../PrimeTime/Counter/Info.plist | 22 + .../PrimeTime/Counter/WolframAlpha.swift | 58 + .../PrimeTime/CounterTests/CounterTests.swift | 34 + .../PrimeTime/CounterTests/Info.plist | 22 + .../PrimeTime/FavoritePrimes/FavoritePrimes.h | 19 + .../FavoritePrimes/FavoritePrimes.swift | 95 + .../PrimeTime/FavoritePrimes/Info.plist | 22 + .../FavoritePrimesTests.swift | 34 + .../PrimeTime/FavoritePrimesTests/Info.plist | 22 + .../PrimeTime/Package.swift | 9 + .../PrimeTime/PrimeModal/Info.plist | 22 + .../PrimeTime/PrimeModal/PrimeModal.h | 19 + .../PrimeTime/PrimeModal/PrimeModal.swift | 57 + .../PrimeTime/PrimeModalTests/Info.plist | 22 + .../PrimeModalTests/PrimeModalTests.swift | 34 + .../Counter.xcplaygroundpage/Contents.swift | 18 + .../Contents.swift | 27 + .../Contents.swift | 13 + .../contents.xcplayground | 2 + .../PrimeTime.xcodeproj/project.pbxproj | 1702 +++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../PrimeTime/PrimeTime/AppDelegate.swift | 17 + .../AppIcon.appiconset/Contents.json | 98 + .../PrimeTime/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../PrimeTime/PrimeTime/ContentView.swift | 138 ++ .../PrimeTime/PrimeTime/Info.plist | 60 + .../Preview Assets.xcassets/Contents.json | 6 + .../PrimeTime/PrimeTime/SceneDelegate.swift | 29 + .../PrimeTime/PrimeTime/Util.swift | 14 + .../PrimeTime/PrimeTimeTests/Info.plist | 22 + .../PrimeTimeTests/PrimeTimeTests.swift | 34 + 0079-effectful-state-management-wtp/README.md | 5 + README.md | 1 + 47 files changed, 3269 insertions(+), 26 deletions(-) create mode 100644 0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.h create mode 100644 0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Effects.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.h create mode 100644 0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/Counter/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/Counter/WolframAlpha.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/CounterTests/CounterTests.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/CounterTests/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.h create mode 100644 0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/Package.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeModal/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.h create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/PrimeModalTests.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/contents.xcplayground create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.pbxproj create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/AppDelegate.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/Contents.json create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/ContentView.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/SceneDelegate.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Util.swift create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/Info.plist create mode 100644 0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift create mode 100644 0079-effectful-state-management-wtp/README.md diff --git a/0078-effectful-state-management-async-effects/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift b/0078-effectful-state-management-async-effects/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift index fc2b24da..721beb45 100644 --- a/0078-effectful-state-management-async-effects/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift +++ b/0078-effectful-state-management-async-effects/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift @@ -9,7 +9,25 @@ struct Parallel { //UIView.animate(withDuration: TimeInterval, animations: () -> Void) -> Void //URLSession.shared.dataTask(with: URL, completionHandler: (Data?, URLResponse?, Error?) -> Void) -> Void -public typealias Effect = (@escaping (Action) -> Void) -> Void +//public typealias Effect = (@escaping (Action) -> Void) -> Void + +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 Publisher where Failure == Never { + public func eraseToEffect() -> Effect { + return Effect(publisher: self.eraseToAnyPublisher()) + } +} public typealias Reducer = (inout Value, Action) -> [Effect] @@ -18,7 +36,8 @@ public typealias Reducer = (inout Value, Action) -> [Effect: ObservableObject { private let reducer: Reducer @Published public private(set) var value: Value - private var cancellable: Cancellable? + private var viewCancellable: Cancellable? + private var effectCancellables: [AnyCancellable] = [] public init(initialValue: Value, reducer: @escaping Reducer) { self.reducer = reducer @@ -28,7 +47,15 @@ public final class Store: ObservableObject { public func send(_ action: Action) { let effects = self.reducer(&self.value, action) effects.forEach { effect in - effect(self.send) + var effectCancellable: AnyCancellable! + effectCancellable = effect + .sink( + receiveCompletion: { [weak self] _ in + self?.effectCancellables.removeAll(where: { $0 == effectCancellable }) + }, + receiveValue: self.send + ) + self.effectCancellables.append(effectCancellable) } // DispatchQueue.global().async { // effects.forEach { effect in @@ -53,7 +80,7 @@ public final class Store: ObservableObject { return [] } ) - localStore.cancellable = self.$value.sink { [weak localStore] newValue in + localStore.viewCancellable = self.$value.sink { [weak localStore] newValue in localStore?.value = toLocalValue(newValue) } return localStore @@ -78,14 +105,13 @@ public func pullback( guard let localAction = globalAction[keyPath: action] else { return [] } let localEffects = reducer(&globalValue[keyPath: value], localAction) return localEffects.map { localEffect in - { callback in -// guard let localAction = localEffect() else { return nil } - localEffect { localAction in + localEffect + .map { localAction -> GlobalAction in var globalAction = globalAction globalAction[keyPath: action] = localAction - callback(globalAction) - } + return globalAction } + .eraseToEffect() } } } @@ -96,11 +122,32 @@ public func logging( return { value, action in let effects = reducer(&value, action) let newValue = value - return [{ _ in - print("Action: \(action)") - print("Value:") - dump(newValue) - print("---") - }] + effects + return [ + Deferred { + Future { _ in + print("Action: \(action)") + print("Value:") + dump(newValue) + print("---") + } + }.eraseToEffect() + ] + effects + } +} + +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() } } diff --git a/0078-effectful-state-management-async-effects/PrimeTime/Counter/Counter.swift b/0078-effectful-state-management-async-effects/PrimeTime/Counter/Counter.swift index 891c3414..3ea867f9 100644 --- a/0078-effectful-state-management-async-effects/PrimeTime/Counter/Counter.swift +++ b/0078-effectful-state-management-async-effects/PrimeTime/Counter/Counter.swift @@ -29,12 +29,12 @@ public func counterReducer(state: inout CounterState, action: CounterAction) -> case .nthPrimeButtonTapped: state.isNthPrimeButtonDisabled = true let count = state.count - return [{ callback in - nthPrime(count) { prime in - DispatchQueue.main.async { - callback(.nthPrimeResponse(prime)) - } - } +// return [{ callback in +// nthPrime(count) { prime in +// DispatchQueue.main.async { +// callback(.nthPrimeResponse(prime)) +// } +// } // var p: Int? // let sema = DispatchSemaphore(value: 0) // nthPrime(count) { prime in @@ -43,7 +43,8 @@ public func counterReducer(state: inout CounterState, action: CounterAction) -> // } // sema.wait() // return .nthPrimeResponse(p) - }] +// }] + return [] case let .nthPrimeResponse(prime): state.alertNthPrime = prime.map(PrimeAlert.init(prime:)) diff --git a/0078-effectful-state-management-async-effects/PrimeTime/FavoritePrimes/FavoritePrimes.swift b/0078-effectful-state-management-async-effects/PrimeTime/FavoritePrimes/FavoritePrimes.swift index 6c361e4e..fc85ee45 100644 --- a/0078-effectful-state-management-async-effects/PrimeTime/FavoritePrimes/FavoritePrimes.swift +++ b/0078-effectful-state-management-async-effects/PrimeTime/FavoritePrimes/FavoritePrimes.swift @@ -30,7 +30,7 @@ public func favoritePrimesReducer(state: inout [Int], action: FavoritePrimesActi } private func saveEffect(favoritePrimes: [Int]) -> Effect { - return { + return .fireAndForget { let data = try! JSONEncoder().encode(favoritePrimes) let documentsPath = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true @@ -39,11 +39,10 @@ private func saveEffect(favoritePrimes: [Int]) -> Effect { let favoritePrimesUrl = documentsUrl .appendingPathComponent("favorite-primes.json") try! data.write(to: favoritePrimesUrl) - return nil } } -private let loadEffect: Effect = { +private let loadEffect = Effect.sync { let documentsPath = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true )[0] @@ -56,6 +55,8 @@ private let loadEffect: Effect = { else { return nil } return .loadedFavoritePrimes(favoritePrimes) } +.compactMap { $0 } +.eraseToEffect() public struct FavoritePrimesView: View { @ObservedObject var store: Store<[Int], FavoritePrimesAction> diff --git a/0078-effectful-state-management-async-effects/README.md b/0078-effectful-state-management-async-effects/README.md index d02b75fb..467cc82d 100644 --- a/0078-effectful-state-management-async-effects/README.md +++ b/0078-effectful-state-management-async-effects/README.md @@ -1,5 +1,5 @@ ## [Point-Free](https://www.pointfree.co) -> #### This directory contains code from Point-Free Episode: [Effectful State Management: Unidirectional Effects](https://www.pointfree.co/episodes/ep77-effectful-state-management-unidirectional-effects) +> #### This directory contains code from Point-Free Episode: [Effectful State Management: Asynchronous Effects](https://www.pointfree.co/episodes/ep78-effectful-state-management-asynchronous-effects) > > It's time to finish our architecture's story for side effects. We've described synchronous effects and unidirectional effects, but we still haven't captured the complexity of async effects. Let's fix that with a final, functional refactor. diff --git a/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.h b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.h new file mode 100644 index 00000000..38201c51 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift new file mode 100644 index 00000000..233cdc64 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/ComposableArchitecture.swift @@ -0,0 +1,133 @@ +import Combine +import SwiftUI + + +struct Parallel { + let run: (@escaping (A) -> Void) -> Void +} + +//DispatchQueue.main.async(execute: <#T##() -> Void#>) -> Void +//UIView.animate(withDuration: <#T##TimeInterval#>, animations: <#T##() -> Void#>) -> Void +//URLSession.shared.dataTask(with: <#T##URL#>, completionHandler: <#T##(Data?, URLResponse?, Error?) -> Void#>) -> Void + +//public typealias Effect = (@escaping (Action) -> Void) -> Void + + +public struct Effect { + public let run: (@escaping (A) -> Void) -> Void + + public init(run: @escaping (@escaping (A) -> Void) -> Void) { + self.run = run + } + + public func map(_ f: @escaping (A) -> B) -> Effect { + return Effect { callback in self.run { a in callback(f(a)) } } + } +} + +public typealias Reducer = (inout Value, Action) -> [Effect] + +//Button.init("Save", action: <#T##() -> Void#>) + +public final class Store: ObservableObject { + private let reducer: Reducer + @Published public private(set) var value: Value + private var cancellable: Cancellable? + + public init(initialValue: Value, reducer: @escaping Reducer) { + self.reducer = reducer + self.value = initialValue + } + + public func send(_ action: Action) { + let effects = self.reducer(&self.value, action) + effects.forEach { effect in + effect.run(self.send) + } +// DispatchQueue.global().async { +// effects.forEach { effect in +// if let action = effect() { +// DispatchQueue.main.async { +// self.send(action) +// } +// } +// } +// } + } + + public func view( + value toLocalValue: @escaping (Value) -> LocalValue, + action toGlobalAction: @escaping (LocalAction) -> Action + ) -> Store { + let localStore = Store( + initialValue: toLocalValue(self.value), + reducer: { localValue, localAction in + self.send(toGlobalAction(localAction)) + localValue = toLocalValue(self.value) + return [] + } + ) + localStore.cancellable = self.$value.sink { [weak localStore] newValue in + localStore?.value = toLocalValue(newValue) + } + return localStore + } +} + +public func combine( + _ reducers: Reducer... +) -> Reducer { + return { value, action in + let effects = reducers.flatMap { $0(&value, action) } + return effects +// return { () -> Action? in +// var finalAction: Action? +// for effect in effects { +// let action = effect() +// if let action = action { +// finalAction = action +// } +// } +// return finalAction +// } + } +} + +public func pullback( + _ reducer: @escaping Reducer, + value: WritableKeyPath, + action: WritableKeyPath +) -> Reducer { + return { globalValue, globalAction in + guard let localAction = globalAction[keyPath: action] else { return [] } + let localEffects = reducer(&globalValue[keyPath: value], localAction) + + return localEffects.map { localEffect in + Effect { callback in +// guard let localAction = localEffect() else { return nil } + localEffect.run { localAction in + var globalAction = globalAction + globalAction[keyPath: action] = localAction + callback(globalAction) + } + } + } + +// return effect + } +} + +public func logging( + _ reducer: @escaping Reducer +) -> Reducer { + return { value, action in + let effects = reducer(&value, action) + let newValue = value + return [Effect { _ in + print("Action: \(action)") + print("Value:") + dump(newValue) + print("---") + }] + effects + } +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Effects.swift b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Effects.swift new file mode 100644 index 00000000..e4691b9e --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Effects.swift @@ -0,0 +1,29 @@ +extension Effect where A == (Data?, URLResponse?, Error?) { + public func decode(as type: M.Type) -> Effect { + return self.map { data, _, _ in + data + .flatMap { try? JSONDecoder().decode(M.self, from: $0) } + } + } +} + +extension Effect { + public func receive(on queue: DispatchQueue) -> Effect { + return Effect { callback in + self.run { a in + queue.async { + callback(a) + } + } + } + } +} + +public func dataTask(with url: URL) -> Effect<(Data?, URLResponse?, Error?)> { + return Effect { callback in + URLSession.shared.dataTask(with: url) { data, response, error in + callback((data, response, error)) + } + .resume() + } +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitecture/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift new file mode 100644 index 00000000..ea4b132f --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/ComposableArchitectureTests.swift @@ -0,0 +1,34 @@ +// +// ComposableArchitectureTests.swift +// ComposableArchitectureTests +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +import XCTest +@testable import ComposableArchitecture + +class ComposableArchitectureTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/ComposableArchitectureTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.h b/0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.h new file mode 100644 index 00000000..ba74c646 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.swift b/0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.swift new file mode 100644 index 00000000..a51d02d9 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/Counter/Counter.swift @@ -0,0 +1,193 @@ +import ComposableArchitecture +import PrimeModal +import SwiftUI + +public enum CounterAction { + case decrTapped + case incrTapped + case nthPrimeButtonTapped + case nthPrimeResponse(Int?) + case alertDismissButtonTapped +} + +public typealias CounterState = ( + alertNthPrime: PrimeAlert?, + count: Int, + isNthPrimeButtonDisabled: Bool +) + +public func counterReducer(state: inout CounterState, action: CounterAction) -> [Effect] { + switch action { + case .decrTapped: + state.count -= 1 + return [] + + case .incrTapped: + state.count += 1 + return [] + + case .nthPrimeButtonTapped: + state.isNthPrimeButtonDisabled = true + return [ + nthPrime(state.count) + .map(CounterAction.nthPrimeResponse) + .receive(on: .main) + + +// Effect { callback in +// nthPrime(count) { prime in +// DispatchQueue.main.async { +// callback(.nthPrimeResponse(prime)) +// } +// } +// var p: Int? +// let sema = DispatchSemaphore(value: 0) +// nthPrime(count) { prime in +// p = prime +// sema.signal() +// } +// sema.wait() +// return .nthPrimeResponse(p) +// } + ] + + case let .nthPrimeResponse(prime): + state.alertNthPrime = prime.map(PrimeAlert.init(prime:)) + state.isNthPrimeButtonDisabled = false + return [] + + case .alertDismissButtonTapped: + state.alertNthPrime = nil + return [] + } +} + +public let counterViewReducer = combine( + pullback(counterReducer, value: \CounterViewState.counter, action: \CounterViewAction.counter), + pullback(primeModalReducer, value: \.primeModal, action: \.primeModal) +) + +public struct PrimeAlert: Identifiable { + let prime: Int + public var id: Int { self.prime } +} + +public struct CounterViewState { + public var alertNthPrime: PrimeAlert? + public var count: Int + public var favoritePrimes: [Int] + public var isNthPrimeButtonDisabled: Bool + + public init( + alertNthPrime: PrimeAlert?, + count: Int, + favoritePrimes: [Int], + isNthPrimeButtonDisabled: Bool + ) { + self.alertNthPrime = alertNthPrime + self.count = count + self.favoritePrimes = favoritePrimes + self.isNthPrimeButtonDisabled = isNthPrimeButtonDisabled + } + + var counter: CounterState { + get { (self.alertNthPrime, self.count, self.isNthPrimeButtonDisabled) } + set { (self.alertNthPrime, self.count, self.isNthPrimeButtonDisabled) = newValue } + } + + var primeModal: PrimeModalState { + get { (self.count, self.favoritePrimes) } + set { (self.count, self.favoritePrimes) = newValue } + } +} + +public enum CounterViewAction { + case counter(CounterAction) + case primeModal(PrimeModalAction) + + var counter: CounterAction? { + get { + guard case let .counter(value) = self else { return nil } + return value + } + set { + guard case .counter = self, let newValue = newValue else { return } + self = .counter(newValue) + } + } + + var primeModal: PrimeModalAction? { + get { + guard case let .primeModal(value) = self else { return nil } + return value + } + set { + guard case .primeModal = self, let newValue = newValue else { return } + self = .primeModal(newValue) + } + } + +} + +public struct CounterView: View { + @ObservedObject var store: Store + @State var isPrimeModalShown = false +// @State var alertNthPrime: PrimeAlert? +// @State var isNthPrimeButtonDisabled = false + + public init(store: Store) { + self.store = store + } + + public var body: some View { + VStack { + HStack { + Button("-") { self.store.send(.counter(.decrTapped)) } + Text("\(self.store.value.count)") + Button("+") { self.store.send(.counter(.incrTapped)) } + } + Button("Is this prime?") { self.isPrimeModalShown = true } + Button( + "What is the \(ordinal(self.store.value.count)) prime?", + action: self.nthPrimeButtonAction + ) + .disabled(self.store.value.isNthPrimeButtonDisabled) + } + .font(.title) + .navigationBarTitle("Counter demo") + .sheet(isPresented: self.$isPrimeModalShown) { + IsPrimeModalView( + store: self.store + .view( + value: { ($0.count, $0.favoritePrimes) }, + action: { .primeModal($0) } + ) + ) + } + .alert( + item: .constant(self.store.value.alertNthPrime) + ) { alert in + Alert( + title: Text("The \(ordinal(self.store.value.count)) prime is \(alert.prime)"), + dismissButton: .default(Text("Ok")) { + self.store.send(.counter(.alertDismissButtonTapped)) + } + ) + } + } + + func nthPrimeButtonAction() { +// self.isNthPrimeButtonDisabled = true +// nthPrime(self.store.value.count) { prime in +// self.alertNthPrime = prime.map(PrimeAlert.init(prime:)) +// self.isNthPrimeButtonDisabled = false +// } + self.store.send(.counter(.nthPrimeButtonTapped)) + } +} + +func ordinal(_ n: Int) -> String { + let formatter = NumberFormatter() + formatter.numberStyle = .ordinal + return formatter.string(for: n) ?? "" +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/Counter/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/Counter/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/Counter/WolframAlpha.swift b/0079-effectful-state-management-wtp/PrimeTime/Counter/WolframAlpha.swift new file mode 100644 index 00000000..6bc21d17 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/Counter/WolframAlpha.swift @@ -0,0 +1,58 @@ +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 + } + } + } +} + +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) + } +} + +import ComposableArchitecture + +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 dataTask(with: components.url(relativeTo: nil)!) + .decode(as: WolframAlphaResult.self) +} + +//return [Effect { callback in +// nthPrime(count) { prime in +// DispatchQueue.main.async { +// callback(.nthPrimeResponse(prime)) +// } +// } +//}] diff --git a/0079-effectful-state-management-wtp/PrimeTime/CounterTests/CounterTests.swift b/0079-effectful-state-management-wtp/PrimeTime/CounterTests/CounterTests.swift new file mode 100644 index 00000000..5fb9df79 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/CounterTests/CounterTests.swift @@ -0,0 +1,34 @@ +// +// CounterTests.swift +// CounterTests +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +import XCTest +@testable import Counter + +class CounterTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/CounterTests/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/CounterTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.h b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.h new file mode 100644 index 00000000..b0a43669 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.swift b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.swift new file mode 100644 index 00000000..60f4972e --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/FavoritePrimes.swift @@ -0,0 +1,95 @@ +import ComposableArchitecture +import SwiftUI + +public enum FavoritePrimesAction { + case deleteFavoritePrimes(IndexSet) + case loadButtonTapped + case loadedFavoritePrimes([Int]) + case saveButtonTapped +} + +public func favoritePrimesReducer(state: inout [Int], action: FavoritePrimesAction) -> [Effect] { + switch action { + case let .deleteFavoritePrimes(indexSet): + for index in indexSet { + state.remove(at: index) + } + return [] + + case let .loadedFavoritePrimes(favoritePrimes): + state = favoritePrimes + return [] + + case .saveButtonTapped: +// let state = state + return [saveEffect(favoritePrimes: state)] + + case .loadButtonTapped: + return [loadEffect] + } +} + +private func saveEffect(favoritePrimes: [Int]) -> Effect { + return Effect { _ in + let data = try! JSONEncoder().encode(favoritePrimes) + let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + let documentsUrl = URL(fileURLWithPath: documentsPath) + let favoritePrimesUrl = documentsUrl.appendingPathComponent("favorite-primes.json") + try! data.write(to: favoritePrimesUrl) + } +} + +private let loadEffect = Effect { callback in + let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + let documentsUrl = URL(fileURLWithPath: documentsPath) + let favoritePrimesUrl = documentsUrl.appendingPathComponent("favorite-primes.json") + guard + let data = try? Data(contentsOf: favoritePrimesUrl), + let favoritePrimes = try? JSONDecoder().decode([Int].self, from: data) + else { return } + // self.store.send(.loadedFavoritePrimes(favoritePrimes)) + callback(.loadedFavoritePrimes(favoritePrimes)) +} + +public struct FavoritePrimesView: View { + @ObservedObject var store: Store<[Int], FavoritePrimesAction> + + public init(store: Store<[Int], FavoritePrimesAction>) { + self.store = store + } + + public var body: some View { + List { + ForEach(self.store.value, id: \.self) { prime in + Text("\(prime)") + } + .onDelete { indexSet in + self.store.send(.deleteFavoritePrimes(indexSet)) + } + } + .navigationBarTitle("Favorite primes") + .navigationBarItems( + trailing: HStack { + Button("Save") { + self.store.send(.saveButtonTapped) +// let data = try! JSONEncoder().encode(self.store.value) +// let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] +// let documentsUrl = URL(fileURLWithPath: documentsPath) +// let favoritePrimesUrl = documentsUrl.appendingPathComponent("favorite-primes.json") +// try! data.write(to: favoritePrimesUrl) + } + Button("Load") { + self.store.send(.loadButtonTapped) +// let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] +// let documentsUrl = URL(fileURLWithPath: documentsPath) +// let favoritePrimesUrl = documentsUrl.appendingPathComponent("favorite-primes.json") +// guard +// let data = try? Data(contentsOf: favoritePrimesUrl), +// let favoritePrimes = try? JSONDecoder().decode([Int].self, from: data) +// else { return } +// self.store.send(.loadedFavoritePrimes(favoritePrimes)) + } + } + ) + } +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimes/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift new file mode 100644 index 00000000..1fcd30d5 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/FavoritePrimesTests.swift @@ -0,0 +1,34 @@ +// +// FavoritePrimesTests.swift +// FavoritePrimesTests +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +import XCTest +@testable import FavoritePrimes + +class FavoritePrimesTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/FavoritePrimesTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/Package.swift b/0079-effectful-state-management-wtp/PrimeTime/Package.swift new file mode 100644 index 00000000..89f8b7a3 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/Info.plist new file mode 100644 index 00000000..9bcb2444 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.h b/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.h new file mode 100644 index 00000000..5bdc9d8f --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.swift new file mode 100644 index 00000000..3c2a96a6 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeModal/PrimeModal.swift @@ -0,0 +1,57 @@ +import ComposableArchitecture +import SwiftUI + +public typealias PrimeModalState = (count: Int, favoritePrimes: [Int]) + +public enum PrimeModalAction { + case saveFavoritePrimeTapped + case removeFavoritePrimeTapped +} + +public func primeModalReducer(state: inout PrimeModalState, action: PrimeModalAction) -> [Effect] { + switch action { + case .removeFavoritePrimeTapped: + state.favoritePrimes.removeAll(where: { $0 == state.count }) + return [] + + case .saveFavoritePrimeTapped: + state.favoritePrimes.append(state.count) + return [] + } +} + +public struct IsPrimeModalView: View { + @ObservedObject var store: Store + + public init(store: Store) { + self.store = store + } + + public var body: some View { + VStack { + if isPrime(self.store.value.count) { + Text("\(self.store.value.count) is prime 🎉") + if self.store.value.favoritePrimes.contains(self.store.value.count) { + Button("Remove from favorite primes") { + self.store.send(.removeFavoritePrimeTapped) + } + } else { + Button("Save to favorite primes") { + self.store.send(.saveFavoritePrimeTapped) + } + } + } else { + Text("\(self.store.value.count) is not prime :(") + } + } + } +} + +func isPrime(_ p: Int) -> Bool { + if p <= 1 { return false } + if p <= 3 { return true } + for i in 2...Int(sqrtf(Float(p))) { + if p % i == 0 { return false } + } + return true +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/PrimeModalTests.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/PrimeModalTests.swift new file mode 100644 index 00000000..3d7655b3 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeModalTests/PrimeModalTests.swift @@ -0,0 +1,34 @@ +// +// PrimeModalTests.swift +// PrimeModalTests +// +// Created by Stephen Celis on 9/8/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +import XCTest +@testable import PrimeModal + +class PrimeModalTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..f57c5bc8 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Counter.xcplaygroundpage/Contents.swift @@ -0,0 +1,18 @@ +import ComposableArchitecture +import Counter +import PlaygroundSupport +import SwiftUI + +PlaygroundPage.current.liveView = UIHostingController( + rootView: CounterView( + store: Store( + initialValue: CounterViewState( + alertNthPrime: nil, + count: 0, + favoritePrimes: [], + isNthPrimeButtonDisabled: false + ), + reducer: logging(counterViewReducer) + ) + ) +) diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..3a958d65 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Favorite Primes.xcplaygroundpage/Contents.swift @@ -0,0 +1,27 @@ +import ComposableArchitecture +import FavoritePrimes +import PlaygroundSupport +import SwiftUI + +PlaygroundPage.current.liveView = UIHostingController( + rootView: NavigationView { + FavoritePrimesView( + store: Store<[Int], FavoritePrimesAction>( + initialValue: [2, 3, 5, 7, 11], + reducer: favoritePrimesReducer + ) + ) + } +) + +// +//func compute(_ x: Int) -> Int { +// let computation = x * x + 1 +// print("Computed \(computation)") +// return computation +//} +// +//func computeAndPrint(_ x: Int) -> (Int, [String]) { +// let computation = x * x + 1 +// return (computation, ["Computed \(computation)"]) +//} diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..98e2e625 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/Pages/Prime Modal.xcplaygroundpage/Contents.swift @@ -0,0 +1,13 @@ +import ComposableArchitecture +import PlaygroundSupport +import PrimeModal +import SwiftUI + +PlaygroundPage.current.liveView = UIHostingController( + rootView: IsPrimeModalView( + store: Store( + initialValue: (2, [2, 3, 5]), + reducer: primeModalReducer + ) + ) +) diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/contents.xcplayground b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/contents.xcplayground new file mode 100644 index 00000000..8d8c40ca --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.pbxproj b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.pbxproj new file mode 100644 index 00000000..44a359bc --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.pbxproj @@ -0,0 +1,1702 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + CAD67E2023329887000C7787 /* WolframAlpha.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC29217322FB231F006090DF /* WolframAlpha.swift */; }; + CAE7DF61234E7E4A00A6076A /* Effects.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAE7DF60234E7E4A00A6076A /* Effects.swift */; }; + DC36ED17234CD8040027F7A1 /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; + DC90717022FA102900B38B42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90716F22FA102900B38B42 /* AppDelegate.swift */; }; + DC90717222FA102900B38B42 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90717122FA102900B38B42 /* SceneDelegate.swift */; }; + DC90717422FA102900B38B42 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90717322FA102900B38B42 /* ContentView.swift */; }; + DC90717622FA102A00B38B42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC90717522FA102A00B38B42 /* Assets.xcassets */; }; + DC90717922FA102A00B38B42 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC90717822FA102A00B38B42 /* Preview Assets.xcassets */; }; + DC90717C22FA102A00B38B42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC90717A22FA102A00B38B42 /* LaunchScreen.storyboard */; }; + DC90718722FA102B00B38B42 /* PrimeTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90718622FA102B00B38B42 /* PrimeTimeTests.swift */; }; + DC90719222FA151D00B38B42 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC90719122FA151D00B38B42 /* Util.swift */; }; + DCF0C5A42326032B008B45A0 /* ComposableArchitecture.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; }; + DCF0C5AB2326032B008B45A0 /* ComposableArchitectureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C5AA2326032B008B45A0 /* ComposableArchitectureTests.swift */; }; + DCF0C5AD2326032B008B45A0 /* ComposableArchitecture.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C59D2326032B008B45A0 /* ComposableArchitecture.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C5B22326032B008B45A0 /* ComposableArchitecture.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C5C723260348008B45A0 /* Counter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5BE23260348008B45A0 /* Counter.framework */; }; + DCF0C5CE23260348008B45A0 /* CounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C5CD23260348008B45A0 /* CounterTests.swift */; }; + DCF0C5D023260348008B45A0 /* Counter.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C5C023260348008B45A0 /* Counter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C5D323260348008B45A0 /* Counter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5BE23260348008B45A0 /* Counter.framework */; }; + DCF0C5D423260348008B45A0 /* Counter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5BE23260348008B45A0 /* Counter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C5E92326035C008B45A0 /* PrimeModal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5E02326035C008B45A0 /* PrimeModal.framework */; }; + DCF0C5F02326035C008B45A0 /* PrimeModalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C5EF2326035C008B45A0 /* PrimeModalTests.swift */; }; + DCF0C5F22326035C008B45A0 /* PrimeModal.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C5E22326035C008B45A0 /* PrimeModal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C5F62326035C008B45A0 /* PrimeModal.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C5E02326035C008B45A0 /* PrimeModal.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C60B2326036F008B45A0 /* FavoritePrimes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; }; + DCF0C6122326036F008B45A0 /* FavoritePrimesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C6112326036F008B45A0 /* FavoritePrimesTests.swift */; }; + DCF0C6142326036F008B45A0 /* FavoritePrimes.h in Headers */ = {isa = PBXBuildFile; fileRef = DCF0C6042326036F008B45A0 /* FavoritePrimes.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DCF0C6172326036F008B45A0 /* FavoritePrimes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; }; + DCF0C6182326036F008B45A0 /* FavoritePrimes.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + DCF0C628232603B7008B45A0 /* ComposableArchitecture.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C627232603B7008B45A0 /* ComposableArchitecture.swift */; }; + DCF0C62A23260405008B45A0 /* FavoritePrimes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C62923260405008B45A0 /* FavoritePrimes.swift */; }; + DCF0C62C2326040D008B45A0 /* PrimeModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C62B2326040D008B45A0 /* PrimeModal.swift */; }; + DCF0C62E23260414008B45A0 /* Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF0C62D23260414008B45A0 /* Counter.swift */; }; + 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 */ + CAD67E1C2332982E000C7787 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5DF2326035C008B45A0; + remoteInfo = PrimeModal; + }; + DC90718322FA102B00B38B42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C5A52326032B008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C5A72326032B008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C5AE2326032B008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C5C823260348008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5BD23260348008B45A0; + remoteInfo = Counter; + }; + DCF0C5CA23260348008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C5D123260348008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5BD23260348008B45A0; + remoteInfo = Counter; + }; + DCF0C5EA2326035C008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C5DF2326035C008B45A0; + remoteInfo = PrimeModal; + }; + DCF0C5EC2326035C008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C60C2326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C6012326036F008B45A0; + remoteInfo = FavoritePrimes; + }; + DCF0C60E2326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC90716B22FA102900B38B42; + remoteInfo = PrimeTime; + }; + DCF0C6152326036F008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C6012326036F008B45A0; + remoteInfo = FavoritePrimes; + }; + DCF0C61F23260383008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C62123260387008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; + DCF0C6232326038A008B45A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC90716422FA102900B38B42 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DCF0C59A2326032B008B45A0; + remoteInfo = ComposableArchitecture; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + DCF0C5B12326032B008B45A0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + DCF0C6182326036F008B45A0 /* FavoritePrimes.framework in Embed Frameworks */, + DCF0C5B22326032B008B45A0 /* ComposableArchitecture.framework in Embed Frameworks */, + DCF0C5D423260348008B45A0 /* Counter.framework in Embed Frameworks */, + DCF0C5F62326035C008B45A0 /* PrimeModal.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + CAE7DF60234E7E4A00A6076A /* Effects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Effects.swift; sourceTree = ""; }; + DC29217322FB231F006090DF /* WolframAlpha.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WolframAlpha.swift; sourceTree = ""; }; + DC39325F230241AF005A0B0A /* PrimeTime.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = PrimeTime.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + DC90716C22FA102900B38B42 /* PrimeTime.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PrimeTime.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DC90716F22FA102900B38B42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + DC90717122FA102900B38B42 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + DC90717322FA102900B38B42 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + DC90717522FA102A00B38B42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DC90717822FA102A00B38B42 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + DC90717B22FA102A00B38B42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + DC90717D22FA102A00B38B42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DC90718222FA102B00B38B42 /* PrimeTimeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrimeTimeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DC90718622FA102B00B38B42 /* PrimeTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeTimeTests.swift; sourceTree = ""; }; + DC90718822FA102B00B38B42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DC90719122FA151D00B38B42 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = ""; }; + DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ComposableArchitecture.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C59D2326032B008B45A0 /* ComposableArchitecture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ComposableArchitecture.h; sourceTree = ""; }; + DCF0C59E2326032B008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5A32326032B008B45A0 /* ComposableArchitectureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ComposableArchitectureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5AA2326032B008B45A0 /* ComposableArchitectureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableArchitectureTests.swift; sourceTree = ""; }; + DCF0C5AC2326032B008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5BE23260348008B45A0 /* Counter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Counter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5C023260348008B45A0 /* Counter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Counter.h; sourceTree = ""; }; + DCF0C5C123260348008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5C623260348008B45A0 /* CounterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CounterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5CD23260348008B45A0 /* CounterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CounterTests.swift; sourceTree = ""; }; + DCF0C5CF23260348008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5E02326035C008B45A0 /* PrimeModal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrimeModal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5E22326035C008B45A0 /* PrimeModal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrimeModal.h; sourceTree = ""; }; + DCF0C5E32326035C008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C5E82326035C008B45A0 /* PrimeModalTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrimeModalTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C5EF2326035C008B45A0 /* PrimeModalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeModalTests.swift; sourceTree = ""; }; + DCF0C5F12326035C008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FavoritePrimes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C6042326036F008B45A0 /* FavoritePrimes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FavoritePrimes.h; sourceTree = ""; }; + DCF0C6052326036F008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C60A2326036F008B45A0 /* FavoritePrimesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FavoritePrimesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DCF0C6112326036F008B45A0 /* FavoritePrimesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritePrimesTests.swift; sourceTree = ""; }; + DCF0C6132326036F008B45A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCF0C627232603B7008B45A0 /* ComposableArchitecture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposableArchitecture.swift; sourceTree = ""; }; + DCF0C62923260405008B45A0 /* FavoritePrimes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritePrimes.swift; sourceTree = ""; }; + DCF0C62B2326040D008B45A0 /* PrimeModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimeModal.swift; sourceTree = ""; }; + DCF0C62D23260414008B45A0 /* Counter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Counter.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DC90716922FA102900B38B42 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF88F02234D4A3B001BA79A /* ComposableArchitecture.framework in Frameworks */, + DCF0C5D323260348008B45A0 /* Counter.framework in Frameworks */, + DCF0C6172326036F008B45A0 /* FavoritePrimes.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90717F22FA102B00B38B42 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5982326032B008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5A02326032B008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5A42326032B008B45A0 /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5BB23260348008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF88F01234D4A28001BA79A /* PrimeModal.framework in Frameworks */, + DCF88F00234D4A28001BA79A /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5C323260348008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5C723260348008B45A0 /* Counter.framework 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 = ( + DCF0C5E92326035C008B45A0 /* PrimeModal.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5FF2326036F008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DC36ED17234CD8040027F7A1 /* ComposableArchitecture.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6072326036F008B45A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C60B2326036F008B45A0 /* FavoritePrimes.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DC36ED16234CD8040027F7A1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + DC90716322FA102900B38B42 = { + isa = PBXGroup; + children = ( + DC39325F230241AF005A0B0A /* PrimeTime.playground */, + DC90716E22FA102900B38B42 /* PrimeTime */, + DC90718522FA102B00B38B42 /* PrimeTimeTests */, + DCF0C59C2326032B008B45A0 /* ComposableArchitecture */, + DCF0C5A92326032B008B45A0 /* ComposableArchitectureTests */, + DCF0C5BF23260348008B45A0 /* Counter */, + DCF0C5CC23260348008B45A0 /* CounterTests */, + DCF0C5E12326035C008B45A0 /* PrimeModal */, + DCF0C5EE2326035C008B45A0 /* PrimeModalTests */, + DCF0C6032326036F008B45A0 /* FavoritePrimes */, + DCF0C6102326036F008B45A0 /* FavoritePrimesTests */, + DC90716D22FA102900B38B42 /* Products */, + DC36ED16234CD8040027F7A1 /* Frameworks */, + ); + sourceTree = ""; + }; + DC90716D22FA102900B38B42 /* Products */ = { + isa = PBXGroup; + children = ( + DC90716C22FA102900B38B42 /* PrimeTime.app */, + DC90718222FA102B00B38B42 /* PrimeTimeTests.xctest */, + DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */, + DCF0C5A32326032B008B45A0 /* ComposableArchitectureTests.xctest */, + DCF0C5BE23260348008B45A0 /* Counter.framework */, + DCF0C5C623260348008B45A0 /* CounterTests.xctest */, + DCF0C5E02326035C008B45A0 /* PrimeModal.framework */, + DCF0C5E82326035C008B45A0 /* PrimeModalTests.xctest */, + DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */, + DCF0C60A2326036F008B45A0 /* FavoritePrimesTests.xctest */, + ); + 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 = ( + DC90718622FA102B00B38B42 /* PrimeTimeTests.swift */, + DC90718822FA102B00B38B42 /* Info.plist */, + ); + path = PrimeTimeTests; + sourceTree = ""; + }; + DCF0C59C2326032B008B45A0 /* ComposableArchitecture */ = { + isa = PBXGroup; + children = ( + DCF0C59D2326032B008B45A0 /* ComposableArchitecture.h */, + DCF0C627232603B7008B45A0 /* ComposableArchitecture.swift */, + CAE7DF60234E7E4A00A6076A /* Effects.swift */, + DCF0C59E2326032B008B45A0 /* Info.plist */, + ); + path = ComposableArchitecture; + sourceTree = ""; + }; + DCF0C5A92326032B008B45A0 /* ComposableArchitectureTests */ = { + isa = PBXGroup; + children = ( + DCF0C5AA2326032B008B45A0 /* ComposableArchitectureTests.swift */, + DCF0C5AC2326032B008B45A0 /* Info.plist */, + ); + path = ComposableArchitectureTests; + sourceTree = ""; + }; + DCF0C5BF23260348008B45A0 /* Counter */ = { + isa = PBXGroup; + children = ( + DCF0C5C023260348008B45A0 /* Counter.h */, + DCF0C62D23260414008B45A0 /* Counter.swift */, + DC29217322FB231F006090DF /* WolframAlpha.swift */, + DCF0C5C123260348008B45A0 /* Info.plist */, + ); + path = Counter; + sourceTree = ""; + }; + DCF0C5CC23260348008B45A0 /* CounterTests */ = { + isa = PBXGroup; + children = ( + DCF0C5CD23260348008B45A0 /* CounterTests.swift */, + DCF0C5CF23260348008B45A0 /* Info.plist */, + ); + path = CounterTests; + sourceTree = ""; + }; + DCF0C5E12326035C008B45A0 /* PrimeModal */ = { + isa = PBXGroup; + children = ( + DCF0C5E22326035C008B45A0 /* PrimeModal.h */, + DCF0C62B2326040D008B45A0 /* PrimeModal.swift */, + DCF0C5E32326035C008B45A0 /* Info.plist */, + ); + path = PrimeModal; + sourceTree = ""; + }; + DCF0C5EE2326035C008B45A0 /* PrimeModalTests */ = { + isa = PBXGroup; + children = ( + DCF0C5EF2326035C008B45A0 /* PrimeModalTests.swift */, + DCF0C5F12326035C008B45A0 /* Info.plist */, + ); + path = PrimeModalTests; + sourceTree = ""; + }; + DCF0C6032326036F008B45A0 /* FavoritePrimes */ = { + isa = PBXGroup; + children = ( + DCF0C6042326036F008B45A0 /* FavoritePrimes.h */, + DCF0C62923260405008B45A0 /* FavoritePrimes.swift */, + DCF0C6052326036F008B45A0 /* Info.plist */, + ); + path = FavoritePrimes; + sourceTree = ""; + }; + DCF0C6102326036F008B45A0 /* FavoritePrimesTests */ = { + isa = PBXGroup; + children = ( + DCF0C6112326036F008B45A0 /* FavoritePrimesTests.swift */, + DCF0C6132326036F008B45A0 /* Info.plist */, + ); + path = FavoritePrimesTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + DCF0C5962326032B008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5AD2326032B008B45A0 /* ComposableArchitecture.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5B923260348008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5D023260348008B45A0 /* Counter.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DB2326035C008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5F22326035C008B45A0 /* PrimeModal.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5FD2326036F008B45A0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C6142326036F008B45A0 /* FavoritePrimes.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + DC90716B22FA102900B38B42 /* PrimeTime */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC90718B22FA102B00B38B42 /* Build configuration list for PBXNativeTarget "PrimeTime" */; + buildPhases = ( + DC90716822FA102900B38B42 /* Sources */, + DC90716922FA102900B38B42 /* Frameworks */, + DC90716A22FA102900B38B42 /* Resources */, + DCF0C5B12326032B008B45A0 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C5AF2326032B008B45A0 /* PBXTargetDependency */, + DCF0C5D223260348008B45A0 /* PBXTargetDependency */, + DCF0C6162326036F008B45A0 /* PBXTargetDependency */, + ); + name = PrimeTime; + productName = PrimeTime; + productReference = DC90716C22FA102900B38B42 /* PrimeTime.app */; + productType = "com.apple.product-type.application"; + }; + DC90718122FA102B00B38B42 /* PrimeTimeTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC90718E22FA102B00B38B42 /* Build configuration list for PBXNativeTarget "PrimeTimeTests" */; + buildPhases = ( + DC90717E22FA102B00B38B42 /* Sources */, + DC90717F22FA102B00B38B42 /* Frameworks */, + DC90718022FA102B00B38B42 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 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; + productName = ComposableArchitecture; + productReference = DCF0C59B2326032B008B45A0 /* ComposableArchitecture.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C5A22326032B008B45A0 /* ComposableArchitectureTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5B82326032B008B45A0 /* Build configuration list for PBXNativeTarget "ComposableArchitectureTests" */; + buildPhases = ( + DCF0C59F2326032B008B45A0 /* Sources */, + DCF0C5A02326032B008B45A0 /* Frameworks */, + DCF0C5A12326032B008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C5A62326032B008B45A0 /* PBXTargetDependency */, + DCF0C5A82326032B008B45A0 /* PBXTargetDependency */, + ); + name = ComposableArchitectureTests; + productName = ComposableArchitectureTests; + productReference = DCF0C5A32326032B008B45A0 /* ComposableArchitectureTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF0C5BD23260348008B45A0 /* Counter */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5D523260348008B45A0 /* Build configuration list for PBXNativeTarget "Counter" */; + buildPhases = ( + DCF0C5B923260348008B45A0 /* Headers */, + DCF0C5BA23260348008B45A0 /* Sources */, + DCF0C5BB23260348008B45A0 /* Frameworks */, + DCF0C5BC23260348008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CAD67E1D2332982E000C7787 /* PBXTargetDependency */, + DCF0C6242326038A008B45A0 /* PBXTargetDependency */, + ); + name = Counter; + productName = Counter; + productReference = DCF0C5BE23260348008B45A0 /* Counter.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C5C523260348008B45A0 /* CounterTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C5D823260348008B45A0 /* Build configuration list for PBXNativeTarget "CounterTests" */; + buildPhases = ( + DCF0C5C223260348008B45A0 /* Sources */, + DCF0C5C323260348008B45A0 /* Frameworks */, + DCF0C5C423260348008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C5C923260348008B45A0 /* PBXTargetDependency */, + DCF0C5CB23260348008B45A0 /* PBXTargetDependency */, + ); + name = CounterTests; + 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 = ( + DCF0C5EB2326035C008B45A0 /* PBXTargetDependency */, + DCF0C5ED2326035C008B45A0 /* PBXTargetDependency */, + ); + name = PrimeModalTests; + productName = PrimeModalTests; + productReference = DCF0C5E82326035C008B45A0 /* PrimeModalTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DCF0C6012326036F008B45A0 /* FavoritePrimes */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C6192326036F008B45A0 /* Build configuration list for PBXNativeTarget "FavoritePrimes" */; + buildPhases = ( + DCF0C5FD2326036F008B45A0 /* Headers */, + DCF0C5FE2326036F008B45A0 /* Sources */, + DCF0C5FF2326036F008B45A0 /* Frameworks */, + DCF0C6002326036F008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C62023260383008B45A0 /* PBXTargetDependency */, + ); + name = FavoritePrimes; + productName = FavoritePrimes; + productReference = DCF0C6022326036F008B45A0 /* FavoritePrimes.framework */; + productType = "com.apple.product-type.framework"; + }; + DCF0C6092326036F008B45A0 /* FavoritePrimesTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCF0C61C2326036F008B45A0 /* Build configuration list for PBXNativeTarget "FavoritePrimesTests" */; + buildPhases = ( + DCF0C6062326036F008B45A0 /* Sources */, + DCF0C6072326036F008B45A0 /* Frameworks */, + DCF0C6082326036F008B45A0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DCF0C60D2326036F008B45A0 /* PBXTargetDependency */, + DCF0C60F2326036F008B45A0 /* PBXTargetDependency */, + ); + name = FavoritePrimesTests; + productName = FavoritePrimesTests; + productReference = DCF0C60A2326036F008B45A0 /* FavoritePrimesTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DC90716422FA102900B38B42 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "Point-Free"; + TargetAttributes = { + 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; + TestTargetID = DC90716B22FA102900B38B42; + }; + DCF0C6012326036F008B45A0 = { + CreatedOnToolsVersion = 11.0; + LastSwiftMigration = 1100; + }; + DCF0C6092326036F008B45A0 = { + CreatedOnToolsVersion = 11.0; + TestTargetID = DC90716B22FA102900B38B42; + }; + }; + }; + buildConfigurationList = DC90716722FA102900B38B42 /* Build configuration list for PBXProject "PrimeTime" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DC90716322FA102900B38B42; + productRefGroup = DC90716D22FA102900B38B42 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DC90716B22FA102900B38B42 /* PrimeTime */, + DC90718122FA102B00B38B42 /* PrimeTimeTests */, + DCF0C59A2326032B008B45A0 /* ComposableArchitecture */, + DCF0C5A22326032B008B45A0 /* ComposableArchitectureTests */, + DCF0C5BD23260348008B45A0 /* Counter */, + DCF0C5C523260348008B45A0 /* CounterTests */, + DCF0C5DF2326035C008B45A0 /* PrimeModal */, + DCF0C5E72326035C008B45A0 /* PrimeModalTests */, + DCF0C6012326036F008B45A0 /* FavoritePrimes */, + DCF0C6092326036F008B45A0 /* FavoritePrimesTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DC90716A22FA102900B38B42 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC90717C22FA102A00B38B42 /* LaunchScreen.storyboard in Resources */, + DC90717922FA102A00B38B42 /* Preview Assets.xcassets in Resources */, + DC90717622FA102A00B38B42 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DC90718022FA102B00B38B42 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5992326032B008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5A12326032B008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5BC23260348008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5C423260348008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DE2326035C008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5E62326035C008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6002326036F008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6082326036F008B45A0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 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 = ( + CAE7DF61234E7E4A00A6076A /* Effects.swift in Sources */, + DCF0C628232603B7008B45A0 /* ComposableArchitecture.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C59F2326032B008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5AB2326032B008B45A0 /* ComposableArchitectureTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5BA23260348008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C62E23260414008B45A0 /* Counter.swift in Sources */, + CAD67E2023329887000C7787 /* WolframAlpha.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5C223260348008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5CE23260348008B45A0 /* CounterTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5DC2326035C008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C62C2326040D008B45A0 /* PrimeModal.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5E42326035C008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C5F02326035C008B45A0 /* PrimeModalTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C5FE2326036F008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C62A23260405008B45A0 /* FavoritePrimes.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCF0C6062326036F008B45A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCF0C6122326036F008B45A0 /* FavoritePrimesTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + CAD67E1D2332982E000C7787 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5DF2326035C008B45A0 /* PrimeModal */; + targetProxy = CAD67E1C2332982E000C7787 /* PBXContainerItemProxy */; + }; + DC90718422FA102B00B38B42 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DC90718322FA102B00B38B42 /* PBXContainerItemProxy */; + }; + DCF0C5A62326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C5A52326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5A82326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C5A72326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5AF2326032B008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C5AE2326032B008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5C923260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5BD23260348008B45A0 /* Counter */; + targetProxy = DCF0C5C823260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5CB23260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C5CA23260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5D223260348008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5BD23260348008B45A0 /* Counter */; + targetProxy = DCF0C5D123260348008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5EB2326035C008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C5DF2326035C008B45A0 /* PrimeModal */; + targetProxy = DCF0C5EA2326035C008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C5ED2326035C008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C5EC2326035C008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C60D2326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C6012326036F008B45A0 /* FavoritePrimes */; + targetProxy = DCF0C60C2326036F008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C60F2326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC90716B22FA102900B38B42 /* PrimeTime */; + targetProxy = DCF0C60E2326036F008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C6162326036F008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C6012326036F008B45A0 /* FavoritePrimes */; + targetProxy = DCF0C6152326036F008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C62023260383008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C61F23260383008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C62223260387008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C62123260387008B45A0 /* PBXContainerItemProxy */; + }; + DCF0C6242326038A008B45A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DCF0C59A2326032B008B45A0 /* ComposableArchitecture */; + targetProxy = DCF0C6232326038A008B45A0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + DC90717A22FA102A00B38B42 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DC90717B22FA102A00B38B42 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + DC90718922FA102B00B38B42 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DC90718A22FA102B00B38B42 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + DC90718C22FA102B00B38B42 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "PrimeTime/Preview\\ Content"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = PrimeTime/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTime; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + DC90718D22FA102B00B38B42 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "PrimeTime/Preview\\ Content"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = PrimeTime/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTime; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + DC90718F22FA102B00B38B42 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeTimeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTimeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DC90719022FA102B00B38B42 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeTimeTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeTimeTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C5B32326032B008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = ComposableArchitecture/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitecture; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C5B42326032B008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = ComposableArchitecture/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitecture; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C5B52326032B008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ComposableArchitectureTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitectureTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DCF0C5B62326032B008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ComposableArchitectureTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.ComposableArchitectureTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C5D623260348008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Counter/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.Counter; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C5D723260348008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Counter/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.Counter; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C5D923260348008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CounterTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.CounterTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DCF0C5DA23260348008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CounterTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.CounterTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C5F82326035C008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = PrimeModal/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModal; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C5F92326035C008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = PrimeModal/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModal; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C5FB2326035C008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeModalTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModalTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DCF0C5FC2326035C008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = PrimeModalTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.PrimeModalTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; + DCF0C61A2326036F008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FavoritePrimes/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimes; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DCF0C61B2326036F008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = FavoritePrimes/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimes; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DCF0C61D2326036F008B45A0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = FavoritePrimesTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Debug; + }; + DCF0C61E2326036F008B45A0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = FavoritePrimesTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.pointfree.FavoritePrimesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PrimeTime.app/PrimeTime"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 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; + }; +/* End XCConfigurationList section */ + }; + rootObject = DC90716422FA102900B38B42 /* Project object */; +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..d6a81a64 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/AppDelegate.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/AppDelegate.swift new file mode 100644 index 00000000..c9606eeb --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/AppDelegate.swift @@ -0,0 +1,17 @@ +import UIKit + +@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/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/Contents.json b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/ContentView.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/ContentView.swift new file mode 100644 index 00000000..54630424 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/ContentView.swift @@ -0,0 +1,138 @@ +import Combine +import ComposableArchitecture +import Counter +import FavoritePrimes +import SwiftUI + +struct AppState { + var count = 0 + var favoritePrimes: [Int] = [] + var loggedInUser: User? = nil + var activityFeed: [Activity] = [] + var alertNthPrime: PrimeAlert? = nil + var isNthPrimeButtonDisabled: Bool = false + + struct Activity { + let timestamp: Date + let type: ActivityType + + enum ActivityType { + case addedFavoritePrime(Int) + case removedFavoritePrime(Int) + } + } + + struct User { + let id: Int + let name: String + let bio: String + } +} + +enum AppAction { + case counterView(CounterViewAction) + case favoritePrimes(FavoritePrimesAction) + + var counterView: CounterViewAction? { + get { + guard case let .counterView(value) = self else { return nil } + return value + } + set { + guard case .counterView = self, let newValue = newValue else { return } + self = .counterView(newValue) + } + } + + var favoritePrimes: FavoritePrimesAction? { + get { + guard case let .favoritePrimes(value) = self else { return nil } + return value + } + set { + guard case .favoritePrimes = self, let newValue = newValue else { return } + self = .favoritePrimes(newValue) + } + } +} + +extension AppState { + var counterView: CounterViewState { + get { + CounterViewState( + alertNthPrime: self.alertNthPrime, + count: self.count, + favoritePrimes: self.favoritePrimes, + isNthPrimeButtonDisabled: self.isNthPrimeButtonDisabled + ) + } + set { + self.alertNthPrime = newValue.alertNthPrime + self.count = newValue.count + self.favoritePrimes = newValue.favoritePrimes + self.isNthPrimeButtonDisabled = newValue.isNthPrimeButtonDisabled + } + } +} + +let appReducer = combine( + pullback(counterViewReducer, value: \AppState.counterView, action: \AppAction.counterView), + pullback(favoritePrimesReducer, value: \.favoritePrimes, action: \.favoritePrimes) +) + +func activityFeed( + _ reducer: @escaping Reducer +) -> Reducer { + + return { state, action in + switch action { + case .counterView(.counter), + .favoritePrimes(.loadedFavoritePrimes), + .favoritePrimes(.loadButtonTapped), + .favoritePrimes(.saveButtonTapped): + break + case .counterView(.primeModal(.removeFavoritePrimeTapped)): + state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.count))) + + case .counterView(.primeModal(.saveFavoritePrimeTapped)): + state.activityFeed.append(.init(timestamp: Date(), type: .addedFavoritePrime(state.count))) + + case let .favoritePrimes(.deleteFavoritePrimes(indexSet)): + for index in indexSet { + state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(state.favoritePrimes[index]))) + } + } + + return reducer(&state, action) + } +} + +struct ContentView: View { + @ObservedObject var store: Store + + var body: some View { + NavigationView { + List { + NavigationLink( + "Counter demo", + destination: CounterView( + store: self.store.view( + value: { $0.counterView }, + action: { .counterView($0) } + ) + ) + ) + NavigationLink( + "Favorite primes", + destination: FavoritePrimesView( + store: self.store.view( + value: { $0.favoritePrimes }, + action: { .favoritePrimes($0) } + ) + ) + ) + } + .navigationBarTitle("State management") + } + } +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Info.plist new file mode 100644 index 00000000..9742bf0f --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/SceneDelegate.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/SceneDelegate.swift new file mode 100644 index 00000000..a32410f9 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/SceneDelegate.swift @@ -0,0 +1,29 @@ +import ComposableArchitecture +import SwiftUI +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController( + rootView: ContentView( + store: Store( + initialValue: AppState(), + reducer: with( + appReducer, + compose( + logging, + activityFeed + ) + ) + ) + ) + ) + self.window = window + window.makeKeyAndVisible() + } + } +} diff --git a/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Util.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTime/Util.swift new file mode 100644 index 00000000..b9509ff8 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/Info.plist b/0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/0079-effectful-state-management-wtp/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/0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift b/0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift new file mode 100644 index 00000000..08d240f1 --- /dev/null +++ b/0079-effectful-state-management-wtp/PrimeTime/PrimeTimeTests/PrimeTimeTests.swift @@ -0,0 +1,34 @@ +// +// PrimeTimeTests.swift +// PrimeTimeTests +// +// Created by Stephen Celis on 8/6/19. +// Copyright © 2019 Point-Free. All rights reserved. +// + +import XCTest +@testable import PrimeTime + +class PrimeTimeTests: XCTestCase { + + override func setUp() { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/0079-effectful-state-management-wtp/README.md b/0079-effectful-state-management-wtp/README.md new file mode 100644 index 00000000..d99c8b68 --- /dev/null +++ b/0079-effectful-state-management-wtp/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Effectful State Management: What’s the Point?](https://www.pointfree.co/episodes/ep79-effectful-state-management-unidirectional-effects) +> +> We've got the basic story of side effects in our architecture, but the story is far from over. Turns out that even side effects themselves are composable. Base effect functionality can be extracted and shared, and complex effects can be broken down into simpler pieces. diff --git a/README.md b/README.md index a271543a..6dea1848 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,4 @@ This repository is the home of code written on episodes of 1. [Effectful State Management: Synchronous Effects](0076-effectful-state-management-synchronous-effects) 1. [Effectful State Management: Unidirectional Effects](0077-effectful-state-management-unidirectional-effects) 1. [Effectful State Management: Asynchronous Effects](0078-effectful-state-management-async-effects) +1. [Effectful State Management: The Point](0079-effectful-state-management-wtp)