Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Extensions may not contain stored properties" unless your are Apple? What am I missing?

How come Apple can do this:

import CoreGraphics
import GameplayKit
import simd

/**
 @header


 SceneKit framework category additions related to GameplayKit integration.


 @copyright 2017 Apple, Inc. All rights reserve.

 */

extension SCNNode {


    /**
     * The GKEntity associated with the node via a GKSCNNodeComponent.
     *
     * @see GKEntity
     */
    @available(OSX 10.13, *)
    weak open var entity: GKEntity?
}

/**
 * Adds conformance to GKSceneRootNodeType for usage as rootNode of GKScene 
 */
extension SCNScene : GKSceneRootNodeType {
}

... and I cannot do this:

extension SCNNode {
    weak open var ntity: GKEntity?
}

and get two errors:

  • 'weak' may only be applied to class and class-bound protocol types, not '<<error type>>'
  • Extensions may not contain stored properties

What I would like to actually do is to provide an entity property on OSX versions before 10.13, so additional suggestions for that are also welcome.

like image 823
Bertan Gundogdu Avatar asked Jun 21 '17 11:06

Bertan Gundogdu


2 Answers

Swift 4 / iOS 11 / Xcode 9.2

The answers here are technically right, but here's a solution anyway.

In your extension, define a private struct with the fields you're going to want. Make everything static, like this:

private struct theAnswer {
    static var name: String = ""
}

I know, I know, static means it's a class-wide variable/property, not an instance one. But we are not actually going to store anything in this struct.

In the code below, obj_getAssociatedObject and objc_setAssociatedObject both require an UnsafeRawPointer as a key. We use these functions to store a key/value pair which is associated with this unique instance.

Here's the complete example with a String:

import Foundation

class ExtensionPropertyExample {

    // whatever
}


extension ExtensionPropertyExample {
    private struct theAnswer {
        static var name: String = ""
    }

    var name: String {
        get {
            guard let theName = objc_getAssociatedObject(self, &theAnswer.name) as? String else {
                return ""
            }
            return theName
        }
        set {
            objc_setAssociatedObject(self, &theAnswer.name, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }
}
like image 122
drewster Avatar answered Sep 20 '22 21:09

drewster


This is currently not possible in Swift. As noted by Sulthan this is an Objective-C category for which you see the Swift version, which is generated by Xcode.

Now, Objective-C does not easily support adding properties in categories (extensions are called categories in Objective-C), but you can use associated objects to get what you want.

Mattt Thompson has a great article about associated objects on his NSHipster blog: Associated Objects - NSHipster

like image 25
Kai Engelhardt Avatar answered Sep 18 '22 21:09

Kai Engelhardt