skip to Main Content

I’m building a Shopify app, but first and foremost this is NOT a Shopify SDK question, it’s regarding saving a customer token into userDefaults or Keychain. I’ve tried but most Keychain libraries I’ve tried only work with strings and I need to save the token as a whole which looks like this:

BUYCustomerToken(customerID: NSNumber!, accessToken: String!, expiry: Date!)

The shared client of the app to access the token looks like this:

BUYClient.sharedClient.customerToken

During sign up, user gets created successfully with token. Once I exit and go back into the app the

BUYClient.sharedClient.customerToken = ""

So my question is, how do I save this object into defaults or Keychain to recall later when entering app. I can’t just save part of the token, like the accessToken which is just a String, I need the whole thing. Thanks in advance!

2

Answers


  1. You can save more than String types in UserDefaults, you have to store your token in multiple keys.

    https://developer.apple.com/reference/foundation/userdefaults

    The NSUserDefaults class provides convenience methods for accessing
    common types such as floats, doubles, integers, Booleans, and URLs. A
    default object must be a property list—that is, an instance of (or for
    collections, a combination of instances of): NSData, NSString,
    NSNumber, NSDate, NSArray, or NSDictionary.

    if you want to store the expiry date

    let expiry = UserDefaults.standard.object(forKey: "expiry") as? Date
    UserDefaults.standard.set(Date(), forKey: "expiry")
    

    The CustomerID

    let customerID = UserDefaults.standard.object(forKey: "customerID") as? NSNumber
    UserDefaults.standard.set(1234, forKey: "customerID")
    

    And for the token the same.

    NSUserDefaults.standardUserDefaults().setObject("YourAccessToken", forKey: "accessToken")
    
    let accessToken = NSUserDefaults.standardUserDefaults().stringForKey("accessToken")
    

    With this way, you just have to get them one by one, but you have all of them stored. I hope it helps you!

    Login or Signup to reply.
  2. You definitely can store the whole object in your UserDefaults or a Keychain, no problem. The trick is to make it conform to the NSCoding protocol, then use NSKeyedArchiver and NSKeyedUnarchiver to convert it to and from a Data object that can be stored. Don’t worry, it sounds more complicated than it is.

    Conforming to NSCoding

    Presumably your BUYCustomerToken class is already a descendant of NSObject, so you’re halfway there. You just need to add a couple of methods to allow your custom class to be encoded (turned into Data) and decoded automatically from an NSCoder. Specifically these are: encode(with coder: NSCoder) and init(coder aDecoder:NSCoder). You then use the coder’s various encode() and decode() functions to make these work. encode() and decode() work on any object that supports NSCoding, which includes NSNumber, String, and Date, so that makes your job here pretty easy. A finished codable class will look like this:

    class BUYCustomerToken : NSObject, NSCoding {
    
        var customerID: NSNumber!
        var accessToken: String!
        var expiry: Date!
    
        override init() {
        }
    
        convenience init(customerID: NSNumber, accessToken: String, expiry: Date) {
            self.init()
            self.customerID = customerID
            self.accessToken = accessToken
            self.expiry = expiry
        }
    
        required init(coder aDecoder: NSCoder) {
            customerID = aDecoder.decodeObject(forKey: "customerID") as! NSNumber
            accessToken = aDecoder.decodeObject(forKey: "accessToken") as! String
            expiry = aDecoder.decodeObject(forKey: "expiry") as! Date
        }
    
        func encode(with aCoder: NSCoder) {
            aCoder.encode(customerID, forKey: "customerID")
            aCoder.encode(accessToken, forKey: "accessToken")
            aCoder.encode(expiry, forKey: "expiry")
        }
    
        override var description: String {
            return "ID: (customerID!), Token: "(accessToken!)", Expires: (expiry!)"
        }
    }
    

    You can add in your own custom methods, of course. The description is not strictly necessary, but convenient for testing.

    Storing and Retrieving from UserDefaults

    Now that your class supports NSCoding, you can archive it and store it, then retrieve it.

    To store it, you start by archiving it (converting it to a Data object) using NSKeyedArchiver.archivedData(withRootObject). This works because your class is NSCoding compliant. Once you get a Data object, you can store that in UserDefaults using set(value, forKey).

    When you want to retrieve it, you do it the other way: first fetch a Data object from UserDefaults using data(forKey), then turn it back into an object using NSKeyedUnarchiver.unarchiveObject(with), and finally cast it to your custom class.

    Here’s a bit of code that attempts to load a BUYCustomerToken from UserDefaults. If it succeeds, it prints the description; if it fails, it creates a new token and stores it. Put this code in the viewDidLoad() of your initial UIViewController to see it in action:

    if let customerTokenData = UserDefaults.standard.data(forKey: "myToken") {
        let customerToken = NSKeyedUnarchiver.unarchiveObject(with: customerTokenData) as! BUYCustomerToken
        print(customerToken)
    } else {
        print("No token stored. Launch again to see token.")
        let customerToken = BUYCustomerToken(customerID: 999, accessToken: "some token", expiry: Date())
        let tokenData = NSKeyedArchiver.archivedData(withRootObject: customerToken)
        UserDefaults.standard.set(tokenData, forKey: "myToken")
    }
    

    The first time you run it there’s no stored token, so the output is:

    No token stored. Launch again to see token.

    The second time, it will find the stored token and output its description:

    ID: 999, Token: “some token”, Expires: 2017-05-31 21:27:25 +0000

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search