• Frank Tyger

    Doing what you like is freedom. Liking what you do is happiness.

Sharing Data Between Apple Watch and iPhone in Swift

There are various ways to persist data in the Apple ecosystem. They range from simple to complex, depending on the type and amount of data saved. On the more complex end of the spectrum, you have SQLite for traditional relational data storage, Core Data for modern object persistance, and iCloud for device synchronization. In the middle, you have NSCoder and NSKeyedArchiver for serializing classes to local document files. And finally, you have the simplest type of storage, which is NSUserDefaults that allows you to store simple things like strings and numbers. In this post, I’d like to focus on NSUserDefaults and how to share it between devices.

The Scoop on NSUserDefaults

NSUserDefaults allows you save settings and properties related to application or user data, even if the user exits the app and comes back later. It is usually seen as the quick and dirty way to persist data. The reason I say “quick and dirty” is because it is dead simple, but sometimes easily abused. The types of data that can stored are the following:

  • Strings
  • Numbers
  • Booleans
  • Dates
  • Arrays
  • Dictionaries
  • NSData

As you can see, it is only simple data that can be stored in NSUserDefaults, with the exception of NSData which actually serializes data to get around the limitations, but even that has limitations. If you need to persist data any more complex than these, such as collections of custom objects or object relationships, better look at the other storage methods like Core Data.

Show Me the Code!!

To actually store data using NSUserDefaults, it is super simple:

let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(true, forKey: "isDarkModeEnabled")

let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(true, forKey: "isDarkModeEnabled")

That’s it!

Ok, but the post title says how to share data between Apple Watch or iPhone. The above code will only save the data to the local device.

Enter App Groups

App Groups were introduced in iOS8. It can be thought of as a container of data in the cloud that multiple apps and widgets can share. They are synchronized to the device so they become available offline, but sync up when online. Bless the souls who had to share data prior to iOS8.

To start using App Groups, you must enable it for each target in your Xcode project that will need to use it:

iOS-App-Groups-500x242

 

When you hit the “+” button, you will create your container ID that all targets and apps will use. An app group container ID begins with “group.” followed by a string in reverse DNS notation. An example is: group.com.example.My-App. Xcode will actually make the change to your App ID in your developer account automatically.

To use App Groups, it’s not that different or difficult to use than NSUserDefaults:

var userDefaults = NSUserDefaults(suiteName: "group.com.example.My-App")
userDefaults.setObject(true, forKey: "isDarkModeEnabled")
userDefaults.synchronize()

The only differences here are how NSUserDefaults is instantiated and calling synchronize at the end. You feed it the container ID to the constructor parameter called “suiteName”, then call “synchronize()”, and your data flies to the cloud for other apps and devices to consume.

Although I titled this post how to share data between Apple Watch and iPhone, this can be used with any device in the Apple ecosystem, including OS X and iPads.

Taking It to the Next Level

You can take this one step further by creating a class for your app and abstract the underlying storage for your properties. Here’s an example:

public class ConfigurationModel: NSObject {

  public static let storageKey = "group.com.example.My-App"
  public let userStorage = NSUserDefaults(suiteName: storageKey)
    
  public var isDarkModeEnabled: Bool {
    get {
      // Get setting from storage or default
      if userStorage?.objectForKey("isDarkModeEnabled") == nil {
        userStorage?.setObject(false, forKey: "isDarkModeEnabled")
        userStorage?.synchronize()
      }
        
      return userStorage?.objectForKey("isDarkModeEnabled")
  }

  set {
    // Set new value in storage
    userStorage?.setObject(newValue, forKey: "isDarkModeEnabled")
    userStorage?.synchronize()
  }
}

At the top of the class, I am declaring my group container ID and creating the NSUserDefault object out of it. Then my properties for the class have getters and setters to store the data to the App Group. If the key doesn’t exist, it creates it with a default value and synchronizes it. Using the class from this point forward is simple:

var configModel = ConfigurationModel()
configModel.isDarkModeEnabled = true

var configModel = ConfigurationModel()
configModel.isDarkModeEnabled = true

This property is stored in the cloud! Everything is abstracted away for you. You don’t have to be bothered about storing and synchronizing it into the App Group. It’s all done automatically for you in the getters and setters!

The true power of this becomes realized when you put this class in a Swift framework and start sharing it between targets and even projects.

Source BASEM EMARA

 

 

Drop a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.