sgmunn

Mostly MonoTouch with a chance of Other Stuff.

CoreData in MonoTouch

I have recently been learning Objective-C and iOS and have had some success. One of the things I wanted to try when starting off with iOS was to see how well MonoTouch would work for me, and I have had some success with that as well. Where I haven’t had much luck is with the CoreData bindings that were introduced in version 3.0.12.

The biggest issue for me was the complete lack of any samples, if you do know of any please let me know.

This post will be about my initial discovery of how you can use CoreData with MonoTouch.

Create the Model

The first thing I determined was that it appears that you will need to create your object model in code and cannot rely on the model editor in Xcode to do any of the boiler plate code for you. In order to use the .xcdatamodel that you might have in an Xcode project you need to use the compiled output, a .momd file that is built as part of the Xcode build process. Without knowing how to do this independently all you can do is build up the model in code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public NSManagedObjectModel CreateModel ()
{
  NSManagedObjectModel model = new NSManagedObjectModel();
  // create an entity description
  NSEntityDescription entity = new NSEntityDescription();
  entity.Name = "TestEntity";

  // create an attribute for the entity
  NSAttributeDescription description = new NSAttributeDescription();
  description.AttributeType = NSAttributeType.Integer32;
  description.Name = "SomeId";
  description.Optional = false;
  // assign the properties to the entity
  entity.Properties = new NSPropertyDescription[1] { description };

  // add the entity to the model, and then add a configuration that
  // contains the entities
  model.Entities = new NSEntityDescription[1] { entity };
  model.SetEntities (model.Entities, String.Empty);

  return model;
}

Two things to note here, the property EntitiesByName on the model object doesn’t appear to have been updated or isn’t bound correctly because it always returns an empty NSDictionary. Debugging similar code in Xcode reveals that this dictionary is updated, but we don’t appear to have access to it from MonoTouch. The other thing to note is that I didn’t have to set entity.ManagedObjectClassName to anything like I would have in Objective-C.

Create the Persistent Store Coordinator

The next bit of code required is a persistent store coordinator, we can do that quite easily.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public NSPersistentStoreCoordinator CreateStore()
{
  // get a location for the store
  string storeUrl = System.IO.Path.Combine(this.ApplicationDocumentsDirectory(), "test.sqlite");
  NSUrl url = new NSUrl (storeUrl, false);

  var store = new NSPersistentStoreCoordinator (this.CreateModel());

  NSError error;
  store.AddPersistentStoreWithType("SQLite", String.Empty, url, new NSDictionary(), out error);
  // ... test the error and report...

  return store;
}

In Objective-C examples, calls to AddPersistentStoreWithType often have nil as the configuration parameter, here we use String.Empty because MonoTouch will throw a null reference exception if we don’t. The configuration name that we use here has to match the configuration name that we gave in SetEntities when we created the model.

Now all we need is a context manager..

1
2
3
4
5
6
7
public NSManagedObjectContext GetContext()
{
  NSManagedObjectContext context = new NSManagedObjectContext();
  context.PersistentStoreCoordinator = this.CreateStore();

  return this.context;
}

This is the basic setup we need to be able to use CoreData, the next steps we need are a way to create objects and get them out again.

Inserting Objects into the Context

At first I tried to copy the methods in Objective-C examples by using

1
NSEntityDescription.InsertNewObjectForEntityForName ("TestEntity", context);

but there appears to be issues with the bindings for that method. In MonoTouch, the return type for InsertNewObjectForEntityForName returns an NSEntityDescription but this method creates an NSManagedObject which results in invalid cast exceptions. I had to create instances of NSManagedObject directly. Creating an NSManagedObject requires the NSEntityDescription for the entity you want created and the NSManagedContext into which the object will be placed. I discovered that the model didn’t appear to have anything in the EntitiesByName dictionary, so we can’t use that. So I use something like

1
2
3
NSEntityDescription entityDescription = this.context.PersistentStoreCoordinator.ManagedObjectModel.Entities
.Where (x => x.Name == "TestEntity").First ();
NSManagedObject newObject = new NSManagedObject(entityDescription, this.context);

Setting attribute values the object is done by using the SetValue method

1
2
IntPtr p = new NSNumber(1234).Handle;
newObject.SetValue (p, "SomeId");

Getting Objects Out

To retrieve our objects from the SQLite database we use an NSFetchRequest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NSError error;
// set up the request, fetch 20 at a time
NSFetchRequest request = new NSFetchRequest ();
request.Entity = entityDescription;
request.FetchLimit = 20;
// fetch each object one by one (as Faults = true), or a batch
request.ReturnsObjectsAsFaults = false;

// define what order you want them in
NSSortDescriptor sort = new NSSortDescriptor ("SomeId", true);
request.SortDescriptors = new NSSortDescriptor[1] { sort };

error = null;
NSObject[] retrievedObjects = this.context.ExecuteFetchRequest (request, out error);
// ... test error object ...         

NSManagedObject anObject = (NSManagedObject)retrievedObjects[0];

// get the value of the SomeId attribute
IntPtr p = anObject.ValueForKey ("SomeId");
int attributeValue = new NSNumber (p).Int32Value;

Now, there are some plenty of other things to learn before we can really use this, NSPredicate for filtering the data; retrieving the second batch of records, versioning and migration just to name a few that spring to mind, but I hope that this might serve as a starting point for anyone else who is new to iOS and MonoTouch like me.

Cheers.

Comments