Search
Rich's Mad Rants
Powered by Squarespace

Entries in iCloud (9)

Thursday
Aug292013

Just a quick update...

Summer is always an awkward time for me. I get so wrapped up with the new iOS SDK, but I cannot actually write about any of it yet. Nor can I really present the new information in my classes. It's frustrating, but also exciting. There are some cool things coming.

In the meantime, I continue to work away at the second edition of my book. It's shaping up well, and has a lot of great new information about iOS 7 in it. 

I will also be running a pre-conference workshop at MacTech Conf on implementing iCloud. This is a full-day, hands on workshop. We will take a sample project and implement all three types of iCloud syncing: key-value syncing, document syncing and even Core Data syncing. We will be looking at the strenghts and weaknesses of each approach, and dig into complex topics, like conflict resolution and duplicate entries.

More importantly, I will cover all the new tools and improved APIs for iOS 7. Unfortunatley, I can't say much about them yet--except to reassure everyone. Apple is continuing to advance and improve their iCloud support--and the changes they've made will make life much easier for everyone.

There are still seats available, if you're interesting in coming. The workshop will be on November 5th, at the Manhattan Beach Marriott, Los Angeles, CA. You can find more information at the MacTech Conference web site. Please spread the word to anyone who might be interested.

Friday
Aug022013

MacTech Conference 2013

Hey all,

Just wanted to let everyone know that MacTech Conf has their Super Early Bird pricing until August 15th.

I'll  be speaking again this year. Actually I'll be speaking and running a pre-conference workshop.

I'm running the Adding iCloud Syncing to your App lab. It will be a hands-on lab, where we will implement various types of iCloud syncing: Key-Value syncing, Document syncing and even the dreaded iCloud syncing. I'm putting the new iOS 7 beta through it's paces as I work on the second edition of my book--so I should have lots of new information to share.

I'll also be doing a presentation on Sprite Kit. I feel like this is really bringing everything full circle. I wrote an article on using Cocos 2D for MacTech Magazine several years ago. That article lead to my first freelance gig. Which lead to me switching to full-time iOS development. Which lead to my book. All of which lead to me talking about 2D game engines again.

I hope to see you there.

 

Friday
Mar302012

Partial Solution for iCloud & Core Data Ubiquitous Update bug

Ok, I have a partial work around for the iCloud and Core Data bug.

First, a little background info. Everything here is based on my previous multi-doc iCloud example. As I mentioned in that post, there seems to be a race condition. If I have the device running on two apps, and I make a change on one app, every once in a while the second app will receive a  NSPersistentStoreDidImportUbiquitousContentChangesNotification notification, but when I try to update the data, nothing happens. It seems like the notification is fired before the managed object contexts  or persistent store coordinator have been properly updated.

Things seemed a little more solid with iOS 5.1. Now, at least, if I closed the document and reopened it, it would fetch the correct information. But it still wasn't perfect.

In the original project, when I received a notification, my app updated the managed object context as shown below:

// This should work, but seems to create race conditions

[moc performBlock:^{

 

[moc mergeChangesFromContextDidSaveNotification:note];

 

self.documentTitle.text = self.textEntry.title;

self.text.text = self.textEntry.text;

}];

In playing around, I discovered that if you reset both the parent context and the current managed object context, then reload your objects, you will actually get the correct value.

[moc performBlock:^{

 

id objectid = self.textEntry.objectID;

 

id parent = [moc parentContext];

[parent performBlockAndWait:^{

 

[parent reset];

}];

 

[moc reset];

self.textEntry = (TextEntry*) [moc objectWithID:objectid];

 

self.documentTitle.text = self.textEntry.title;

self.text.text = self.textEntry.text;

}];

 

Here, I'm just saving the object ID for my entry. Next, I clear both managed object contexts. Then I re-fetch the object for the ID and update my user interface. This works around the race condition, but you will lose any unsaved data in your managed object context--which is less than ideal. For the current app, this doesn't matter. But, for many applications, you may have to get the object IDs out of the notification and perform your own data merging.

In my tests, this has proven to be very reliable--depending, of course, on the quality of your internet connection. Updates occurred almost instantly when working at home. When testing it at Starbucks, I'd usually have to wait a minute or so for the update to propagate. So be patient when testing. I like to set a breakpoint in my update code that plays a sound then continues execution, just to alert me when the update is finally triggered.

However, I've now noticed another bug. If I make changes on both devices at the same time, then I can see that I trigger two updates--but neither device actually changes. They both keep their original data. This is true even if I shut down the apps and restart them. Of course, that's a pretty artificial test--so I think I'm OK ignoring that one. If you're making simultaneous changes on your devices, you kind of deserve whatever chaos you create.

Anyway, I know this is an imperfect solution, but I post it in the hope that it will inspire someone, and that we may be able to find a better solution to this problem.

Thanks,

-Rich-

Wednesday
Mar282012

Debug settings for Core Data and iCloud

