I've implemented this method, but there's one big downside as far as I can tell: while the subscript method does provide access to the enclosing self, the subscript method is static, so you don't have access to the instance of the property wrapper. This means that if you provide a custom init to your wrapper like so:
in the implementation of the static subscript you cannot access the wrapper's private instance variable str. You can imagine a lot of uses of simultaneously referencing both the enclosing self instance and the wrapper instance, like implementing your own theming system.
I'm wondering if I'm overlooking some way here of accessing the wrapper instance in this static subscript?
Yes, that's what I'm looking for, thank you! But, I'm having trouble getting the compiler to pass. This wrapper implementation alone compiles just fine:
@propertyWrapper
public final class Wrapper {
public static subscript<EnclosingSelf>(
_enclosingInstance observed: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Int?>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
) -> Int? {
get {
return observed[keyPath: storageKeyPath].stored
}
set {
let oldValue = observed[keyPath: storageKeyPath].stored
if newValue != oldValue {
// TODO: call wrapper instance with enclosing self
}
observed[keyPath: storageKeyPath].stored = newValue
}
}
public var wrappedValue: Int? {
get { fatalError("called wrappedValue getter") }
set { fatalError("called wrappedValue setter") }
}
public init(str: String) {
self.str = str
}
// MARK: - Private
private let str: String
private var stored: Int?
}
But when I add it to a class, the compiler fails for an unknown reason:
open class TestView: UIView {
@Wrapper(str: "HelloWorld") public var testProp: Int?
}
/**
Swift Compiler Error
Type '_' has no member 'testProp'
*/
Try to swap Self with Wrapper and check if it works then. If it does, file a bug at bugs.swift.org. This could be related to Self on classes not properly respecting final keyword. If it does not work then it must be something else.
Yeah, Self and TypeName should be equivalent in a final class, but it's just not supported at the moment (I opened a PR then forgot about it, so sorry about that. I'll try to find some time soon). I think there's a dup for this, so I'll update the JIRA ticket when I find it.
What's a private API...? From what I can tell looking at Swift's source for PropertyWrappers, this is supported right in the base of Swift, if not described in the docs. I mean, it seems to compile and run without even importing Foundation.
As you said it, it's undocumented implementation which also happens to be prefixed with an underscore which by Swift standards considered as "hands off" or simply private.
I don't think #2 will happen, but I'm not a compiler expert (could bitcode compilation affect this somehow?).
#1 is more likely, but I don't think it "will change", it rather gets removed and somehow reattached to the new feature in the future for backwards compatibility with existing code Apple already published.
I was worrying about #2 because I thought with Swift 5 ABI stability apps are no longer deployed bundled with a static version of the standard library. So something like this could be there in one version of iOS and disappear in the next. ??
Do you mean that there are compilation errors, or compiler literally crashes? Compiler shouldn't ever crash, so that's a bug you can file at bugs.swift.org
Yes, it literally crashes. I was making the property wrapper type a class. Once I made it a struct, it stopped crashing and worked. I will file a bug soon.
Unfortunately for my case, I also wanted to be able to turn the keypaths into a strings, to store data in dynamic things like CKRecords and SQL insert/update statements, and there is apparently no way to get a string from those ReferenceWritableKeyPaths. (Correct?) Grrr.
Why does only work when wrapping properties of a class? When I wrap properties of a struct, then the property wrapper's wrappedValue function gets called instead of static subscript<EnclosingSelf>(_enclosingInstance observed: EnclosingSelf, wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>, storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>) -> Value. I tried changing the keypath types to WritableKeyPath but it still doesn't work.
Please advise. We really need this functionality to work!