How to fix ‘Extensions must not contain stored properties’ error


Greetings, traveler!

This is uncommon, but we may want to store properties within extensions. After doing that, we will immediately find that the compiler does not allow us to do this on the spot.

class Object {}

extension Object {
    var property = "" // ❌ Extensions must not contain stored properties
}

This is because Swift is a type-safe language. Therefore, storing properties in extensions can be unexpected, as it may change an object’s size. However, there are two possible solutions to this problem.

Nested entities

You can create a nested struct or enum with a static property.

extension Object {
    
    private enum Storage {
        static var property = ""
    }
    
}

Then, using this nested object, you can create a computed property in an extension with a getter and a setter.

extension Object {
    
    private enum Storage {
        static var property = ""
    }
    
    var property: String {
        get {
            Storage.property
        }
        set {
            Storage.property = newValue
        }
    }
    
}

Associated Objects

There is another way to solve our problem. You can add import ObjectiveC and use Associated Objects. To do this, first, create a key storage. I prefer using enums.

import ObjectiveC

extension Object {
    
    private enum Key {
        static var propertyKey: Void?
    }
    
}

Then, you can add a computed property like in the previous case. But now we will use it with the associated object where:

  • object is an object pointing to another one.
  • objectkey is the key for linking these objects.
  • value — the object that we want to bind with.
  • policy is the enum defining the link type. There are five different policy cases: OBJC_ASSOCIATION_ASSIGN, OBJC_ASSOCIATION_RETAIN_NONATOMIC, OBJC_ASSOCIATION_COPY_NONATOMIC, OBJC_ASSOCIATION_RETAIN, and OBJC_ASSOCIATION_COPY.
import ObjectiveC

extension Object {
    
    private enum Key {
        static var propertyKey: Void?
    }
    
    var property: String? {
        get {
            objc_getAssociatedObject(
                self,
                &Key.propertyKey
            ) as? String
        }
        set {
            objc_setAssociatedObject(
                self,
                &Key.propertyKey,
                newValue,
                .OBJC_ASSOCIATION_RETAIN
            )
        }
    }
    
}

Conclusion

We have found a way to implicitly avoid Swift’s restriction. But be careful with these techniques. Often, attempting to do something that is not meant to be done using standard language methods signals that we have made a mistake in our design.