Store UIColor with UserDefaults in Swift 3

    Bob Lee
    Bob Lee
    Share
    This article was originally published at iOS Geek Community.

    So, what the heck is UserDefaults in the first place? Why is the name so ugly? Why are we using it? and Why am I writing about it? If you can give at least one answer to these questions, you may skip to Part 2 where I talk about UIColor.

    Prerequisite: Understand Type Casting from the bottom of your heart. In other words, be able to distinguish between as, as!, as? You can start off with this video where I show my face and speak English on YouTube.

    Stage One: Analogy

    As a tradition, let’s start off some funky and tangible ways to understand UserDefaults at an extremely high level. Actually, this is too simple. I don’t think it’s necessary. The UserDefaults object saves user data. So that when you first download an app, you can save preferences such as a background color/image even when the battery kills itself. It can save ALL kinds of things. If you have 254GB of free space on your phone, it can save 254GB of user data. But, there is a big problem.

    It regurgitates everything during the runtime. Okay, the previous sentence can be a bit ambiguous. Let’s try this. It will vomit everything out when you first launch an app or the view is loaded. Hmm, 🤔. Here is the better way. It’s like you running to the bathroom and taking a poop that you’ve been holding for 5 days at once. What happens to your body? You get overwhelmed. It may not even come out right, and most importantly, it will hurt you real bad. Same thing, you want to make sure you only carry enough of poop inside of the large intestine so that you (iPhone) can take care of and handle like a boss.

    LOL. Yeah, for those newcomers, this is how I think and execute. Excuse me. No one can stop me.

    Sure, there is another way to go about in order to solve this serious problem both for you and the phone. We can use CoreData. You’ve probably heard it before. I will publish one on this Saturday 8am. Stay tuned.

    Stage Two: Let’s Get Real

    We’ve had enough of fun (at least for me). It’s time to dive in. As usual, we have to create an instance/object of UserDefaults like this

    let defaults = UserDefaults.standard
    Now, the object is able to take intake a bunch of value, to be exact, a bunch of `poop` and each `poop` has its own name. For example, let’s try to store an `Int` value.
    defaults.set(20, forKey: "myAge")
    I think the poop analogy is a little bit too disturbing, so let’s change the analogy to a cute hamster like below.

    Okay, so, it should look something like this,

    class ViewController: UIViewController {
     let defaults = UserDefaults.standard
     override func viewDidLoad() {
      super.viewDidLoad()
    
      defaults.set(20, forKey: "myAge")
      print(defaults.integer(forKey: "myAge"))
     }
    }

    Yes, you may change the designated value of myAge of the app if your daddy wants to change the value.

    defaults.set(47, forKey: "myAge")

    That’s it. You’ve written just two lines of code to save data offline with UserDefaults. You can save things like gender, blood type, height, weight, show size, game level. But, of course, this isn’t the end of the story. Just like hamsters/humans, they can’t eat all kinds of things like rock and diamond. You don’t feed rice to a 15 day year old. Do you? (Dear, Apple UIKit engineers, why can’t we…?)

    // You can only feed these
    func set(Bool, forKey: String)
    func set(Float, forKey: String)
    func set(Int, forKey: String)
    func set(Any?, forKey: String)
    func set(Double, forKey: String)
    func set(URL?, forKey: String)

    But, surprisingly, you can vomit all kinds of things. I don’t know why. I guess it has to do with how our body kinda works like that.

    // Catharsis
    func array(forKey: String)
    func bool(forKey: String)
    func data(forKey: String)
    func dictionary(forKey: String)
    func float(forKey: String)
    func integer(forKey: String)
    func object(forKey: String)
    func stringArray(forKey: String)
    func string(forKey: String)
    func double(forKey: String)
    func url(forKey: String)
    func value(forKey: String)

    Another problem, how about storing/retrieving Dictionary? There isn’t a direct method to set a dictionary value. But, of course, there are two ways.

    First Method

    let name = ["Real": "SangJoon Lee"]
    defaults.set(name, forKey: "name") // Store as Any?
    if let name = defaults.value(forKey: "name") as? [String: String] {
     print(name) // Downcast from Any? to [String: String]
    }
    // ["Real": "SangJoon Lee"]

    Second Method

    if let name = defaults.dictionary(forKey: "name") as? [String: String] {
     print(name) // Downcast from [String : Any]? to [String: String]
    }
    
    // ["Real": "SangJoon Lee"]

    Okay, great. But, my dad suddenly wants to save the background color to pink… Pink? You mean UIColor? That’s right. Here comes UIColor 😶 It’s time to get pretty serious. Brace yourself.

    Part 2: UIColor

    You’ve probably noticed that there isn’t a straight path to deal with it. We have to manipulate the type a little. For example,

    We are going to turn UIColor into Data and save it as Any?. Sounds good? I know it sounds ridiculous. I will explain as we go along.

    In order to convert from UIColor to Data, we have to rely on a special encoder, NSKeyedArchiver. Imagine this class is like a blacksmith who makes swords and shields from rocks and raw metals. Of course, he/she can convert back and forth.

    For more information, refer to the Apple’s API: NSKeyedArchiver

    NSKeyedArchiver, a concrete subclass of NSCoder, provides a way to encode objects (and scalar values) into an architecture-independent format that can be stored in a file. — Apple.

    So, the code looks like this.

    extension UserDefaults {
     func setColor(color: UIColor?, forKey key: String) {
     var colorData: NSData?
     if let color = color {
      colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
     }
     set(colorData, forKey: key)// UserDefault Built-in Method into Any?
     }
    }

    Now, we’ve converted UIColor to Data and we saved it as Any? because that’s the only option available. Just let you know, Any is like the grand grand super class of pretty much everything.

    Okay, it’s time to vomit out the “color” we’ve recently saved. So, since we archievedData into humanly incomprehensible stuff, it’s time to unarcheive. But, the funny thing is, you can vomit outdata? instead of Any? We could technically vomit out Any? and convert it to data and then UIColor? but let’s keep it simple. Just data? to UIColor?

    extension UserDefaults {
     func colorForKey(key: String) -> UIColor? {
      var color: UIColor?
      if let colorData = data(forKey: key) {
       color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
      }
      return color
     }
    
     func setColor(color: UIColor?, forKey key: String) {
      var colorData: NSData?
       if let color = color {
        colorData = NSKeyedArchiver.archivedData(withRootObject: color) as NSData?
      }
      set(colorData, forKey: key)
     }
    
    }

    Now, we can do this,

    defaults.setColor(color: UIColor.red, forKey: "myColor") // set
    let myColor = defaults.colorForKey(key: "myColor") // get

    Awesome? That’s it.

    Source Code
    Github

    Andyy Hope wrote an amazing article on how to take UserDefaults to the next level using Protocol. I learn alot from him, and I guarantee he is way smarter than I’m. Check his article out. Swift: UserDefaults Protocol You won’t regret.

    Frequently Asked Questions (FAQs) about Storing UIColor with UserDefaults in Swift 3

    How can I save UIColor in UserDefaults in Swift 3?

    Saving UIColor in UserDefaults in Swift 3 involves converting the UIColor to NSData and then storing it in UserDefaults. Here’s a simple code snippet to illustrate this:

    let color = UIColor.red
    let colorData = NSKeyedArchiver.archivedData(withRootObject: color)
    UserDefaults.standard.set(colorData, forKey: "myColor")
    In this code, we first define a UIColor (in this case, red), then convert it to NSData using NSKeyedArchiver. Finally, we store this data in UserDefaults with a key of “myColor”.

    How can I retrieve a UIColor from UserDefaults in Swift 3?

    Retrieving a UIColor from UserDefaults involves getting the NSData from UserDefaults and then converting it back to UIColor. Here’s how you can do this:

    if let colorData = UserDefaults.standard.data(forKey: "myColor") {
    let color = NSKeyedUnarchiver.unarchiveObject(with: colorData) as? UIColor
    }
    In this code, we first get the NSData from UserDefaults using the key “myColor”. We then convert this data back to UIColor using NSKeyedUnarchiver.

    What is UserDefaults in Swift 3?

    UserDefaults is a simple, lightweight mechanism for persisting key-value pairs between app launches in Swift. It’s typically used for storing simple data like user preferences, settings, and other small amounts of data that don’t require a full-blown database.

    Why should I use UserDefaults to store UIColor?

    UserDefaults is a convenient and efficient way to store small amounts of data like UIColor. It allows you to persist data between app launches, which is useful for maintaining user preferences and settings. However, it’s not suitable for storing large amounts of data or complex data structures.

    Can I store other types of data in UserDefaults?

    Yes, UserDefaults can store a variety of data types, including strings, numbers, dates, arrays, and even custom objects. However, custom objects must be converted to NSData before they can be stored in UserDefaults.

    Is UserDefaults secure for storing sensitive data?

    UserDefaults is not encrypted, so it’s not suitable for storing sensitive data like passwords or credit card numbers. For storing sensitive data, consider using Keychain, which provides secure, encrypted storage.

    Can I store multiple UIColors in UserDefaults?

    Yes, you can store multiple UIColors in UserDefaults. Each UIColor should be converted to NSData and stored with a unique key.

    What happens if I try to retrieve a UIColor from UserDefaults that doesn’t exist?

    If you try to retrieve a UIColor from UserDefaults using a key that doesn’t exist, UserDefaults will return nil. You should always check for nil when retrieving data from UserDefaults to avoid unexpected behavior.

    Can I remove a UIColor from UserDefaults?

    Yes, you can remove a UIColor from UserDefaults by calling the removeObject(forKey:) method with the key of the color you want to remove.

    Can I use UserDefaults to store UIColor in Swift versions other than Swift 3?

    Yes, the process for storing UIColor in UserDefaults is similar in all versions of Swift. However, the syntax may vary slightly between different Swift versions.