In case this is useful to someone, this is the workaround I'll be using:

```
class CustomTextStorage: NSTextStorage {

        private let backingStore: NSMutableAttributedString

        // This method should never get called from Objective-C as it doesn't 
respect 
        // the API contract because of the wrapping in `String`.
        //
        // This could get called directly from Swift code when dispatching 
without 
        // passing by the Objective-C runtime. So it must still produce the 
right 
        // thing for Swift.
        override var string: String {
                return backingStore.string
        }

        // Objective-C method implementation for `string` is remapped to this 
method to 
        // avoid wrapping the result in `String`. With the correct method 
signature 
        // we can return the backing store string object.
        var backingNSString: NSString {
                return backingStore.mutableString
        }

        // call once at program initialization:
        static func fixupStringMethod() {
                let theClass = CustomTextStorage.self
                let badStringMeth = class_getInstanceMethod(theClass, 
#selector(getter: string))
                let goodStringMeth = class_getInstanceMethod(theClass, 
#selector(getter: backingNSString))
                let goodImp = method_getImplementation(goodStringMeth)
                method_setImplementation(badStringMeth, goodImp)
        }

        // ... rest of the class goes here ...
}
```

> Le 9 févr. 2017 à 18:12, Michel Fortin via swift-users 
> <swift-users@swift.org> a écrit :
> 
> The `string` property of `NSTextStorage` is of type `String`, but the 
> contract it must implement is that it should return the backing store of the 
> attributed string (the underlying `NSMutableString` used as the backing 
> store). It seems to me that this makes it impossible to implement correctly a 
> subclass of `NSTextStorage` in Swift, because Swift automatically wraps the 
> `NSString` into a `String` and the underlying mutable storage is not passed 
> around.
> 
> Here's the documentation for that method:
> https://developer.apple.com/reference/foundation/nsattributedstring/1412616-string
> 
> In case the contract isn't clear from the documentation (it wasn't for me), I 
> got a confirmation as a response in radar 30314719:
>> It’s returning a copy of the backing store, but the contract is actually 
>> returning the backing store string. The copy storage declaration in the 
>> property spec is only used for setter implementation.
> 
> So looks like this is impossible to model correctly in Swift due to the 
> automatic bridging to `String`. Some APIs in AppKit expect the `NSString` 
> they receive to mutate when mutating the text storage (touch bar suggestions 
> in `NSTextView`) and will do bad things this isn't the case.
> 
> Obviously, I can work around this by writing some Objective-C code, but it'd 
> be better if I could avoid splitting the class implementation between two 
> languages. There's another way using swizzling to map the Objective-C method 
> to a Swift implementation of the same method that has the correct signature, 
> and that's probably what I'll end up doing unless a better solution can be 
> pointed out to me.


-- 
Michel Fortin
https://michelf.ca

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to