Search code examples
jsonswiftxcodensuserdefaults

What's the best way to save multiple variables into NSUserdefaults and display on another view controller in the order saved?


New swift guy here! I'm having problems conceptualizing how to store this information of a location with multiple details. Is user defaults the best way? Perhaps json array file?

I've got two view controllers 1. favorites and 2. info.

FavoritesViewController displays multiple groups of data in the order in which they were favorited like this


Favorites

Group 1             Remove button
- Name of location
- Address
- latitude
- longitude
- Hours

then continues groups

On the infoViewController it shows locations from a database. It has a favorite button which stores it for use in the favoritesViewController.

  @IBAction func favoriteThis(_ sender: Any) { 

       // check the state of favorited button
          if toggleState == 1 {
            toggleState = 2
            favoriteThis.setTitle("favorited!", forState: .normal)
            UserDefaults.standard.set(toggleState, forKey: "favorited")

        } else {
            toggleState = 1
         favoriteThis.setTitle("favorite", forState: .normal)
            UserDefaults.standard.set(toggleState, forKey: "favorited")

        }

        //set the current values to variables

        let name = self.selectedLocation!.name
        let address = self.selectedLocation!.address
        let lat = self.selectedLocation!.latitude
        let long = self.selectedLocation!.longitude

         // set them to an array and store them with a unique id
         let array = [id, name, address, lat, long]

         let defaults = UserDefaults.standard
          defaults.set(array, forKey: "FavArray")

   }

This does store in the FavArray, but if I need to save and display more than one of these groups in favoriteViewController, What do you think is a better solution to approaching this?

Thanks everyone!


Solution

  • UserDefaults is meant for small pieces of data. So instead of using User defaults, I would recommend making your custom PList file (You can think of it as your custom UserDefaults file) special for your array of custom Types.

    To solve your problem I created a custom Type which will hold all your location data like so: -

    struct FavoriteLocation: Codable {
        let name: String
        let address: String
        let latitude:CLLocationDegrees
        let longitude: CLLocationDegrees
    
        init(name: String,address: String, latitude: CLLocationDegrees,longitude: CLLocationDegrees) {
            self.name = name
            self.address = address
            self.latitude = latitude
            self.longitude = longitude
        }
    }
    
    

    NOTE: Your Custom type should conform to Codable protocol so that it can be encoded and decoded to and from Custom Plist file.

    NEXT:- In your ViewController you can populate an array of this type and use it to save as well as retrieve data from your customPList file. I have summarized everything in a ViewController with Comments as shown below. Let me know if you don't understand any part of it.

    class CustomPListViewController: UIViewController {
    
        /// Make your array of Favorite Locations
        let favLocationArray:[FavoriteLocation] = []
    
       /**
          Create our own directory to save data locally in a custom Plist file, inside our apps Document directory.
          This is used both when writing and retrieving.
         */
        let dataFilePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("FavoriteLocations.plist")
    
        /// Save your array in custom PList file (Encode)
        func saveFavoritePLacesInCustomPList(favLocationArray: [FavoriteLocation]) {
            let encoder = PropertyListEncoder()
            do {
                let data = try encoder.encode(favLocationArray)
                guard let dataFilePath = self.dataFilePath else { return }
                do {
                    try data.write(to: dataFilePath)
                } catch {
                    // Handle error
                }
            } catch {
                // Handle error
            }
    
        }
    
    
        // Retrieve your saved data (Decode)
        func retrieveFavoriteLocations() {
            guard let filePath = self.dataFilePath else {return }
            do {
                let data = try Data(contentsOf: filePath)
                let decoder = PropertyListDecoder()
                do {
                    let favoriteLocationsArray = try decoder.decode(Array<FavoriteLocation>.self, from: data)
                    // This is your data ready to use
                    print(favoriteLocationsArray)
    
                } catch  {
                    // Handle error
                }
    
            } catch  {
               // Handle error
            }
    
        }
    
    
    
    }
    
    
    
    
    

    How does it work?

    We are using PropertyListEncoder to encode ie to transform your custom object into a format which can be saved in .plist file. and to get your data back to your app's model (FavoriteLocation) we use PropertyListDecoder which does the opposite ie decode .plist formatted data back to your custom Object.

    How to view your custom .plist file?

    On your viewDidLoad method call print(dataFilePath!) This will print the full file path which you can naviagate to it and see your FavoriteLocations.plist