Unwrapping Unmanaged<SKIndex>

This compiler error is confusing me.

'Unmanaged' is not convertible to 'SKIndex'; did you mean to use 'as!' to force downcast?

In this function below, doc is an Unmanaged<SKDocument> and the compiler is fine with the as. But it doesn't like a similar construction with skIndex: Unmanaged<SKIndex>. I tried changing as to as! but it crashes at runtime.

How do I pass the SKIndex to that function? (It's from Search Kit.)

func addCardToIndex(card: Card) {
    guard let skIndex = self.skIndex else { fatalError() }
    let doc = SKDocumentCreate("card" as CFString, nil, card.id.description as CFString)!
    let contents = "foo bar baz"
    let replace = true
    SKIndexAddDocumentWithText(
         skIndex as SKIndex /* error */, 
         doc as SKDocument, contents as CFString, replace)
}

Yup, that appears to do it.

The as compiled with SKDocument, but causes a failure at runtime. I needed to replace that with doc.takeUnretainedValue() as well, to get the function call to succeed.

Nikita Belov wrote:

Ideally, you should have already called takeRetainedValue() whenever
you've got that Unmanaged<SKIndex> passed to you by one of the
SKIndexCreate* functions.

That’s exactly right. Your goal here should be to minimise the amount of unmanaged stuff that you do. In this case that means converting from an unmanaged reference to a managed reference as soon as possible. I often do that by writing a small wrapper, for example:

func SKDocumentCreateQ(_ scheme: String, parent: SKDocument?, name: String) -> SKDocument? {
    guard let doc = SKDocumentCreate(scheme as NSString, parent, name as NSString) else {
        // This returns `nil` on error but it might make more sense to throw.
        return nil
    }
    return doc.takeRetainedValue()
}

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like