From bb6249ac87d5366c5a747e7fc53aa844e187806c Mon Sep 17 00:00:00 2001 From: Bill Dunay Date: Wed, 6 Dec 2023 10:11:15 -0500 Subject: [PATCH] Mutatingcopyable keypath (#41) * Added a method to MutatingCopyable to use a KeyPath instead of a closure * Added mutating copyable unit tests * grammar --------- Co-authored-by: Bill Dunay Co-authored-by: Albert Bori --- Sources/VSM/MutatingCopyable.swift | 28 +++++++++++++---- Tests/VSMTests/MutatingCopyableTests.swift | 35 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 Tests/VSMTests/MutatingCopyableTests.swift diff --git a/Sources/VSM/MutatingCopyable.swift b/Sources/VSM/MutatingCopyable.swift index 0492a12..63422a5 100644 --- a/Sources/VSM/MutatingCopyable.swift +++ b/Sources/VSM/MutatingCopyable.swift @@ -1,6 +1,6 @@ import Foundation -/// Extend a value type with the ability to copy and mutate in a single line of code +/// Extend a value type with the ability to copy and mutate in a single line of code. /// /// Example Usage /// ```swift @@ -11,19 +11,37 @@ import Foundation /// // Save username /// return self.copy(mutating: { $0.username = username }) /// } +/// +/// // OR +/// +/// func update(username newValue: String) -> Self { +/// // Save username +/// return self.copy(mutating: \.username, value: newValue) +/// } /// } /// ``` public protocol MutatingCopyable { } public extension MutatingCopyable { - /// Creates a mutated copy of this type - /// - /// - Parameter mutator: The function that mutates the copy of this type - /// - Returns: A mutated copy of this type + /// Creates a mutated copy of this type. + /// + /// - Parameter mutator: The function that mutates the copy of this type. + /// - Returns: A mutated copy of this type. func copy(mutating mutator: (inout Self) -> Void) -> Self { var copy = self mutator(©) return copy } + + /// Creates a mutated copy of this type while simultaneously mutating the value at the provided KeyPath. + /// + /// - Parameter keyPath: The KeyPath of the property you want to mutate. Please not that this property MUST be a `var` in order to change its value using this method. + /// - Parameter value: The new value you want set on the property. + /// - Returns: A mutated copy of this type. + func copy(mutatingPath keyPath: WritableKeyPath, value: T) -> Self { + var copy = self + copy[keyPath: keyPath] = value + return copy + } } diff --git a/Tests/VSMTests/MutatingCopyableTests.swift b/Tests/VSMTests/MutatingCopyableTests.swift new file mode 100644 index 0000000..3adcfd3 --- /dev/null +++ b/Tests/VSMTests/MutatingCopyableTests.swift @@ -0,0 +1,35 @@ +// +// MutatingCopyableTests.swift +// +// +// Created by Albert Bori on 12/6/23. +// + +@testable import VSM +import XCTest + +final class MutatingCopyableTests: XCTestCase { + struct Subject: MutatingCopyable { + var bar: String + var baz: Bool + } + + func testMutatingCopyableClosure() { + let subject = Subject(bar: "old", baz: false) + let result = subject.copy { + $0.bar = "new" + $0.baz = true + } + XCTAssertEqual(result.bar, "new") + XCTAssertEqual(result.baz, true) + } + + func testMutatingCopyableKeyPath() { + let subject = Subject(bar: "old", baz: false) + let result = subject + .copy(mutatingPath: \.bar, value: "new") + .copy(mutatingPath: \.baz, value: true) + XCTAssertEqual(result.bar, "new") + XCTAssertEqual(result.baz, true) + } +}