Skip to content

Commit

Permalink
Adds binding utilities for SwiftUI2
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Usbergo committed Feb 23, 2021
1 parent a33b6ff commit f865af2
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 267 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/apple/swift-log.git",
"state": {
"branch": null,
"revision": "173f567a2dfec11d74588eea82cecea555bdc0bc",
"version": "1.4.0"
"revision": "12d3a8651d32295794a850307f77407f95b8c881",
"version": "1.4.1"
}
}
]
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PackageDescription
let package = Package(
name: "Store",
platforms: [
.iOS(.v12),
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13),
.watchOS(.v6)
Expand Down
285 changes: 41 additions & 244 deletions Sources/Store/action/Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,234 +62,60 @@ extension Action {
}
}

// MARK: - Template Actions

/// Utility actions that are applicable to any store.
public struct TemplateAction {

// MARK: - Reduce

/// Reduce the model by using the closure passed as argument.
public struct Reduce<M>: Action {
public let id: String
public let reduce: (inout M) -> Void

public init(id: String = _ID.reduce, reduce: @escaping (inout M) -> Void) {
self.id = id
self.reduce = reduce
}

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}
context.reduceModel(closure: reduce)
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
/// Reduce the model by using the closure passed as argument.
public struct Reduce<M>: Action {
public let id: String
public let reduce: (inout M) -> Void

public init(id: String = _ID.reduce, reduce: @escaping (inout M) -> Void) {
self.id = id
self.reduce = reduce
}

// MARK: - Assign

/// Assigns the value passed as argument to the model's keyPath.
public struct Assign<M, V>: Action {
public let id: String
public let keyPath: KeyPathArg<M, V>
public let value: V?

public init(id: String = _ID.assign, _ keyPath: KeyPathArg<M, V>, _ value: V) {
self.id = id
self.keyPath = keyPath
self.value = value
}

public init(id: String = _ID.assign, _ keyPath: WritableKeyPath<M, V>, _ value: V) {
self.id = id
self.keyPath = .value(keyPath: keyPath)
self.value = value
public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}

public init(id: String = _ID.assign,_ keyPath: WritableKeyPath<M, V?>, _ value: V?) {
self.id = id
self.keyPath = .optional(keyPath: keyPath)
self.value = value
}

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}
context.reduceModel { model in
_assignKeyPath(object: &model, keyPath: keyPath, value: value)
}
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
context.reduceModel(closure: reduce)
}

// MARK: - Filter

// Filter the array the keyPath points at by using the given predicate.
public struct Filter<M, V: Collection>: Action {
public let id: String
public let keyPath: KeyPathArg<M, V>
public let isIncluded: (V.Element) -> Bool
public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
}

public init(
id: String = _ID.filter,
_ keyPath: KeyPathArg<M, V>,
_ isIncluded: @escaping (V.Element) -> Bool
) {
self.id = id
self.keyPath = keyPath
self.isIncluded = isIncluded
}

public init(
id: String = _ID.filter,
_ keyPath: WritableKeyPath<M, V>,
_ isIncluded: @escaping (V.Element) -> Bool
) {
self.id = id
self.keyPath = .value(keyPath: keyPath)
self.isIncluded = isIncluded
}

public init(
id: String = _ID.filter,
_ keyPath: WritableKeyPath<M, V?>,
_ isIncluded: @escaping (V.Element) -> Bool
) {
self.id = id
self.keyPath = .optional(keyPath: keyPath)
self.isIncluded = isIncluded
}

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { $0 = $0.filter(isIncluded) }
}
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
}
/// Assigns the value passed as argument to the model's keyPath.
public struct Assign<M, V>: Action {
public let id: String
public let keyPath: KeyPathArg<M, V>
public let value: V?

// MARK: - Remove

// Remove an element from the array the keyPath points at.
public struct Remove<M, V: Collection>: Action {
public let id: String
public let keyPath: KeyPathArg<M, V>
public let index: Int

public init(id: String = _ID.remove, _ keyPath: KeyPathArg<M, V>, index: Int) {
self.id = id
self.keyPath = keyPath
self.index = index
}

public init(id: String = _ID.remove, _ keyPath: WritableKeyPath<M, V>, index: Int) {
self.id = id
self.keyPath = .value(keyPath: keyPath)
self.index = index
}

public init(id: String = _ID.remove, _ keyPath: WritableKeyPath<M, V?>, index: Int) {
self.id = id
self.keyPath = .optional(keyPath: keyPath)
self.index = index
}

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { $0.remove(at: index) }
}
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
public init(id: String = _ID.assign, _ keyPath: KeyPathArg<M, V>, _ value: V) {
self.id = id
self.keyPath = keyPath
self.value = value
}

// MARK: - Append

// Append an element in the array the keyPath points at.
public struct Append<M, V: Collection>: Action {
public let id: String
public let keyPath: KeyPathArg<M, V>
public let object: V.Element

public init(id: String = _ID.append, _ keyPath: KeyPathArg<M, V>, object: V.Element) {
self.id = id
self.keyPath = keyPath
self.object = object
}

public init(id: String = _ID.append, _ keyPath: WritableKeyPath<M, V>, object: V.Element) {
self.id = id
self.keyPath = .value(keyPath: keyPath)
self.object = object
}

public init(id: String = _ID.append, _ keyPath: WritableKeyPath<M, V?>, object: V.Element) {
self.id = id
self.keyPath = .optional(keyPath: keyPath)
self.object = object
}

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { $0.append(object) }
}
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
public init(id: String = _ID.assign, _ keyPath: WritableKeyPath<M, V>, _ value: V) {
self.id = "\(id)[\(keyPath.readableFormat ?? "unknown")]"
self.keyPath = .value(keyPath: keyPath)
self.value = value
}

// MARK: - Push
public init(id: String = _ID.assign,_ keyPath: WritableKeyPath<M, V?>, _ value: V?) {
self.id = "\(id)[\(keyPath.readableFormat ?? "unknown")]"
self.keyPath = .optional(keyPath: keyPath)
self.value = value
}

// Push an element at index 0 in the array the keyPath points at.
public struct Push<M, V: Collection>: Action {
public let id: String
public let keyPath: KeyPathArg<M, V>
public let object: V.Element

public init(id: String = _ID.push, _ keyPath: KeyPathArg<M, V>, object: V.Element) {
self.id = id
self.keyPath = keyPath
self.object = object
public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}

public init(id: String = _ID.push, _ keyPath: WritableKeyPath<M, V>, object: V.Element) {
self.id = id
self.keyPath = .value(keyPath: keyPath)
self.object = object
}

public init(id: String = _ID.push, _ keyPath: WritableKeyPath<M, V?>, object: V.Element) {
self.id = id
self.keyPath = .optional(keyPath: keyPath)
self.object = object
}

public func reduce(context: TransactionContext<Store<M>, Self>) {
defer {
context.fulfill()
}
context.reduceModel { model in
_mutateArray(object: &model, keyPath: keyPath) { $0.insert(object, at: 0) }
}
context.reduceModel { model in
_assignKeyPath(object: &model, keyPath: keyPath, value: value)
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
}

public func cancel(context: TransactionContext<Store<M>, Self<M, V>>) { }
}

// MARK: - Internal
Expand All @@ -302,31 +128,6 @@ public enum KeyPathArg<M, V> {
}

// MARK: - Private

private func _mutateArray<M, V: Collection>(
object: inout M,
keyPath: KeyPathArg<M, V>,
mutate: (inout [V.Element]) -> Void
) {
var value: V
switch keyPath {
case .value(let keyPath): value = object[keyPath: keyPath]
case .optional(let keyPath):
guard let unwrapped = object[keyPath: keyPath] else { return }
value = unwrapped
}
guard var array = value as? [V.Element] else {
logger.error("Arrays are the only collection type supported.")
return
}
mutate(&array)
switch keyPath {
case .value(let keyPath):
object[keyPath: keyPath] = array as! V
case .optional(let keyPath):
object[keyPath: keyPath] = array as? V
}
}

private func _assignKeyPath<M, V>(object: inout M, keyPath: KeyPathArg<M, V>, value: V?) {
switch keyPath {
Expand All @@ -341,10 +142,6 @@ private func _assignKeyPath<M, V>(object: inout M, keyPath: KeyPathArg<M, V>, va
// MARK: - IDs

public struct _ID {
public static let reduce = "_TEMPLATE_REDUCE"
public static let assign = "_TEMPLATE_ASSIGN"
public static let filter = "_TEMPLATE_FILTER"
public static let remove = "_TEMPLATE_REMOVE"
public static let append = "_TEMPLATE_APPEND"
public static let push = "_TEMPLATE_PUSH"
public static let reduce = "_REDUCE"
public static let assign = "_BINDING_ASSIGN"
}
Loading

0 comments on commit f865af2

Please sign in to comment.