53

I have following struct:

public protocol SuperModel {
    // empty protocol
}
struct ModelOne: SuperModel {
    struct SubModelOne {
        var someVar: Double
        var othervar: Double?
    }
    var sub: SubModelOne?
    mutating func setSub(sub: SubModelOne) {          
        self.sub = sub
    }
}

In my class, I want to use this struct like that:

final class SomeClass: SuperClass {
    var data: SuperModel
    init() {
        self.data = ModelOne()
    }
    func someFunc() {
        (self.data as! ModelOne).setSub(ModelOne.SubModelOne(someVar: 2, otherVar: 1))
    }
}

I get following error: Cannot use mutating member on immutable value of type 'ModelOne'. Why is that so and how can I fix this?

j0h4nn3s
  • 2,016
  • 2
  • 20
  • 35
  • 2
    What is NetworkCheckModel? A self-contained example would be useful. – Martin R Aug 04 '16 at 09:52
  • try this var data = self.data as! ModelOne data.setSub(ModelOne.SubModelOne(someVar: 2, othervar: 1)) – Sahil Aug 04 '16 at 09:57
  • @MartinR sorry I was in a rush and forget to change it. Edit was made – j0h4nn3s Aug 04 '16 at 10:50
  • @SahilBeri this works, but that means i would have a copy of it. Can I do `self.data = data` do copy it back? – j0h4nn3s Aug 04 '16 at 10:53
  • You can do it self.data = data but then you will not able to call setSub func using only self.data . only then you can call only those which is define in SuperModel protocol. – Sahil Aug 04 '16 at 11:24
  • > by doing this var data = self.data as! ModelOne you are getting object of ModelOne which reference is you assigning to data variable and calling the setSub function. - @SahilBeri you are very close to right answer, but by some mistake. You think that structs are reference types, but if so - original code should work. Structs are value types. – Yury Aug 04 '16 at 11:32
  • ohh sorry i did it by mistake . – Sahil Aug 04 '16 at 11:34
  • basically a copy of the ModelOne is made, and this new copy is assigned to data. – Sahil Aug 04 '16 at 11:36

6 Answers6

51

When you apply type casting to value types (such structs), if succeed, you receive immutable copy of requested value:

(self.data as! ModelOne) // this is copy of data

The only way (as known to me) how you can mutate values that need to be casted - reassign value (as @Sahil Beri pointed you need declare variable):

func someFunc() {
    if var data = data as? ModelOne {
        data.setSub(ModelOne.SubModelOne(someVar: 2, otherVar: 1))
        self.data = data // you can do this since ModelOne conforms to SuperModel
    }
}
Yury
  • 6,044
  • 3
  • 19
  • 41
3

Use like this,

struct UserAttributes {
    var name:String?
    var organizationID:String?
    var email:String?
    
    mutating func parseUserAttributes(attribues:[AWSCognitoIdentityProviderAttributeType])->UserAttributes{
        
        for type in attribues{
            if type.name == "name"{
                name = type.value
            }else if(type.name == "family_name"){
                organizationID = type.value
            }else if(type.name == "custom:role_id"){
                role = type.value
            }else if(type.name == "email"){
                email = type.value
            }
        }
    }
}

In some other file call like this,

var userAttributes = UserAttributes()
userAttributes = userAttributes.parseUserAttributes(attribues:attributes)
bfox
  • 274
  • 2
  • 13
1

Problem is that you have declared data as SuperModel but allocate it as ModelOne. Declare data as ModelOne. Then the problem goes away.

final class SomeClass: SuperClass {
    var data: ModelOne
    init() {
        self.data = ModelOne()
    }
    func someFunc() {
        (self.data).setSub(ModelOne.SubModelOne(someVar: 2, otherVar: 1))
    }
}
Muzahid
  • 5,072
  • 2
  • 24
  • 42
0

First downcast the self.data to ModelOne then call setSub function

 if var data = self.data as? ModelOne {
   data.setSub(ModelOne.SubModelOne(someVar: 2, othervar: 1))
 }
Sahil
  • 9,096
  • 3
  • 25
  • 29
0

@Shadow of is right. You try to mutate a temporary structure which is impossible and most of the time useless as it will be released once the mutation done. It's in fact a similar issue to trying to modify the return struct of a function. (see answer here : Cannot assign to property: function call returns immutable value)

Community
  • 1
  • 1
Bioche
  • 458
  • 4
  • 11
0

In Swift 3, in my case, I was able to resolve the error just by changing struct to a class object.

shim
  • 9,289
  • 12
  • 69
  • 108
Naishta
  • 11,885
  • 4
  • 72
  • 54
  • 27
    Should not do that in Swift. – lee Oct 19 '16 at 07:28
  • 3
    Yes, of course you fix it! But this isn't a smart approach to do it. Check https://developer.apple.com/videos/play/wwdc2015/408/ – Daniel Molina Oct 03 '17 at 07:24
  • This solved my issue where I was looping through and array of objects that contained another object that i needed to modify. – Wez Jan 31 '19 at 11:27