Rather than pollute the review thread, I thought I'd post my experiences with the latest toolchain for this feature here.
I'm attempting to create an observable type using an existing pattern I use (observable around NotificationCenter).
- It seems that delegated values aren't compatible with automatic
nil initialization. Is that intentional or just a limitation of the current implementation?
- Initializers with additional, even defaulted, parameters can't meet the
init(initialValue:) requirement. Again, is this intentional or just a limitation of the current implementation?
- There doesn't seem to be any way to reference a delegated property in a protocol. I know you can't use the delegate syntax itself in the protocol, but not being able to reference it at all makes thinks like generic observable network APIs impossible, at least in a way that guarantees the properties being updated are actually observable. Any alternatives here?
Code I'm using
```swift
@propertyDelegate
final class DelegateObservable {
typealias ObservationClosure = (_ value: T) -> Void
var value: Value {
didSet {
center.postNotification(named: name, with: value)
}
}
let name: Notification.Name = Notification.Name(rawValue: UUID().uuidString)
let center: NotificationCenter = .default
init(initialValue: Value) {
value = initialValue
}
// init(initialValue: Value, name: String = UUID().uuidString, center: NotificationCenter = .default) {
// value = initialValue
// self.name = Notification.Name(rawValue: name)
// self.center = center
// }
func observe(returningCurrentValue: Bool = true,
queue: OperationQueue = .main,
handler: @escaping ObservationClosure<Value>) -> NotificationToken {
if returningCurrentValue {
handler(value)
}
return center.observe(notificationNamed: name, queue: queue) { handler($0.payload()) }
}
}
final class SomeModelController {
static let shared = SomeModelController()
@DelegateObservable private(set) var model: Int = 0
let timer: DispatchSourceTimer
init() {
timer = DispatchSource.makeTimerSource()
timer.schedule(deadline: .now() + 1, repeating: 1)
timer.setEventHandler {
print(self.model)
self.model += 1
}
timer.resume()
}
}
extension NotificationCenter {
/// Convenience wrapper for addObserver(forName:object:queue:using:) that returns a NotificationToken.
public func observe(notificationNamed name: NSNotification.Name?,
object: Any? = nil,
queue: OperationQueue? = .main,
using block: @escaping (Notification) -> Void) -> NotificationToken {
let token = addObserver(forName: name, object: object, queue: queue, using: block)
return NotificationToken(notificationCenter: self, token: token)
}
/// Convenience function to post a notification with a generic payload.
public func postNotification<Payload>(named name: Notification.Name, with payload: Payload) {
let notification = Notification(name: name, payload: payload)
post(notification)
}
}
public extension Notification {
init(name: Notification.Name, payload: Payload) {
self.init(name: name, object: nil, userInfo: [String.payload: payload])
}
func payload<Payload>() -> Payload {
guard let payload = userInfo?[String.payload] as? Payload else {
fatalError("Unexpected payload type: \(Payload.self)")
}
return payload
}
func transform<T, U>(_ closure: (_ payload: T) -> U) -> U {
return closure(payload())
}
}
/// Wraps the observer token received from NotificationCenter.addObserver(forName:object:queue:using:)
/// and unregisters it in deinit.
public final class NotificationToken: NSObject {
let notificationCenter: NotificationCenter
let token: Any
init(notificationCenter: NotificationCenter = .default, token: Any) {
self.notificationCenter = notificationCenter
self.token = token
}
deinit {
notificationCenter.removeObserver(token)
}
}
private extension String {
static let payload = "payload"
}
</details>