Cannot assign to property: 'self' is immutable swift error
Asked Answered
B

1

7

Inside my app I created a Protocol called "Positions" and extended the UIView class to conform to this protocol in order to add some properties to the UIView class. I used the code below.

import Foundation
import UIKit

protocol Positions {
    var initialPositions: [CGRect] {get set}
    var finalPositions: [CGRect] {get set}
    var positionsAreDefined: Bool {get}
}

public enum PositionsType {
    case initial
}

private var initialPositionKey: UInt = 0
private var finalPositionKey: UInt = 0

extension Positions {
    var initialPositions: [CGRect] {
        get {
            if objc_getAssociatedObject(self, &initialPositionKey) != nil {
                return objc_getAssociatedObject(self, &initialPositionKey) as! [CGRect]
            } else {
                return [] as! [CGRect]
            }

        }
        set {
            objc_setAssociatedObject(self, &initialPositionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var finalPositions: [CGRect] {
        get {
            if objc_getAssociatedObject(self, &finalPositionKey) != nil {
                return objc_getAssociatedObject(self, &finalPositionKey) as! [CGRect]
            } else {
                return [] as! [CGRect]
            }
        }

        set {
            objc_setAssociatedObject(self, &finalPositionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var positionsAreDefined: Bool {
        get {return initialPositions.count != 0 && finalPositions.count != 0 && initialPositions.count == finalPositions.count}
    }

    var positionsCount: Int {
        get {return initialPositions.count}
    }
}

extension UIView: Positions {}

Well, today I tried to extend the UIView class with a new method that changes the added-by-me properties but the compiler gave me this error on the line where I tried to modify the properties value:

Cannot assign to property: 'self' is immutable

Here's the method:

public func horizontallyInContainer(withShift shift: CGFloat, forExpansionNumber index: Int, forPositionType type: PositionsType) {
    if self.positionsAreDefined && self.superview != nil {
        if type == .initial {

            var newPositions = self.initialPositions
            newPositions[index].origin.x = self.superview!.bounds.width * 0.5 - self.bounds.width + shift
            self.initialPositions = newPositions //Here happens the error

        } else {

            var newPositions = self.finalPositions
            newPositions[index].origin.x = self.superview!.bounds.width * 0.5 - self.bounds.width + shift
            self.finalPositions = newPositions //Also here happens

        }
    }
}

Can anyone explain me the nature of that error?

Bellringer answered 9/10, 2018 at 18:35 Comment(7)
Non-class bound protocols have value semantics, so any mutating functions have to be declared so with the mutating modifier, just as if they were inside a struct definition.Hebrides
If you're going to be using objc_getAssociatedObject, and other Objective C runtime features, you need to make this a class bound protocol. It will work otherwise, but you'll notice bizarre behavior. Structs get boxed into a __SwiftValue, which can be used by the ObjC runtime, but their have unstable object identity. Object identity is what the ObjC runtime uses as keys in its associated objects table.Hebrides
@Carcigenicate I edited my postBellringer
@Hebrides do you suggest I should put the keyword "mutating" before the "horizontallyInContainer" method declaration?Bellringer
@Hebrides Can you explain me how can I make this a class bound protocol?Bellringer
It's what @vadian shows in his answer.Hebrides
Yes I saw...thank u tooBellringer
M
33

Make the protocol become reference type

protocol Positions: AnyObject {
Massarelli answered 9/10, 2018 at 18:58 Comment(2)
Ok, thank you, it works...Can you explain me what does that do and maybe point me toward something I can read about that, just in order to don't fall again on the same mistake? :)Bellringer
It's discussed in the Swift Language Guide : Protocols, however in the most recent version conforming to AnyObject is recommended rather than to abstract classMassarelli

© 2022 - 2024 — McMap. All rights reserved.