json: Cannot assign to value: 'self' is immutable

Swift 4, Codable json decode/encode - Cannot assign to value: 'self' is immutable


Using Stanford CS193P Developing iOS 11 Apps with Swift. Lecture 14. Persistence and Documents Demo , I was able to make my objects Codable


The saving part work, I can see the resulting json in Apple File App

but when I try to read the file with


init?(json: Data) {

if let newValue = try? JSONDecoder().decode(CADevice.self, from: json){

self = newValue

}else{

return nil

}

}


I get an error at compile time

self = newValue -> Cannot assign to value: 'self' is immutable


what should I be looking for ?


Thank you

Answered by QuinceyMorris in 340663022

There are only a very few circumstances where you can assign to 'self'. The most common one is in the initializer for a custom struct.


My guess is that the lesson asked you to implement Codable for a custom struct, and you accidentally made a custom class instead.

Can you show the complete code ?


Typically, you should have :


class SomeClass {
    var aDevice: CADevice

    init?(json: Data) {
        if let newValue = try? JSONDecoder().decode(CADevice.self, from: json) {
            self.aDevice = newValue
        }else{
            return nil
        }
    }

}
Accepted Answer

There are only a very few circumstances where you can assign to 'self'. The most common one is in the initializer for a custom struct.


My guess is that the lesson asked you to implement Codable for a custom struct, and you accidentally made a custom class instead.

Ohhhh


i was impress with the Self init within an object :-) but you are right, I was trying to do it in a class.

I will see if my class can be converted into a Struct, i think it is


To go a bit further. instead of reading the json inside the model class itsef, can i do it in the Controller class to recreate the object.

I will try that.

If you do not want to convert to a struct, change the init :


class SomeClass { 
    var aDevice: CADevice 
 
    init?(json: Data) { 
        if let newValue = try? JSONDecoder().decode(CADevice.self, from: json) { 
            self.aDevice = newValue 
        }else{ 
            return nil 
        } 
    } 
 
}

In the stanford class, that's how they do it

they recreate the EmojiArt struct from json directely in the init of the struct

not just a var inside an object


but i guess that how i should do it in the controller class

thank you

import Foundation

struct EmojiArt: Codable {
    var url: URL
    var emojis = [EmojiInfo]()
    
    struct EmojiInfo: Codable {
        let x: Int
        let y: Int
        let text: String
        let size: Int
    }
    
    init?(json: Data) {
        if let newValue = try? JSONDecoder().decode(EmojiArt.self, from: json) {
            self = newValue
        } else {
            return nil
        }
    }
    
    var json: Data? {
        return try? JSONEncoder().encode(self)
    }
    
    init(url: URL, emojis: [EmojiInfo] ) {
        self.url = url
        self.emojis = emojis
    }
}

my question was about the error nessage and even if I did not try to change my class to struct, I would make sense to me that it would work with a struct so QuinceyMorrisanswer would probably be the right one. If i have time i will try to change to struct just to see


but for now, I did what Claude31propose and put my code outside of my class and it worked.


I really like Paul Hegarty code, i find it very elegant !!!

and keeping the code inside the struct itself really help with encapsulation.


it is my first time with saving file on iphone and the first time with json data.

Codable make it really easy,


Thank you both for the help.

There is another approach you can use if you really want to use a custom class: a static method instead of an initializer. In your example, this would look something like this:


    static func create(json: Data) -> CADevice? {
        if let newValue = try? JSONDecoder().decode(CADevice.self, from: json){
            return newValue
        }else{
            return nil
        }
    }


To use it, instead of this:


let device = CADevice(json: data)


you would do this:


let device = CADevice.create(json: data)


(I'm not suggesting that "create" is the best name for this function, but you get the idea.)

json: Cannot assign to value: 'self' is immutable
 
 
Q