Skip to content

Commit

Permalink
Basics
Browse files Browse the repository at this point in the history
  • Loading branch information
stephencelis committed Jul 31, 2019
1 parent ed55f1b commit 34fa5d5
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
163 changes: 163 additions & 0 deletions 0068/ComposableArchitecture.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import Combine
import SwiftUI

class AppState: ObservableObject {
@Published var count = 0
@Published var favoritePrimes: [Int] = []
@Published var loggedInUser: User? = nil
@Published var activityFeed: [Activity] = []

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

struct FavoritePrimesState {
var favoritePrimes: [Int]
var activityFeed: [AppState.Activity]
}

extension AppState {
var favoritePrimesState: FavoritePrimesState {
get {
FavoritePrimesState(
favoritePrimes: self.favoritePrimes,
activityFeed: self.activityFeed
)
}
set {
self.favoritePrimes = newValue.favoritePrimes
self.activityFeed = newValue.activityFeed
}
}
}

struct PrimeAlert: Identifiable {
let prime: Int
var id: Int { self.prime }
}

struct CounterView: View {
@ObservedObject var state: AppState
@State var isPrimeModalShown = false
@State var alertNthPrime: PrimeAlert?
@State var isNthPrimeButtonDisabled = false

var body: some View {
VStack {
HStack {
Button("-") { self.state.count -= 1 }
Text("\(self.state.count)")
Button("+") { self.state.count += 1 }
}
Button("Is this prime?") { self.isPrimeModalShown = true }
Button(
"What is the \(ordinal(self.state.count)) prime?",
action: self.nthPrimeButtonAction
)
.disabled(self.isNthPrimeButtonDisabled)
}
.font(.title)
.navigationBarTitle("Counter demo")
.sheet(isPresented: self.$isPrimeModalShown) {
IsPrimeModalView(state: self.state)
}
.alert(item: self.$alertNthPrime) { alert in
Alert(
title: Text("The \(ordinal(self.state.count)) prime is \(alert.prime)"),
dismissButton: .default(Text("Ok"))
)
}
}

func nthPrimeButtonAction() {
self.isNthPrimeButtonDisabled = true
nthPrime(self.state.count) { prime in
self.alertNthPrime = prime.map(PrimeAlert.init(prime:))
self.isNthPrimeButtonDisabled = false
}
}
}

struct IsPrimeModalView: View {
@ObservedObject var state: AppState

var body: some View {
VStack {
if isPrime(self.state.count) {
Text("\(self.state.count) is prime 🎉")
if self.state.favoritePrimes.contains(self.state.count) {
Button("Remove from favorite primes") {
self.state.favoritePrimes.removeAll(where: { $0 == self.state.count })
self.state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(self.state.count)))
}
} else {
Button("Save to favorite primes") {
self.state.favoritePrimes.append(self.state.count)
self.state.activityFeed.append(.init(timestamp: Date(), type: .addedFavoritePrime(self.state.count)))

}
}
} else {
Text("\(self.state.count) is not prime :(")
}
}
}
}

struct FavoritePrimesView: View {
@Binding var state: FavoritePrimesState

var body: some View {
List {
ForEach(self.state.favoritePrimes, id: \.self) { prime in
Text("\(prime)")
}
.onDelete { indexSet in
for index in indexSet {
let prime = self.state.favoritePrimes[index]
self.state.favoritePrimes.remove(at: index)
self.state.activityFeed.append(.init(timestamp: Date(), type: .removedFavoritePrime(prime)))
}
}
}
.navigationBarTitle(Text("Favorite Primes"))
}
}

struct ContentView: View {
@ObservedObject var state: AppState

var body: some View {
NavigationView {
List {
NavigationLink(
"Counter demo",
destination: CounterView(state: self.state)
)
NavigationLink(
"Favorite primes",
destination: FavoritePrimesView(state: self.$state.favoritePrimesState)
)
}
.navigationBarTitle("State management")
}
}
}

import PlaygroundSupport
PlaygroundPage.current.liveView = UIHostingController(
rootView: ContentView(state: AppState())
)
70 changes: 70 additions & 0 deletions 0068/ComposableArchitecture.playground/Sources/Util.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Foundation

private let wolframAlphaApiKey = "6H69Q3-828TKQJ4EP"

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

private func wolframAlpha(query: String, callback: @escaping (WolframAlphaResult?) -> Void) -> Void {
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),
]

URLSession.shared.dataTask(with: components.url(relativeTo: nil)!) { data, response, error in
callback(
data
.flatMap { try? JSONDecoder().decode(WolframAlphaResult.self, from: $0) }
)
}
.resume()
}

public func nthPrime(_ n: Int, callback: @escaping (Int?) -> Void) -> Void {
wolframAlpha(query: "prime \(n)") { result in
callback(
result
.flatMap {
$0.queryresult
.pods
.first(where: { $0.primary == .some(true) })?
.subpods
.first?
.plaintext
}
.flatMap(Int.init)
)
}
}

public func ordinal(_ n: Int) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .ordinal
return formatter.string(for: n) ?? ""
}

public 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
}
4 changes: 4 additions & 0 deletions 0068/ComposableArchitecture.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios'>
<timeline fileName='timeline.xctimeline'/>
</playground>

0 comments on commit 34fa5d5

Please sign in to comment.