I'm still trying to get Core Data and iCloud to work better together. Things seem more stable under iOS 5.1, but I'm still having an issue with a device occasionally receiving update notifications, but the new data doesn't appear to be available yet. However, when I shutdown and relaunch the application, it's then in the correct state (so, that's at least something).

I found an interesting tool for generating tons of debug information while working with iCloud and Core Data (thanks to @drewmccormack and @dlpasco on twitter). Just set the -com.apple.coredata.ubiquity.logLevel 3 launch argument.

In Xcode 4, click on the scheme and select Edit Scheme… in the drop down menu.

 

Editscheme

 

Now, select the Run action, and the Arguments tab. Then add "-com.apple.coredata.ubiquity.logLevel 3" as an argument.

 

AddRunArguments

 

Next time you run the app, it will generate a ton of data. Too much data, actually. I'm still trying to work through it in my own debugging. A lot of it doesn't make sense yet, but there are some interesting bits there. Like the following breakdown of all the files in my ubiquity container:

 

2012-03-28 16:40:29.428 MultiDocument[6616:3317] -[PFUbiquityImportScanOperation main](144): CoreData: Ubiquity:  <PFUbiquityImportScanOperation: 0x2715c0> got subpaths of root location: (

".baseline",

".baseline/TestDocument1",

".baseline/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

".baseline/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.zip",

".baseline/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/staging.nosync",

".baseline/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/staging.nosync/persistentStore",

".baseline/TestDocument2",

".baseline/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

".baseline/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.zip",

".baseline/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/staging.nosync",

".baseline/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/staging.nosync/persistentStore",

".baseline/current.nosync",

".baseline/current.nosync/TestDocument1",

".baseline/current.nosync/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

".baseline/current.nosync/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.gcmodel",

".baseline/current.nosync/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.meta",

".baseline/current.nosync/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.model",

".baseline/current.nosync/TestDocument2",

".baseline/current.nosync/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

".baseline/current.nosync/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.gcmodel",

".baseline/current.nosync/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.meta",

".baseline/current.nosync/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/baseline.model",

".cdmetadata",

".cdmetadata/metadata.nosync",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/4450B5BF-99DD-4134-898C-976F968ED329.cid",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument2",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument2/F2601224-6FC8-421B-83DD-30A6D91ECC6A.cid",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/metadata.store",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/metadata.store-shm",

".cdmetadata/metadata.nosync/mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/metadata.store-wal",

TestDocument1,

"TestDocument1/DocumentMetadata.plist",

"TestDocument1/StoreContent.nosync",

"TestDocument1/StoreContent.nosync/persistentStore",

TestDocument2,

"TestDocument2/DocumentMetadata.plist",

"TestDocument2/StoreContent.nosync",

"TestDocument2/StoreContent.nosync/persistentStore",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/001504B6-2D01-47DD-AE73-B803C8035260.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/20BFF411-6568-44B2-9DCB-168042D5CE1F.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/31C82DB6-BDF1-4E5B-903A-BCECFD9B4A1C.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/33D9CDFE-EBE7-4680-AC3F-920D4B9E1187.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/36E0F1D7-133B-4BB8-9F98-2A15C41F9559.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/38D95E15-E2A7-4415-9075-E3CE20761466.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/38DC2357-91AE-438D-BF14-31F64A737967.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/396FF0A4-23AD-4166-956B-6F28ABDF4CBA.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/6B0D9BF8-38ED-434F-A973-759E646E82CE.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/9965D68D-DE76-47C0-B005-9F17C649B962.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/9CE07122-E648-4223-BB9D-E5BDEBE8D5F3.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/9D53C5E4-049F-4750-8BB4-1005C6A2027F.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/A0048882-80D0-4125-A99B-295875B972F6.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/EA29AFD2-D3F5-41B4-B2A6-6174B24F9056.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/F1C0821B-F1C8-4F7D-BBE1-FDBD24A8EB8F.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/receipt.0.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument2",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/1A9E7FEF-EEDF-4CB8-BEAD-36E796EC60BA.1.cdt",

"mobile.3C7B62BB-61A2-57FF-8F35-78674A43F99A/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/receipt.0.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/.stage.nosync",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/.stage.nosync/TestDocument1",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/.stage.nosync/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/.stage.nosync/TestDocument2",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/.stage.nosync/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/008EDD62-DF18-4D05-A477-8D0BE6FBF521.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/0424A34D-6BEB-44BA-A87E-C9147FC04B3A.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/1605DC16-584B-425A-912A-6B28F83D8A4A.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/2C24C947-DA7F-4D74-A64A-6BA40EC4ECEF.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/381FFDB3-6EE5-48E1-BADF-9A66795F3D8B.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/54BD050D-D546-4BDE-A0C1-A69382841AFD.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/5E02B10A-300C-4870-8151-EAA9A3E843BD.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/73440C3D-D4A9-42AC-949A-4DF1B2C0503B.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/7888473C-3BC0-4165-9FE3-4995E6B43172.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/889C46AC-0780-41B4-859C-DD1D215CC400.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/A973A450-0AAE-48F9-8E8D-BC56E9341F81.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/B7036990-D33E-4F1B-9045-D7548BF282E2.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/C25E3570-B1FB-447A-BEC3-A6778C7CEE74.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/C6351ECD-D1D3-48BC-A17B-7FE480E14CE9.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/D69C37C3-43AE-4F4C-90B6-55FC6BDC805E.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/E259DC9D-FD8D-4CD7-8603-BECD99AEBE03.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/receipt.0.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument2",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/F0F13DE8-7CCA-4E29-BE27-0E04AAA11F23.1.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/TestDocument2/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A=/receipt.0.cdt",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/tempLogs.nosync",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/tempLogs.nosync/TestDocument1",

"mobile.D07EBC6E-03EA-5295-8D2F-0C1D2E737524/tempLogs.nosync/TestDocument1/WdKZGuhOiADrlyspq5GroEkGHfNbxImTR1BYSTCku1A="

)


And that just scratches the surface. So far, I haven't found anything that helps with my race condition problem. However, let me know if you use this setting, and if you find any useful signals inside all the noise.

-Rich-

Wednesday
Jan252012

Automatically Syncing User Defaults

OK, I did a quick proof of concept based partially on my discussion of iCloud's key/value storage with Saul on NSBrief, and partially on the previous NSBrief episode about metaprogramming. I thought it would be interesting to create a class that used metaprogramming to automatically set up NSUserDefaults syncing via iCloud.

Usage

Here's how it works. I created an abstract class called RWUserDefaultsManager. To use this class, simply create a concrete subclass and add dynamic properties for all the default values you wish to use.

In the sample code, I created a DefaultManager class with the following header:

#import "RWUserDefaultManager.h"

 

@interface DefaultManager : RWUserDefaultManager

 

@property (assign)NSString* name;

@property (assign)NSDate* date;

 

@end

These properties are then declared as @dynamic in the implementation file:

@implementation DefaultManager

 

@dynamic name;

@dynamic date;

 

@end

I can then access the shared DefaultManager anywhere in my code, and use these properties to get or set my user defaults.

 

- (IBAction)changed:(id)sender {

 

DefaultManager* defaultManager =

[DefaultManager sharedManager];

 

NSDate* date = [NSDate date];

 

self.dateLabel.text =

[NSDateFormatter localizedStringFromDate:date

dateStyle:NSDateFormatterMediumStyle

timeStyle:NSDateFormatterShortStyle];

 

defaultManager.date = date;

defaultManager.name = self.nameTextField.text;

}

The default manager will also post a RWUserDefaultsChangedNotification whenever the defaults change (e.g. from the Settings app or from an iCloud update). I simply listen to that notification and update my UI.

That's it. It's very simple to use. It forces me to explicitly declare my user defaults, and it transparently manages all the iCloud syncing.

Metaprogramming

The metaprogramming kicks in when the class is first loaded. It scans through all the properties looking for dynamic properties. Then it creates three methods for those properties, the getter, the setter and an update method (used to update the user defaults whenever the iCloud data changes).

I'm going to gloss over the metaprogramming, except to say that it uses class_copyPropertyList() to access the list of properties. property_getAttributes() and property_getNames() to get the information we need about the properties. imp_implementationWithBlock() to convert Objective-c blocks to IMP objects, and class_addMethod() to add the methods to our class. All of these methods are from the Objective-C Runtime. Check out the sample code to see how they are used (as always, if you'd like a longer post specifically on metaprogramming, say so in the comments--if I get enough interest, I'll see what I can do).

Currently it's limited to plist objects (NSData, NSString, NSNumber, NSDate, NSArray and NSDictionary). It doesn't do any type checking--so if you try to use a different object, it will cause a runtime error. It also supports most of the primitive C types (BOOL, char, int, long, long long, float and double).

I'd like to expand it to do some auto-boxing--for example, of commonly used structures or automatically converting objects that adopt NSCoding into NSData objects so they can be stored, but that's work for later on. Right now, you can implement your own auto-boxing by simply declaring a public property of the type you wish to store:

@property (assign)CGPoint center;

Then, in the implementation file, declare private properties to hold the actual data:

@interface DefaultManager ()

 

@property (assign)CGFloat centerX;

@property (assign)CGFloat centerY;

 

@end

Declare these properties as dynamic, as usual

@dynamic centerX;

@dynamic centerY;

Then implement custom getters and setters to read and load the values

-(void)setCenter:(CGPoint)center {

 

self.centerX = center.x;

self.centerY = center.y;

}

 

-(CGPoint)center {

 

return CGPointMake(self.centerX,

self.centerY);

}

Other Notes:

  • The library should work for either iOS or OS X Lion. However, I haven't tested it yet on Lion.
  • The sample code has a bit more repetition than I'm really comfortable with. I'd like to refactor it to get rid of the repetition and longer methods.
  • I've lightly tested the code but haven't done any robust testing yet. Please take a look, check it out and give me any feedback/suggestions/criticisms/whatever.
  • Remember, iCloud's key/value storage doesn't support conflict resolutions--the last change always wins. It also has rather strict memory limits. This code doesn't check to make sure you're staying under those limits.

Sample Code

Check out the sample code here.