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

5

6

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.

Academic answered 21/6, 2017 at 11:14 Comment(2)
They can't really. SCNNode is probably an Obj-C extension and this is how the Swift interface got generated. It's not valid Swift really.Gradation
It can be a computed property, they look the same as stored properties in the generated interface.Equalitarian
B
5

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

Beriosova answered 21/6, 2017 at 16:42 Comment(0)
K
13

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)
        }
    }
}
Kolyma answered 26/2, 2018 at 1:28 Comment(0)
B
5

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

Beriosova answered 21/6, 2017 at 16:42 Comment(0)
T
1

Funny I have the same feeling as the OP. Check this blog post by Apple itself: https://developer.apple.com/swift/blog/?id=37

You will notice they clearly break their only rules, and they explain how to use JSON with a code that isn't compilable.

extension Restaurant {
    private let urlComponents: URLComponents // base URL components of the web service
    private let session: URLSession // shared session for interacting with the web service

    static func restaurants(matching query: String, completion: ([Restaurant]) -> Void) {
        var searchURLComponents = urlComponents
        searchURLComponents.path = "/search"
        searchURLComponents.queryItems = [URLQueryItem(name: "q", value: query)]
        let searchURL = searchURLComponents.url!

        session.dataTask(url: searchURL, completion: { (_, _, data, _)
            var restaurants: [Restaurant] = []

            if let data = data,
                let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                for case let result in json["results"] {
                    if let restaurant = Restaurant(json: result) {
                        restaurants.append(restaurant)
                    }
                }
            }

            completion(restaurants)
        }).resume()
    }
}

That entire article is about how to handle JSON in Swift, so "Restaurants" is not defined in any Objective-C code.

Towhee answered 7/8, 2017 at 18:33 Comment(0)
O
0

in Swift 5, Xcode 13 I was able to add the property directly to the extension using static (no need to create an inner struct etc..)

For example:

extension String {
   static var onboarded = "onboarded"
   static var locationAllowed = "locationAllowed"
}

Usage:

@AppStorage(.locationAllowed) var locationAllowed = false
@AppStorage(.onboarded) var a = false

@AppStorage need a "String" as a parameter, so I just used .onboarded from the Extension

Owlish answered 7/4, 2022 at 10:43 Comment(0)
L
-1

You may try this code, I'm using Xcode 8.3.3 and Swift 3.1:

import SceneKit
import GameplayKit

extension SCNNode {

    @available(OSX 10.13, *)
    public var entity: GKEntity? {
        return self.entity
    }
}
Lust answered 15/9, 2017 at 7:36 Comment(1)
get only properties are allowedDemmy

© 2022 - 2024 — McMap. All rights reserved.