John_Sm1 John_Sm1 - 2 months ago 9
iOS Question

NSUser Defaults - Saving 3 variables together and then fetching

I'm using

NSUserDefaults
for persistent storage in my app. It is a game where after the game ends the score, together with the date and name (entered by user) must be stored and then shown in an table view. My code so far is:

import UIKit

class LeaderboardVC: UIViewController, UITableViewDataSource, UITableViewDelegate {

var finishedGame = 0
var gameScore:Int! = 0
var name:String!
var date:String!
var score:Int!
var scoreData = [NSArray]()
var defaults = UserDefaults.standard

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()

if finishedGame == 1{
saveNew()
}

self.tableView.delegate = self
self.tableView.dataSource = self


}

override func viewWillAppear(_ animated: Bool) {

self.navigationController?.isNavigationBarHidden = false
self.navigationItem.hidesBackButton = true



}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()

}

func saveNew() {

let enterNameAlert = UIAlertController(title: "Please Enter Your Name", message: "This will be used to place you in the leaderboards", preferredStyle: .alert)

enterNameAlert.addTextField { (textField:UITextField) -> Void in
textField.placeholder = "Name"
textField.autocapitalizationType = UITextAutocapitalizationType.words
textField.autocorrectionType = UITextAutocorrectionType.no
textField.clearsOnBeginEditing = true
textField.clearsOnInsertion = true
textField.clearButtonMode = UITextFieldViewMode.always
textField.keyboardType = UIKeyboardType.default

}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action:UIAlertAction) in

let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime = timeFormatter.string(from: currentTime) //date
let enteredName = enterNameAlert.textFields?.first?.text //name

// set object of date,name and score here
}

enterNameAlert.addAction(cancelAction)
enterNameAlert.addAction(confirmAction)
self.present(enterNameAlert, animated: true, completion: nil)

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! LeaderBoardCell

cell.dateLabel?.text =
cell.dateLabel.adjustsFontSizeToFitWidth = true
cell.scoreLabel?.text =
cell.scoreLabel.adjustsFontSizeToFitWidth = true
cell.nameLabel?.text =
cell.nameLabel.adjustsFontSizeToFitWidth = true

return cell
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return scoreData.count
}

func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}


There are 3 labels in the cell one for each of the 3 values which must be displayed. How do I set these values using
NSUserDefaults
(They must be together FOR example score:500 goes with name:John and so on)?

Answer

I couldn't get as much but I just found that you need to add some elements to your scoreData for the tableview datasource

Here you can add the complete object in UserDefaults

//Make your scoreData as MutableArray
var scoreData = NSMutableArray()

// Record of a user
let user = [
            "name"  : "John",
            "score" : 10,
            "id"    : 20
        ] as [String : Any]

scoreData.add(user)
defaults.set(scoreData, forKey: "UserData")

How to fetch them

let userData = defaults.object(forKey: "UserData") as! NSArray
print((userData.object(at: 0) as! [String:Any])["name"] as! String)
print((userData.object(at: 0) as! [String:Any])["score"] as! Int)
print((userData.object(at: 0) as! [String:Any])["id"] as! Int)

I have printed them on console and only the 0th index. You can print them on your Label with indexPath.row elements.

EDIT
In order to add a new record, first Fetch the old data from userDefaults

let newUserData = defaults.object(forKey: "UserData") as! NSArray

Then convert it into mutable array because we want to add a new user record. I could have done it while fetching but on fetching from UserDefaults it gives a immutable object no matter we type cast to NSMutableArray. So convert the newUserData to mutable object

let newUserMutableArray = NSMutableArray(array: newUserData)

Add a new record

// New record
let newUserRecord = [
    "name"  : "Rajan",
    "score" : 20,
    "id"    : 30
    ] as [String : Any]

newUserMutableArray.add(newUserRecord)

Save it again

defaults.set(newUserMutableArray, forKey: "UserData")

And again fetch the way it is mentioned above. Now the fetched NSArray will contain two elements.
I still prefer to use core data in this case as the handling will become lot easy.