UserDefaults is one of the good and handy storage you can use. You can use UserDefaults
to store any basic data type for as long as the app is installed. For example, you can use basic types as Bool
, Float
, Double
, Int
, String
, or URL
, but you can also write more complex types such as arrays, dictionaries and Date
– and even Data
values.
Advantage: After writing data to UserDefaults, when you run the app the UserDefaults data gets automatically loaded. Its very easy to use it.
Disadvantage: Its a bad idea to use UserDefaults to save lot of data as it slows down your app loading and increases app load time.
You can use it making an instance of the UserDefaults like below:
let defaults = UserDefaults.standard
Now you can save your data like below:
defaults.set(26, forKey: "Age")
defaults.set(true, forKey: “is_iOS_Best”)
defaults.set(CGFloat.pi, forKey: “Pi”)
defaults.set(Date(), forKey: "CurrentDate")
let myArray = ["iOS", "macOS"]
defaults.set(myArray, forKey: "SavedArray")
let myDict = ["Name": "Sagar", "Hobby": "Coding"]
defaults.set(dict, forKey: "SavedDict")
You can get the stored values/data back using below methods:
integer(forKey:)
returns an integer if the key existed, or 0 if not.bool(forKey:)
returns a boolean if the key existed, or false if not.float(forKey:)
returns a float if the key existed, or 0.0 if not.double(forKey:)
returns a double if the key existed, or 0.0 if not.object(forKey:)
returnsAny?
so you need to conditionally typecast it to your data type.You can save a complex kind of data too in UserDefaults using NSCoding and Codable protocols.
NSCoding is available for both Objective-C and Swift developers but Codable is only for Swift developers.We will see the usage of first one : NSCoding
NSCoding
Your class must confirm to NSCoding before using it, like you use other protocols : UITableViewDataSource and UITableViewDataDelegate.
Then you can implement it using below method which changes the object graph to Data, so that it becomes able to save in UserDefaults.
archivedData(withRootObject:)
method ofNSKeyedArchiver
There are many Apple’s own classes that support NSCoding like:
UIColor
,UIImage
,UIView
,UILabel
,UIImageView
,UITableView
,SKSpriteNode
but your custom classes don’t support it by default.
You can also refer HackingWithSwift blog for free and good coding tutorials.
Let us have a look at below example for its use.
For example:
Consider your custom class is MySelf in which you are using name and image properties.
import UIKit
class MySelf: NSObject, NSCoding { //Here NSObject is required to use NSCoding.
func encode(with aCoder: NSCoder) { // this method is required and used to encode data
aCoder.encode(name, forKey: “name“)
aCoder.encode(image, forKey: “image“)
}
required init?(coder aDecoder: NSCoder) { // this method is required and used to decode data
name = aDecoder.decodeObject(forKey: “name“) as! String
image = aDecoder.decodeObject(forKey: “image“) as! String
}
var name: Stringvar image: String
init(name: String, image: String) {self.name = name
self.image = image
}
}
Now in your ViewController class you can use it like below
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDataSource {
var people = [MySelf]() // array of MySelf type data
@IBOutlet weak var myCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
if let savedPeople = defaults.object(forKey: “people“) as? Data {
// Get saved values using below method
people = NSKeyedUnarchiver.unarchiveObject(with: savedPeople) as! [MySelf] }}
@IBAction func barButton_addPersonTapped(_ sender: Any) {
addPerson()
}
func addPerson() { // Show ImagePickerController
let imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
present(imagePicker, animated: true) {
// completion block
}
}
func getDocumentsDirectory() -> URL { // // Get DocumentsDirectory
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
// UICollectionViewController Delegates
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {return people.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: “PersonCollectionViewCell“, for: indexPath) as! PersonCollectionViewCell
let aPerson = people[indexPath.item]
cell.name.text = aPerson.name
let path = getDocumentsDirectory().appendingPathComponent(aPerson.image)
cell.imageView.image = UIImage(contentsOfFile: path.path)
cell.imageView.layer.cornerRadius = 3
cell.imageView.layer.borderWidth = 2
cell.imageView.layer.borderColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.3).cgColor
cell.layer.cornerRadius = 7
return cell
}
// UIImagePickerController Delegates
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard let image = info[UIImagePickerControllerEditedImage] as? UIImage else { return }
let imageName = UUID().uuidString
let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
if let jpegData = UIImageJPEGRepresentation(image, 80) {
try? jpegData.write(to: imagePath) // Write your image jpeg data to imagePath
}
let aPerson = MySelf(name: “Img“, image: imageName)
people.append(aPerson)
myCollectionView.reloadData()
dismiss(animated: true) {
//
}
self.save() // Save newly added Image with name.
}
func save() { // Save image and name using archivedData method of NSKeyedArchiverlet saveData = NSKeyedArchiver.archivedData(withRootObject: people)
let defaults = UserDefaults.standard
defaults.set(saveData, forKey: “people“)
}
}
Your custom collectionViewCell class: PersonCollectionViewCell
import UIKit
class PersonCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var name: UILabel!
}
Have fun… 🙂
I will show the usage of Codable in my next post.
Let me know if you have any queries or problems implementing this feature. Enjoy … 🙂