diff --git a/Package.swift b/Package.swift index f81e1388e2..ff10a97ad2 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), - .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.1.0"), + .package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.2.2"), .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.0.0"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.0.0"), ], diff --git a/Sources/SwiftUINavigation/Binding.swift b/Sources/SwiftUINavigation/Binding.swift index edfce57644..9d65e4f25a 100644 --- a/Sources/SwiftUINavigation/Binding.swift +++ b/Sources/SwiftUINavigation/Binding.swift @@ -14,16 +14,7 @@ dynamicMember keyPath: KeyPath> ) -> Binding? where Value: CasePathable { - let casePath = Value.allCasePaths[keyPath: keyPath] - return Binding( - unwrapping: Binding( - get: { casePath.extract(from: self.wrappedValue) }, - set: { newValue, transaction in - guard let newValue else { return } - self.transaction(transaction).wrappedValue = casePath.embed(newValue) - } - ) - ) + Binding(unwrapping: self[keyPath]) } /// Returns a binding to the associated value of a given case key path. @@ -36,20 +27,7 @@ dynamicMember keyPath: KeyPath> ) -> Binding where Value == Enum? { - let casePath = Enum.allCasePaths[keyPath: keyPath] - return Binding( - get: { - guard let wrappedValue = self.wrappedValue else { return nil } - return casePath.extract(from: wrappedValue) - }, - set: { newValue, transaction in - guard let newValue else { - self.transaction(transaction).wrappedValue = nil - return - } - self.transaction(transaction).wrappedValue = casePath.embed(newValue) - } - ) + self[keyPath] } #endif @@ -68,7 +46,8 @@ /// - Parameter base: A value to project to an unwrapped value. /// - Returns: A new binding or `nil` when `base` is `nil`. public init?(unwrapping base: Binding) { - self.init(unwrapping: base, case: AnyCasePath(\.some)) + guard let value = base.wrappedValue else { return nil } + self = base[default: DefaultSubscript(value)] } /// Creates a binding by projecting the current optional value to a boolean describing if it's @@ -79,14 +58,7 @@ /// - Returns: A binding to a boolean. Returns `true` if non-`nil`, otherwise `false`. public func isPresent() -> Binding where Value == Wrapped? { - .init( - get: { self.wrappedValue != nil }, - set: { isPresent, transaction in - if !isPresent { - self.transaction(transaction).wrappedValue = nil - } - } - ) + self._isPresent } /// Creates a binding that ignores writes to its wrapped value when equivalent to the new value. @@ -139,4 +111,68 @@ ) } } + + extension Optional { + fileprivate var _isPresent: Bool { + get { self != nil } + set { + guard !newValue else { return } + self = nil + } + } + + fileprivate subscript(default defaultSubscript: DefaultSubscript) -> Wrapped { + get { + defaultSubscript.value = self ?? defaultSubscript.value + return defaultSubscript.value + } + set { + defaultSubscript.value = newValue + if self != nil { self = newValue } + } + } + } + + private final class DefaultSubscript: Hashable { + var value: Value + init(_ value: Value) { + self.value = value + } + static func == (lhs: DefaultSubscript, rhs: DefaultSubscript) -> Bool { + lhs === rhs + } + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + } + + extension CasePathable { + fileprivate subscript( + keyPath: KeyPath> + ) -> Member? { + get { Self.allCasePaths[keyPath: keyPath].extract(from: self) } + set { + guard let newValue else { return } + self = Self.allCasePaths[keyPath: keyPath].embed(newValue) + } + } + } + + extension Optional where Wrapped: CasePathable { + fileprivate subscript( + keyPath: KeyPath> + ) -> Member? { + get { + guard let wrapped = self else { return nil } + return Wrapped.allCasePaths[keyPath: keyPath].extract(from: wrapped) + } + set { + guard let newValue else { + self = nil + return + } + self = Wrapped.allCasePaths[keyPath: keyPath].embed(newValue) + } + } + } #endif // canImport(SwiftUI) diff --git a/SwiftUINavigation.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SwiftUINavigation.xcworkspace/xcshareddata/swiftpm/Package.resolved index 51a14302a3..a6a4bc1692 100644 --- a/SwiftUINavigation.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/SwiftUINavigation.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/pointfreeco/swift-case-paths", "state": { "branch": null, - "revision": "bba1111185863c9288c5f047770f421c3b7793a4", - "version": "1.1.3" + "revision": "8cc3bc05d0cc956f7374c6c208a11f66a7cac3db", + "version": "1.2.2" } }, {