Skip to content

Commit

Permalink
Prevent case navigation binding from writing to other cases (#149)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip
  • Loading branch information
stephencelis authored May 9, 2024
1 parent 2ec6c3a commit 5f77d0a
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 8 deletions.
16 changes: 8 additions & 8 deletions Sources/SwiftUINavigation/Binding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@
fileprivate subscript<Member>(
keyPath: KeyPath<Self.AllCasePaths, AnyCasePath<Self, Member>>
) -> Member? {
get { Self.allCasePaths[keyPath: keyPath].extract(from: self) }
get {
Self.allCasePaths[keyPath: keyPath].extract(from: self)
}
set {
guard let newValue else { return }
self = Self.allCasePaths[keyPath: keyPath].embed(newValue)
Expand All @@ -144,15 +146,13 @@
keyPath: KeyPath<Wrapped.AllCasePaths, AnyCasePath<Wrapped, Member>>
) -> Member? {
get {
guard let wrapped = self else { return nil }
return Wrapped.allCasePaths[keyPath: keyPath].extract(from: wrapped)
self.flatMap(Wrapped.allCasePaths[keyPath: keyPath].extract(from:))
}
set {
guard let newValue else {
self = nil
return
}
self = Wrapped.allCasePaths[keyPath: keyPath].embed(newValue)
let casePath = Wrapped.allCasePaths[keyPath: keyPath]
guard self.flatMap(casePath.extract(from:)) != nil
else { return }
self = newValue.map(casePath.embed)
}
}
}
Expand Down
59 changes: 59 additions & 0 deletions Tests/SwiftUINavigationTests/BindingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#if swift(>=5.9) && canImport(SwiftUI)
import CustomDump
import SwiftUI
import SwiftUINavigation
import XCTest

final class BindingTests: XCTestCase {
@CasePathable
@dynamicMemberLookup
enum Status: Equatable {
case inStock(quantity: Int)
case outOfStock(isOnBackOrder: Bool)
}

func testCaseLookup() throws {
@Binding var status: Status
_status = Binding(initialValue: .inStock(quantity: 1))

let inStock = try XCTUnwrap($status.inStock)
inStock.wrappedValue += 1

XCTAssertEqual(status, .inStock(quantity: 2))
}

func testCaseCannotReplaceOtherCase() throws {
@Binding var status: Status
_status = Binding(initialValue: .inStock(quantity: 1))

let inStock = try XCTUnwrap($status.inStock)

status = .outOfStock(isOnBackOrder: true)

inStock.wrappedValue = 42
XCTAssertEqual(status, .outOfStock(isOnBackOrder: true))
}

func testDestinationCannotReplaceOtherDestination() throws {
@Binding var destination: Status?
_destination = Binding(initialValue: .inStock(quantity: 1))

let inStock = try XCTUnwrap($destination.inStock)

destination = .outOfStock(isOnBackOrder: true)

inStock.wrappedValue = 42
XCTAssertEqual(destination, .outOfStock(isOnBackOrder: true))
}
}

private extension Binding {
init(initialValue: Value) {
var value = initialValue
self.init(
get: { value },
set: { value = $0 }
)
}
}
#endif // canImport(SwiftUI)

0 comments on commit 5f77d0a

Please sign in to comment.