Photo by Benh LIEU SONG, http://www.flickr.com/photos/blieusong/7234335792
It’s common for iOS applications to preserve their state when quitting, so that the next time the user launches the app, their previous session is restored. Apple reduced the development burden for this state restoration in iOS 6 with their State Preservation and Restoration APIs. Even with the APIs, though, each view controller is responsible for preserving its own state, and that includes any references to core data objects that are being displayed in a table or detail view.
Of course, the persistence of NSManagedObjects is handled by the core data stack, so the view controllers just need to store references that will allow them to refetch the objects during restoration. What’s a straight-forward way to implement encoding those references?
I saw Square’s PonyDebugger project on GitHub a while back, starred it, and made a mental note to try it out in my next iOS project. It’s simply fantastic — easy to setup and incredible to use. If you’re an iOS developer be sure to add this one to your debugging toolkit.
PonyDebugger is remote debugging toolset that uses a gateway server and the Chrome Developer Tools to allow you to inspect NSURLConnection based network activity, Core Data managed object contexts, and your view hierarchy remotely while your application is running.
Core Data provides a capable framework for connecting data to an iOS user interface. The framework doesn’t so much reduce code as it does abstract the SQLite interface in order to support advanced features. However, in doing so it masks certain implementation decisions that can result in inefficient, less-than-instant queries when a table reaches 5-10k records. Since these queries are often run on the main thread for easy integration with the UI
, they lead to unresponsive apps.
Solving these slowdowns requires understanding more about SQLite and Core Data’s connection to it. For both technologies, there are simple analytical tools and standard solutions for common optimization problems. (more…)
In order to store private data in an iOS Core Data database, there are several methods available for encryption, including:
However, neither of these options is sufficient for if you need to use multiple encryption keys, encrypt only certain attributes, or preserve decrypted data while the device is locked.
Encryption Transformer class
Instead, it’s fairly simple and straight-forward to perform lazy decryption on only certain database fields using the special Transformable Core Data attribute type. Transformable attributes are configured with an NSValueTransformer subclass that you write that specifies:
- a method for converting one object into another
- an optional method for reversing that process (and whether reversing is supported)
- the class of an object after transformation
Here’s an example class that converts from a decrypted NSData object to an encrypted one. It relies on a key method to get the encryption key, and a couple NSData category methods that perform AES-256 encryption and decryption.
The key method might get the secret key from any number of places, such as a password requested at login and stored in the app delegate. I’ll touch on the NSData category methods that actually perform the encryption in a little bit.
This encryption class can then easily be subclassed to handle different data types, like strings, dates, numbers, etc. Here’s an example string encryption class that converts between NSString and NSData in order to use its parent class transformation methods.
Once these classes are set up, the Core Data model editor lets you assign an entity attribute type of Transformable and a name of the NSValueTransformer class, such as StringEncryptionTransformer.
Core Data model editor in Xcode showing an entity’s Transformable attribute.
Now, these attributes can be written to in code just like any other (e.g.
clark.secretIdentity = @"superman"), but when they are persisted to the underlying SQLite database (or other sort of data store), the appropriate NSValueTransformer class will be called to encrypt the values before writing them to the data store.
Likewise, at the time a persisted object is read from the data store, the NSValueTransformer class will decrypt it. Encryption and decryption is thus lazy and only performed when an object is used or updated — no need to decrypt an entire file or database during app startup.
AES-256 encryption category
There are a number of example classes that make performing AES-256 encryption as simple as shown above, such as Jim Dovey’s NSData+CommonCrypto category and this unattributed snippet. The better ones use encryption libraries provided by Apple, which may (or may not, IANAL) mean that you don’t need a CCATS form for app submission.
However, one major caveat to encrypting attributes individually is that patterns from short, repeated values will naturally rise. If you are encrypting only a couple different possible values for an attribute (e.g. “superhero” and “evildoer”), simple encryption using the same key will result in only two encrypted values. That is, for the key “
top secret“, the value “
Superman” will always encrypt to “
?b64JzJ4aC0IhKaf7xeTWaglC6L/3VFxwA5XVrfDRntxebO4rFUSdNNrzfVFIU3y” (Javascrypt is a handy online tool for encryption).
Enter the initialization vector (IV), which like the salt for a password helps hide patterns by randomizing encryption input. The IV should be:
- a random number, for example the result of arc4random()
- different for every attribute, or at least every entity
- stored alongside the encrypted value, since the same IV used for encryption is necessary for decryption
- public, that is it need not (and should not) be encrypted
Unfortunately, many example code snippets that call CCCrypt (including one linked above) leave the initialization vector parameter as simply:
NULL /* initialization vector (optional) */,
Rather than follow suit, it’s simple to update our EncryptionTransformer to generate a IV and prepend it to the encrypted data:
Now, every time an attribute value is saved, its encrypted version includes an extra stage of randomization that prevents comparisons between different attributes encrypted with the same key.
Thus, with fairly minimal code that ensures resilient encryption, we have an easy way to store private information in Core Data attributes with as many different keys as necessary.