Re: [swift-users] So how do you implement a NSTextStorage subclass in Swift?
Le 12 févr. 2017 à 18:41, Karl Wagnera écrit : > > I've seen this before; I considered it something to be resolved in AppKit. > Strings in Swift are values, and the framework expects it to be a reference. > Since NSTextStorage is itself a reference-type and informs its delegate of > changes, those frameworks should probably retain the NSTS itself and pull > Strings out as-needed. Well, this is apparently not an AppKit problem because the `string` method is first defined in `NSAttributedString` in Foundation. `NSTextStorage` only inherits that method. The cleanest fix I can see at the framework level is expose it as two properties in `NSAttributedString`: // swift-only getter for the string, can't override this one @nonobjc final public var string: String { get { return backingString as String } } // this is the one mapped to `string` in objc, you can override this one @objc(string) public var backingString: NSString { get } This is somewhat analogous to what I'm doing with `method_setImplementation` in my solution to remap the method to a Swift method that has the right signature. That would be source-breaking, but only for those who override the property. Pretty much all of these overrides are going to be violating the API contract anyway. They probably deserve the error so they can be fixed. At this point though, I think the topic belong to somewhere else than swift-users. I'm just not sure where. I should probably file a radar against Foundation at the very least. -- Michel Fortin https://michelf.ca ___ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users
Re: [swift-users] So how do you implement a NSTextStorage subclass in Swift?
I've seen this before; I considered it something to be resolved in AppKit. Strings in Swift are values, and the framework expects it to be a reference. Since NSTextStorage is itself a reference-type and informs its delegate of changes, those frameworks should probably retain the NSTS itself and pull Strings out as-needed. Once the new String model is done (long time yet), I would love it if Apple's CoreText team created a truly Swift (i.e. Protocol-based) NSTextStorage API. You wouldn't necessarily have to provide a stdlib String; any UTF16-encoded Unicode/StringProtocol instance could possibly suffice, allowing you to optimise storage through buffer-gaps and whatnot (doable today with NSMutableString subclass, but you can't subclass a struct). - Karl > > On Feb 10, 2017 at 10:51 pm, (mailto:swift-users@swift.org)> wrote: > > > > Got some clarity on this from Apple folks on Twitter: > https://twitter.com/jtbandes/status/830159670559993856 > > > On Fri, Feb 10, 2017 at 12:54 PM, Michel Fortin(mailto:michel.for...@michelf.ca)> wrote: > > > > > I did file one (30314719). I might not have explained the problem clearly > > enough, I suppose, because at the time I was misinterpreting the API > > contract thinking it was the new AppKit Touch Bar stuff that was violating > > it instead. That bug now sits closed and I hesitate opening a new bug for > > the same problem just to ask it to be fixed in another way. Meanwhile I > > found an acceptable workaround that I attached to the existing bug report > > in addition and I posted all this to the list. Hopefully someone at the > > right place will notice. > > > > > > But yeah... maybe I should file another bug, against Foundation's Swift > > interface this time, since NSTextStorage's string property comes from > > NSAttributedString. I'll think about it. > > > > > > > > > > > > > > > > > > Le 10 févr. 2017 à 11:36, Jacob Bandes-Storch > > (mailto:jtban...@gmail.com)> a écrit : > > > > > > > > > > > > This seems like a bug (missing feature?) in how the API is imported for > > > Swift. You might consider filing a Radar. > > > > > > > > > > > > > > > On Thu, Feb 9, 2017 at 3:12 PM Michel Fortin via swift-users > > > wrote: > > > > > > > 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 (https://michelf.ca/) > > > > > > > > ___ > > > > swift-users mailing list > > > > swift-users@swift.org (mailto:swift-users@swift.org) > > > > https://lists.swift.org/mailman/listinfo/swift-users > > > > > > > > > > > > > > > > > > > > > > -- > > Michel Fortin > > https://michelf.ca > > > > > > > > > > > > > > > > > > >___ swift-users mailing
Re: [swift-users] So how do you implement a NSTextStorage subclass in Swift?
Got some clarity on this from Apple folks on Twitter: https://twitter.com/jtbandes/status/830159670559993856 On Fri, Feb 10, 2017 at 12:54 PM, Michel Fortinwrote: > I did file one (30314719). I might not have explained the problem clearly > enough, I suppose, because at the time I was misinterpreting the API > contract thinking it was the new AppKit Touch Bar stuff that was violating > it instead. That bug now sits closed and I hesitate opening a new bug for > the same problem just to ask it to be fixed in another way. Meanwhile I > found an acceptable workaround that I attached to the existing bug report > in addition and I posted all this to the list. Hopefully someone at the > right place will notice. > > But yeah... maybe I should file another bug, against Foundation's Swift > interface this time, since NSTextStorage's string property comes from > NSAttributedString. I'll think about it. > > > Le 10 févr. 2017 à 11:36, Jacob Bandes-Storch a > écrit : > > This seems like a bug (missing feature?) in how the API is imported for > Swift. You might consider filing a Radar. > > On Thu, Feb 9, 2017 at 3:12 PM Michel Fortin via swift-users < > swift-users@swift.org> wrote: > >> 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 >> > > -- > Michel Fortin > https://michelf.ca > > ___ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users
Re: [swift-users] So how do you implement a NSTextStorage subclass in Swift?
This seems like a bug (missing feature?) in how the API is imported for Swift. You might consider filing a Radar. On Thu, Feb 9, 2017 at 3:12 PM Michel Fortin via swift-users < swift-users@swift.org> wrote: > 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 > ___ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users
Re: [swift-users] So how do you implement a NSTextStorage subclass in Swift?
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 >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
[swift-users] So how do you implement a NSTextStorage subclass in Swift?
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