1

I read somewhere that it was best practice to use structs when passing around model data using Swift.

I have been doing so but I have been wondering whether there is anything that I can do in regards to the creation of large (and growing) user data objects like this:

struct UserAccount {

    var id: String?
    let authId: String

    let emailAddress: String
    let mobilePhoneNumber: String

    let firstName: String
    let lastName: String

    let countryCode: String
    let homepageUrl: String
    let tagline: String

    let pictureUrl: String

    let accountState: AccountState = .NoAccount

    // ... 
}

This is more or less what I use when I create a user account, but later on it feels cumbersome and wrong to have to instantiate gigantic objects in code. I am parsing JSON responses using json-swift but then having to instantiate the models separately, like so:

let id = jsonData["id"].string!
let authId = jsonData["authId"].string!
let emailAddress = jsonData["emailAddress"].string!
let mobilePhoneNumber = jsonData["mobilePhoneNumber"].string!
let firstName = jsonData["firstName"].string!
let lastName = jsonData["lastName"].string!
let countryCode = jsonData["countryCode"].string!
let homepageUrl = jsonData["homepageUrl"].string!
let tagline = jsonData["tagline"].string!
let pictureUrl = jsonData["pictureUrl"].string!
let accountState = convertAccountStateStringToEnum(jsonData["accountState"].string!)


let userAccount = UserAccount(
    id: id,
    authId: authId,
    emailAddress: emailAddress,
    mobilePhoneNumber: mobilePhoneNumber,
    firstName: firstName,
    lastName: lastName,
    countryCode: countryCode,
    homePageUrl: homepageUrl,
    tagline: tagline,
    pictureUrl: pictureUrl,
    accountState: accountState
)

It might seem absurd that above I've instantiated the variables before I instantiate the struct, but the reason I did so is that when the IDE gives me type coercion errors from within a struct it is very difficult to understand what is wrong, and so this allows me to troubleshoot it quicker when I am making model changes. Any thoughts around this?

My Question:

Later on that user object is likely to contain a lot more data on the server side and instantiating use models with 50+ lines seems like a bad idea, therefore:

  1. Are there solutions to create structs in some simpler way? Does the pattern have a name?
  2. Should I be creating User models that are related to specific tasks? For example, a UserAccount model to GET or PUT a profile might be different from the one used to authenticate, get the user settings or list the most popular accounts on my service.
  3. Do people just leave it as it is and define mappers so that there is no redundancy? If so - what's a good example of this?

Thanks.

2 Answers 2

3

When faced with this, I would build another initializer for the model that takes the JSON data:

struct UserAccount {
    // ...
    init(jsValue: JSValue) {
        id = jsonData["id"].string!
        authId = jsonData["authId"].string!
        emailAddress = jsonData["emailAddress"].string!
        mobilePhoneNumber = jsonData["mobilePhoneNumber"].string!
        firstName = jsonData["firstName"].string!
        lastName = jsonData["lastName"].string!
        countryCode = jsonData["countryCode"].string!
        homepageUrl = jsonData["homepageUrl"].string!
        tagline = jsonData["tagline"].string!
        pictureUrl = jsonData["pictureUrl"].string!
        accountState = convertAccountStateStringToEnum(jsonData["accountState"].string!)
    }
}

Then you can simply create new UserAccount instances from your JSON data:

let userAccount = UserAccount(jsValue: jsonData)
Sign up to request clarification or add additional context in comments.

Comments

0

A few thoughts:

  • Can you share the reference that suggests its best practice to use structs rather than classes when passing around model data? I would have thought that passing around structs (especially big ones) by value would be less efficient than passing around class instances by reference.

    See Choosing between classes and structures in The Swift Programming Language: Classes and Structures which suggests you want to use structures when:

    • The structure’s primary purpose is to encapsulate a few relatively simple data values.

    • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure.

    • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.

    • The structure does not need to inherit properties or behavior from another existing type.

    Your user object does not seem to match this criteria. I would have thought the class approach is more logical.

  • BTW, if using classes, you can also simplify the code to instantiate an object if the class is KVO compliant. For example, you can iterate through an array of key names, grabbing the value associated with each key name from the JSON and then using setValue:forKey: to set the object's property for that particular key.

    For more information about KVO-compliance see the Key-Value Observing Programming Guide: KVO Compliance. For more information about how to make Swift object KVO-compliant, see Key-Value Observing section of Using Swift with Cocoa and Objective-C: Adopting Cocoa Design Patterns.

  • In terms of separate user models for different tasks, that doesn't makes sense for me. You might have different objects for different functional purposes (e.g. accounts/authentication objects) which might reference the user objects, but it doesn't seem to make sense to have different types of user classes/structures.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.