0

I would like to create a Codable structure to save data from a user profile Let's say the ios user filled a form with his name, last name,adress, put his picture (UIImage)) and that user have an unique ID . My JSON would be like

{"Unique_ID" :"1234556778", 
    {"Name":"John",
     "Last Name" : "Doe",
     "adress" :
        {"street": "21 jump street",
         "country" : "USA"}
     } 
}

I tried to create the Codable but I think it isn't optimal , could you kindly give me some hint to optimize it or tell me if I'm wrong doing it like this ?

        struct User_Profile: Codable {
        let Unique_ID : String
        let Data_Of_User : User_Profile_Data
         }

  struct User_Profile_Data: Codable {
        let name : String
        let last_name: String
        let adress : User_Adress_Data
       }

  struct User_Adress_Data: Codable {
        let street : String
        let country: String
       }



override func viewDidLoad() {
        super.viewDidLoad()
        
        let usernametest = "John"
     
        let b = User_Adress_Data(street: "21 jump street", country: "USA")
        let c = User_Profile_Data(name: usernametest, last_name: "Doe", adress: b)
        let d = User_Profile(Unique_ID: "123456", Data_Of_User: c)
        }

After that, I would like to cache it, with Haneke swift(https://github.com/Haneke/HanekeSwift), or Datacache (https://github.com/huynguyencong/DataCache) How can I cache my Codables? (for eg, I didn't view any 'set cache for json' with Haneke)

And finally, I would like to use it after fetched with SwiftyJSON : (https://github.com/SwiftyJSON/SwiftyJSON), and I don't know if my Codables are enought readable for it.

Thanks for your idea/comment !

Victor
  • 61
  • 1
  • 13
  • Don’t use _ with struct name or variables is first thing I would recommend. Also, how do you know it isn’t optimal did you verified with some tool? If you’re persisting user profile and later want to access that at some point to modify, I would suggest use class, not struct. – Tushar Sharma Apr 07 '22 at 10:40
  • So what is your question? What have you tried to make it work, what errors did you face? Why do you think your struct is `not optimal`? Btw. `I would like to` is not a question. – burnsi Apr 07 '22 at 10:51
  • 1
    @TusharSharma structs are the Swift "prefered" way to persist model data. No need for classes here. – burnsi Apr 07 '22 at 10:53
  • The JSON example you provided is not valid JSON. – burnsi Apr 07 '22 at 10:54
  • Design and implement the user profile structures the way you want them then use a playground to instantiate a profile object and encode it and then convert the Data object to a string and print it, there is your json format you want. You only need to use Codable, no need for any 3rd party library – Joakim Danielson Apr 07 '22 at 10:57
  • @burnsi yes, I suggested that so user can pass around and modify profile data objects instead of copying them. Depends on requirement, and not always on preferred way. – Tushar Sharma Apr 07 '22 at 11:04
  • By optimal, I mean is that possible (or is it better to code) with 1 struct with all of is it ok to do 3 struct(User_Profile, User_Profile_Data, User_Adress_Data). And @burnsi you say it is not a valid JSON, why ? I need it to cache it later and fetch it with swifty json – Victor Apr 07 '22 at 11:35
  • If you want to use `Codable` you don't need `SwiftyJSON` at all. And it's unclear what the other library is supposed to do. Most likely you don't need it either. – vadian Apr 07 '22 at 12:00
  • What I am willing to do : A user fills a form (from textfields). I want to cache it with a hierarchy (UID {User Data{Adress...}}} and I would like to fetch it afterwards . My thought was using JSON to insert all data from the filled form, set it (using Haneke or datacache github) and fetch it later (when I need it, in another ViewController) – Victor Apr 07 '22 at 12:05

3 Answers3

2

As you have full controll over your structure and there is no collection involved i would recommend to put everything in one struct instead of scattering it over many different:

struct UserProfile: Codable{
    var id: String
    var name: String
    var lastname: String
    var street: String
    var country: String
}

Regarding caching. As you can mark this struct Codable it can be easily stored in Userdefaults as Data. This extension on Userdefaults should allow you to access UserProfile in a typesafe manner.

extension UserDefaults{
    var userProfile: UserProfile?{
        get{
            guard let data = data(forKey: "userProfile") else{
                return nil
            }
            
            return try? JSONDecoder().decode(UserProfile.self, from: data)
        }
        set{
            set(try? JSONEncoder().encode(newValue), forKey: "userProfile")
        }
    }
}

Usage example:

//Write
UserDefaults.standard.userProfile = UserProfile(id: UUID().uuidString, name: "First", lastname: "Last", street: "nowhere", country: "atlantis")
//Read
let profile = UserDefaults.standard.userProfile

Edit: Editing example:

As this is a struct it gets copied every time something changes. So read the value in a var. Modify it and save it to the defaultStore.

var profile = UserDefaults.standard.userProfile
profile?.country = "newCountry"
UserDefaults.standard.userProfile = profile
burnsi
  • 6,194
  • 13
  • 17
  • 27
1

The example you've provided isn't valid JSON. It looks like some sort of hybrid of a dictionary and an array.

So let's make a few tweaks:

{
  "Unique_ID": "1234556778",
  "Data_of_User": {
    "First_Name": "John",
    "Last_Name": "Doe",
    "Address": {
      "Street": "21 jump street",
      "Country": "USA"
    }
  }
}

Then you can create structs which look like:

struct UserProfile: Codable {
    let uniqueID: String
    let userData: UserData
    
    enum CodingKeys: String, CodingKey {
        case uniqueID = "Unique_ID"
        case userData = "Data_of_User"
    }
}

struct UserData: Codable {
    let firstName: String
    let lastName: String
    let address: Address
    
    enum CodingKeys: String, CodingKey {
        case firstName = "First_Name"
        case lastName = "Last_Name"
        case address = "Address"
    }
}

struct Address: Codable {
    let street: String
    let country: String
    
    enum CodingKeys: String, CodingKey {
        case street = "Street"
        case country = "Country"
    }
}
Joshua Kaplan
  • 843
  • 5
  • 9
  • Thanks @joshua-kaplan , and if I want to add a user by code, how can I do it ? (or to modify his name?) – Victor Apr 07 '22 at 12:37
  • If you wanted to create any of these structs directly, you can just initialize them. The Swift compiler autogenerates an initializer for structs that has parameters for all of its members. If you want to modify any of these values, then you need to switch from `let` to `var`. I'd recommend you spend a couple hours reading [Swift.org's intro](https://docs.swift.org/swift-book/) to the language - it'll almost certainly save you time within a few days. – Joshua Kaplan Apr 08 '22 at 04:27
0

Codable requires the property names in your struct to match the json counterparts. In your User_Profile_Data struct you have name while the json contains "Name" and you have last_name where the json contains "Last Name".

One way to get around this is to add a CodingKeys enum to your struct that tells it how to map the properties. So in your User_Profile_Data struct I would add:

enum CodingKeys: String, CodingKey { case name = "Name" case last_name = "Last Name" case adress }

inTheAM
  • 26
  • 2