Is it possible to achieve one-way data binding with SwiftUI in situations like below?
import Combine
final class Counter: ObservableObject {
@Published private(set) var count: Int = 0
func increment() { count += 1 }
func reset() { count = 0 }
}
import SwiftUI
struct ContentView: View {
@ObservedObject private var counter: Counter = .init()
var body: some View {
VStack {
NumberDisplay(number: $counter.count) // ⛔because `count` is read-only
HStack {
Button("Reset") { self.counter.reset() }
Button("Increment") { self.counter.increment() }
}
}
}
}
import SwiftUI
struct NumberDisplay: View {
@Binding var number: Int
private let superCoolFont: Font = .system(.largeTitle)
var body: some View {
Text("\(number)")
.font(superCoolFont)
}
}
It seems that SwiftUI does not provide any simple APIs to achieve one-way data binding as far as I know, though I am not familiar with SwiftUI. Could you inform me of the way if you know it?
I think, for example, some APIs like readOnly
in the following code can be helpful.
import SwiftUI
struct ContentView: View {
@ObservedObject private var counter: Counter = .init()
var body: some View {
VStack {
//NumberDisplay(number: $counter.count) // ⛔
NumberDisplay(number: $counter.readOnly.count) // ✅
HStack {
Button("Reset") { self.counter.reset() }
Button("Increment") { self.counter.increment() }
}
}
}
}
import SwiftUI
extension ObservedObject.Wrapper {
var readOnly: ReadOnly {
// FIXME: ⚠️ Replace it with a safe implementation!!
return ReadOnly(unsafeBitCast(self, to: ObjectType.self))
}
@dynamicMemberLookup
struct ReadOnly {
private let object: ObjectType
init(_ object: ObjectType) {
self.object = object
}
subscript<Subject>(dynamicMember keyPath: KeyPath<ObjectType, Subject>) -> Binding<Subject> {
Binding<Subject>(
get: { self.object[keyPath: keyPath] },
set: { _ in assertionFailure("Read-only") }
)
}
}
}