Skip to content

KFData 1.0 Migrations Guide

Kyle Fuller edited this page Dec 18, 2013 · 4 revisions

KFData 1.0 is the upcoming release of KFData which is in development. This release has many API-breaking changes with a completely new architecture. Since we use Semantic Versioning it's time to increment the major version.

This guide is provided to outline the changes and explain the new design and structure of KFData. This document is also a transition guide for applications which use an older version of KFData outlining any changes or steps you will need to do while upgrading to 1.0.

It is also recomended that you refer to the documentation which is available here.

KFDataStore

The data store has greatly changed. Instead of creating multiple child managed object context we instead maintain two single contexts. We have a background context and a context for the UI on the main thread. This allows you to perform background operations and allow them to get automatically merged between the two contexts. This greatly simplifies the code-base and can provide a far better implementation.

The new architecture allows you to pick a data store which works best for your app. There are four new stores. Please see the documentation. Normally you would simply call [KFDataStore standardLocalDataStore:nil] to get hold of a data store which persists to disk using SQLite.

As of 1.0, we have added a helper method to get hold of a data store which syncs to iCloud. [KFDataStore standardCloudDataStore:nil].

The new KFDataStore has two properties, one for the main thread managed object context, called managedObjectContext. The other is called backgroundMangedObjectContext and it's important that you do not keep objects alive outside of perform block on this context. This managed object context is often reset after the main context has saved so it's important to also save all work done inside the performBlock: (or use performWriteBlock:).

-- (NSManagedObjectContext *)managedObjectContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType;
+- (NSManagedObjectContext *)managedObjectContext;
+- (NSManagedObjectContext *)backgroundManagedObjectContext;

You will have to switch from using managedObjectContextWithConcurrencyType: to either managedObjectContext or backgroundManagedObjectContext.

KFObjectManager

Another significant change is the object manager, we have removed many categories and helper methods for the easier to maintain object manager.

The object manager is a mutable object which has many methods to retrieve and perform operations on managed objects. It is a tool for creating fetch requests and executing them. You can chain multiple methods together to easily perform operations.

Compatibility

To ease migration, we have provided a compatibility layer which can be installed using the pod KFData/Compatibility. For example, you can use:

pod 'KFData/Compatibility'

With this compatibility layer installed, all the old helper methods on NSManagedObject are still availible. This may be a useful component to ease upgrading, however ongoing use of it is discourage and you should try to upgrade to the new syntax.

After installing the pod, you will need to import the header:

#import <KFData/Compatibility/KFDataCompatibility.h>

Manager

Here is the basic header:

@interface KFObjectManager (Filtering)

- (KFObjectManager *)filter:(NSPredicate *)predicate;
- (KFObjectManager *)exclude:(NSPredicate *)predicate;

@end

@interface KFObjectManager (Sorting)

- (KFObjectManager *)orderBy:(NSArray *)sortDescriptors;

@end

@interface KFObjectManager (Operations)

- (NSFetchRequest *)fetchRequest;
- (NSUInteger)count:(NSError **)error;
- (NSArray *)array:(NSError **)error;
- (NSSet *)set:(NSError **)error;
- (NSOrderedSet *)orderedSet:(NSError **)error;

#pragma mark - Enumeration

- (void)enumerateObjects:(void (^)(NSManagedObject *object, NSUInteger index, BOOL *stop))block error:(NSError **)error;
- (void)each:(void (^)(NSManagedObject *managedObject))block error:(NSError **)error;

#pragma mark - Deletion

- (NSUInteger)deleteObjects:(NSError **)error;

@end

Please see the full documentation for more detailed information about these methods.

As you can see, you can filter the manager, and sort it using these methods. Once you have filtered your manager, you can then perform operations to execute it.

Single objects

You can lookup single objects using the following:

NSError *error;
Episode *episode = [[[Episode managerWithManagedObjectContext:managedObjectContext] filter:predicate] firstObject:&error];

Before you would have to do something like:

Episode *episode = [Episode findSingleWithPredicate:predicate inManagedObjectContext:managedObjectContext];

Which silently discards any error. The main reason behind this change is that there were an awful lot of variants for each method. For example, there were two findSingle variants, another three for findLast and then another three for findFirst. The new API has a single method for each, because you can chain the commands together to first filter the data and then perform the operation.

Deletion

Deleting objects would now use the following syntax:

[[[File managerWithManagedObjectContext:managedObjectContext] filter:removalPredicate] deleteObjects:&error];

Instead of:

[File removeAllInManagedObjectContext:managedObjectContext withPredicate:removalPredicate];

Iteration

You can iterate directly over the object manager, because it conforms to the NSFastEnumeration protocol.

KFObjectManager *manager = [Person managedInManagedObjectContext:context];

for (Person *person in manager) {
    NSLog(@"- %@", [person name]);
}

KFObjectManager is completely lazy, so it won't actually perform any operations until the final action.

You can take a look at the implementation of our compatibility layer here which shows you how to perform any of the old operation using the new API.

UI

Both KFDataTableViewController and KFDataCollectionViewController have been refactored to split out the data source into a separate class. This has resulted in a newer API.

These classes do not take a managed object context, instead you can set the fetch request when you want to set it. This would normally be done in viewDidLoad. You can implement the normal cell for row/item at index path methods and use [[self dataStore] objectAtIndexPath:] to get hold of the mangaed object.

KFDataTableViewController

By default, the new data source implements deletion for your table rows. You can disable this by setting the style as none for editing on your table view delegate:

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewCellEditingStyleNone;
}

If you are using data source methods on the view controller, you will need to update this code to ask the table view or the data source directly.

-[self tableView:tableView numberOfRowsInSection:section];
+[tableView numberOfRowsInSection:section];

KFAttribute

KFAttribute is a completely new piece of functionality, so it won't be covered in this guide. However it is a great addition to KFData and is certainly worth checking out. It allows you to easily create predicates without using hard-coded strings as key-value paths.