1

I've already check all of those topics:

How to save an array of custom struct to NSUserDefault with swift?

How to save struct to NSUserDefaults in Swift 2.0

STRUCT Array To UserDefaults

I have a struct containing some Strings and an other struct: MySection.

struct MySection {
  var name: String = ""
  var values: [MyRow] = []
}

And there is MyRow which is store in MySection.values

struct MyRow {
  var value: String = ""
  var quantity: String = ""
  var quantityType: String = ""
  var done: String = ""
}

Two arrays for use it

var arraySection: [MySection] = []
var arrayRow: [MyRow] = []

And in my application, I add dynamically some values in those arrays.

There is the delegate method for get datas from my second ViewController

func returnInfos(newItem: [MyRow], sectionPick: String) {
    arrayRow.append(MyRow())
    arrayRow[arrayRow.count - 1] = newItem[0]
    manageSection(item: sectionPick)
    listTableView.reloadData()
}

And there is the manageSection function.

func manageSection(item: String) {
    var i = 0
    for _ in arraySection {
        if arraySection[i].name == item {
            arraySection.insert(MySection(), at: i + 1)
            arraySection[i + 1].values = [arrayRow[arrayRow.count - 1]]
            return
        }
        i += 1
    }
    arraySection.append(MySection())
    arraySection[arraySection.count - 1].name = item
    arraySection[arraySection.count - 1].values = [arrayRow[arrayRow.count - 1]]
}

My need is to store datas of the two arrays in UserDefaults (or CoreData maybe??) and use these datas when the user going back to the application.

I don't know how to do it, I've already try methods from the 3 topics but I'm not even doing a good job.

How can I do it?

Thanks guys!

pierreafranck
  • 445
  • 5
  • 19
  • The last post you linked explains quite well. What do you not understand about it? – Sweeper Aug 21 '17 at 08:38
  • How to use it, I don't understand the method. And when call the save and reload function.. – pierreafranck Aug 21 '17 at 08:41
  • You write a method that converts the struct to and from a `[String: Any]`. This way you can save the converted struct into UserDefaults. When you retrieve it you convert the dictionary back to the struct. Show something that you have tried from all these posts that you read. – Sweeper Aug 21 '17 at 08:43

1 Answers1

2

Since both types contain only property list compliant types a suitable solution is to add code to convert each type to a property list compliant object and vice versa.

struct MySection {
    var name: String
    var values = [MyRow]()

    init(name : String, values : [MyRow] = []) {
        self.name = name
        self.values = values
    }

    init(propertyList: [String: Any]) {
        self.name = propertyList["name"] as! String
        self.values = (propertyList["values"] as! [[String:String]]).map{ MyRow(propertyList: $0) }
    }

    var propertyListRepresentation : [String: Any] {
        return ["name" : name, "values" : values.map { $0.propertyListRepresentation }]
    }
}

struct MyRow {
    var value: String
    var quantity: String
    var quantityType: String
    var done: String

    init(value : String, quantity: String, quantityType: String, done: String) {
        self.value = value
        self.quantity = quantity
        self.quantityType = quantityType
        self.done = done
    }

    init(propertyList: [String:String]) {
        self.value = propertyList["value"]!
        self.quantity = propertyList["quantity"]!
        self.quantityType = propertyList["quantityType"]!
        self.done = propertyList["done"]!
    }

    var propertyListRepresentation : [String: Any] {
        return ["value" : value, "quantity" : quantity,  "quantityType" : quantityType, "done" : done ]
    }
}

After creating a few objects

let row1 = MyRow(value: "Foo", quantity: "10", quantityType: "Foo", done: "Yes")
let row2 = MyRow(value: "Bar", quantity: "10", quantityType: "Bar", done: "No")

let section = MySection(name: "Baz", values: [row1, row2])

call propertyListRepresentation to get a dictionary ([String:Any]) which can be saved to User Defaults.

let propertyList = section.propertyListRepresentation

Recreation of the section is quite easy, too

let newSection = MySection(propertyList: propertyList)

Edit

Use the propertyList initializer only if you get data from UserDefaults in all other cases use the other initializer.

For example replace

@IBAction func addButtonPressed(_ sender: Any) {
    newProducts.append(MyRow(propertyList: ["":""]))
    newProducts[newProducts.count - 1].value = nameTextField.text!
    newProducts[newProducts.count - 1].quantity = quantityTextField.text!
    newProducts[newProducts.count - 1].quantityType = type
    newProducts[newProducts.count - 1].done = "No"
    delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
    navigationController?.popViewController(animated: true)
}

with

@IBAction func addButtonPressed(_ sender: Any) {

    let row = MyRow(value: nameTextField.text!,
                    quantity: quantityTextField.text!,
                    quantityType: type,
                    done: "No")
    newProducts.append(row)
    delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
    navigationController?.popViewController(animated: true)
}

and replace

func returnInfos(newItem: [MyRow], sectionPick: String) {
    arrayRow.append(MyRow(propertyList: ["":""]))
    arrayRow[arrayRow.count - 1] = newItem[0]
    manageSection(item: sectionPick)
    listTableView.reloadData()
}

with

func returnInfos(newItem: [MyRow], sectionPick: String) {
    arrayRow.append(newItem[0])
    manageSection(item: sectionPick)
    listTableView.reloadData()
}

Basically first create the object, then append it to the array. The other way round is very cumbersome.

vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thanks for your answer. I've tried to implement the first part of your answer and now when I try to add something in my arraySection, i've an error on this line: `init(propertyList: [String:String]) { self.value = propertyList["value"]!` – pierreafranck Aug 21 '17 at 09:27
  • There are two initializers, a standard one and a second which expects a dictionary with the four keys corresponding to the properties. The second initializer is supposed to be used only for objects created with `propertyListRepresentation` because there is no check for `nil`. – vadian Aug 21 '17 at 09:30
  • i've edit and post my entire code, can you help me on that? I very don't know how to make it works. – pierreafranck Aug 21 '17 at 09:36
  • I updated the answer with an example. Once again, use the `propertyList` initializer only if you get/set data from/to `UserDefaults`. I removed all default values from the properties, so you have to use the initializer passing all parameters. That makes the code more safe and consistent. – vadian Aug 21 '17 at 09:48
  • For setting convert the objects to property by calling `propertyListRepresentation`, for getting create the objects with the `propertyList` initializer. For arrays you have to call `map` like in the `MySection` struct. – vadian Aug 21 '17 at 10:03
  • You have to write the converted dictionary to user defaults and read the dictionary and create `MySection` instances from it. – vadian Aug 21 '17 at 12:22