I lied, I love databases too much and core data is a weird one.

Strictly speaking this isn’t a post about core data, honest. It’s ACTUALLY a post about how iPhone applications are deployed and updated. My shipped application contained two data models, my new application also contains two data models. One of the new versions data models is an old one renamed and another is entirely new. For some ridiculous reason I thought if I deleted something from my applications bundle it would be similarly deleted on the iPhone. That’s not the case. Thus the application ends up with 4 data models. This causes problems as the code I previously posted, cribbed from elsewhere in part, will happily migrate backwards if it finds a suitable model. I’d not thought of this as a strict problem if I was in control of what models were present, it seems I am not though.

UPDATE: After seeing some discussions about this issue and people saying it’s how XCode pushes out the updates I tested a bit more. XCode only pushes out files that have changed to ensure a quick testing cycle, it does not currently delete files. To confirm this I built an ad-hoc ipa file and installed that on my phone with iTunes. The files are indeed deleted. Therefore this is potentially a bug with XCode, it should delete files that are no longer in the bundle even if it isn’t pushing out a full update like iTunes does. It can take shortcuts, sure it has to I mean imagine testing something like Inifinity Blade any other way?, but should really ape the real world as much as possible and therefore should delete files.
End of

Of course you shouldn’t delete data models from your application after it’s deployed as you cannot dictate that somebody will upgrade to each release of your app. If the data model really was deleted from your bundle the user would be stuck as the software could not open the database to migrate.

There’s an argument to be made there, I am sure, for not using Core Data on simple or incredibly dynamic projects but that could be one for another day.

I do like the code I have got though and will not be abandoning it as it’s lightweight in terms of memory. Much more so than the other options. The use of NSOperations pushed into a queue as the migrations procede gives it remarkable power and flexibility too. There is plenty that can be done inside the migration loop to ensure it proceeds forwards and not backwards.

Posted in Programming | Tagged , , | Comments Off on I lied, I love databases too much and core data is a weird one.

Another Core Data post. Last one for a while I expect.

It seems that when you create a mapping model for core data migrations using XCode (3 or 4) it will not create mapping models for abstract entities you could try here. If you do not create this manually within your mapping model any data contained therein will be lost. In my case I needed to tie the image data back to the entity it belonged to. I could create several entities each with the requisite attributes but it made more sense to create one abstract image entity and have the others inherit from it. This enabled me to have a recipeimage and stepimage that could have relationships back to their parent without having to create lots of attributes. RecipeImage has Image as a parent and has a defined relationship with Recipe etc. The downside is that after migration if you do not create the Image mapping manually all you images vanish from the database.
I cannot fathom a good reason for this mapping not being created automatically, under what circumstances would you not want the data contained in an abstract entity migrated when you update the schema? At least it was easy to fix.

Posted in Programming | Tagged , , | Comments Off on Another Core Data post. Last one for a while I expect.

Using Core Data’s automatic migrations to update data

It’s not brilliantly efficient but it does reuse existing importing code and it does work reliably and well.  If I update the app to include new recipes I only want to include them once.  When the app is first launched a preloaded Core Data sqlite file is copied into the Documents directory.  To provide new recipes to existing users I could have included them as a set of chefdoc files to import from the applications bundle but that would be wasteful as they’d already be in the preloaded file.  So, expanding a bit on the previous use of automatic migrations we can use an NSEntityMigrationPolicy just like before, it happens that I have added more information to the DB so I needed one anyway.  Alongside a routine much like the one used previously we can add the following routine:

- (BOOL)endEntityMapping:(NSEntityMapping *)mapping 
                 manager:(NSMigrationManager *)manager 
                   error:(NSError **)error {
    if (![super endEntityMapping:mapping manager:manager error:error]) return NO;

    [self addNewRecipes:manager];
    
    return YES;
}

Inside the addNewRecipes routine we first grab our own NSPersistentStoreCoordinator and NSManagedObjectContexts:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSError *error = nil;
    NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"Chefs_Book" ofType:@"momd"];
    NSURL *modelURL = [NSURL fileURLWithPath:modelPath];
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] 
                                                initWithContentsOfURL:modelURL];    
    
    NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
    NSString *initialDataPath = [[NSBundle mainBundle]
                                 pathForResource:@"InitialData" ofType:@"sqlite"];
    NSURL *initialStoreURL = [NSURL fileURLWithPath:initialDataPath];
    [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                                             configuration:nil 
                                                       URL:initialStoreURL 
                                                   options:nil 
                                                     error:&error];
    
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:persistentStoreCoordinator];

Don’t forget to drain the pool at the end of the above! We can then set about iterating through the Recipes. I simply build a list of unique ID’s from our preloaded db, compare it to the users database using the passed in NSMigrationManager to reference the destination context like so:

    NSArray *destRecipeUUIDs = [KookaDIS allRecipeUUID:[manager destinationContext]];

Once I’ve got a list of which ones are missing I convert them into the same format that’s used for emailing then use the same routines to import the data that are used when somebody opens a file in the email application or in safari. It’s not the most efficient as images are converted and base64 encoded and decoded etc. but it has two major advantages:

– The routines are proven and reliable.
– I only have one set of bulk import/export routines to maintain should my needs change in the future.

Having said that, the routines involved are recursive, data independent and obey a set of rules passed in to avoid looping recursion so not many data base schema changes will make it throw a fit.

That is the last big bit of code done for the update. To do now is the progress bar code for data updating/import, some polish (simple UI changes like hiding icons instead of deactivating them to fall more inline with apples UI recommendations) then test it for memory likes and finally do end to end tests on hardware. Probably another week so perhaps two weeks until the update is in the store.

Posted in Chef's Book, Programming | Tagged , , , | 2 Comments

Ownership of user created content.

I want to implement some concept of ownership/authorship in Chef’s Book.  Not for any reason of control but as a way of letting users swap recipes without data loss or unnecessary duplicates.  The problem is if I email a recipe to myself and load it on the iPad I get a new recipe.  That was fine for launch but now I want to keep the iPad copy up to date with changes I make on the iPhone (it has the camera!) so I don’t want duplicates.  That’s easy.. if the recipe exists on my iPad replace it.  That’s fine, and that’s what will happen, but what if I’ve sent it to Bob and Bob has made changes?  Bob would be pissed if he lost them when I sent him a new version with more milk and less photos.

To solve this each user needs to be identified and thus if Bob makes changes to a recipe he’s got then he becomes the owner of that recipe, in fact it becomes a new recipe.  This way if I send an updated version of my recipe to Bob he’ll get it as a new recipe, if Bob doesn’t make changes then his recipe will be updated if necessary.

So far, so simple.  Any change to a recipe that you do not own will make you the owner and generate a new unique identifier.  Any change to the title of a recipe you own will also generate a new unique identifier such that I can make chocolate cake and send it out, edit the dish to become a spanish omelette and send that out too.  Peoples chocolate cakes won’t change into spanish omelettes overnight then.

Good good… what about if I edit the recipe on my iPad and send it back to the iPhone?  Ah.. without a centralised system my iPad will grab ownership of the recipe after changes and make it a new recipe, sending it back to the iPhone will therefore import it anew.  That’s also fine, but not ideal.

Such centralised behaviour is something I want to do but it wasn’t something I planned to have to do with this update.  It was something that would be necessary for uploading recipes to a server for others to search and download.  It would allow them to receive updates to recipes they have downloaded but not altered.  Of course that behaviour isn’t cast in stone, I would probably store the ID’s of downloaded recipes against the users profile to notify them of updates.  That allows for some flexibility too, users can modify recipes to their hearts content and yet still get poked if a new version is available.  Which would then download as a fresh copy.

So one part of me has had a flippant and amusing idea.  If it’s possible to grab some unique info back from Game Center would that be a good thing to implement?  It’s not a game so how intrusive would it be?  If I felt truly silly I could bung in some achievements too.  Part of me wants to do that just because it’s daft and amusing.

It has to be practical though.  So I’ll have to check on a few things:

  • Is there an ID I can safely store from game center.
  • Do Apple mind if it’s used for non gaming applications?  I know of a To Do list dressed up as a game that uses it.
  • How unobtrusive can it be made?

Of course the ideal solution is to code something quickly that just provides the basics of authentication using OpenID.  That would get the foundations laid for the future which needs to be done anyway and would not exclude any flippant future use of game center.

Whilst I ponder this I’ll just implement the flow I’ve got so I can test it extensively.  One thing I want to check is the speed with which I can do a deep comparison of a large recipe incoming as a chefdoc with a present recipe NSManagedObject hierarchy.  I want to know how long it would take to determine whether even an image in a recipe has been modified.  For example someone taking an existing received recipe and updating it with their own photos.

Posted in Chef's Book, Programming | Tagged , , | Comments Off on Ownership of user created content.

Whilst the mac is away we play with android.

Seeing as my mac is temporarily indisposed seeing as it no longer charges from the magsafe connector I’ve been fiddling with android/eclipse getting a rudimentary chef book app running there. I must say the layout of interfaces comes as a bit of a shock after using IB. But it’s OK once you pretend you’re designing web pages in xml….

Case in point… I might have been wrong about there being margins but padding is there. If you add an image that’s say 32px wide you do this:

<ImageView android:id=”@+id/imgIcon”

android:layout_width=”32dp”

android:layout_height=”fill_parent”

android:src=”@drawable/logo” >

in your xml. You launch it and find that it’s all a bit cramped so you want to space it out by 10px. So you do this:

<ImageView android:id=”@+id/imgIcon”

android:layout_width=”32dp”

android:layout_height=”fill_parent”

android:src=”@drawable/logo”

android:padding=”10px”>

Right? Wrong…. as in html padding, unlike margins, are inside the view and thus take away from the width/height. So you end up with your logo being a paltry 12px, 10px removed by padding on each side. So you have to write this instead:

<ImageView android:id=”@+id/imgIcon”

android:layout_width=”52dp”

android:layout_height=”fill_parent”

android:src=”@drawable/logo”

android:padding=”10px”>

To layout a cell with an image on the left, centered and padded, followed by two rows of text on the right aligned vertically you need all this:

<?xml version=”1.0″ encoding=”utf-8″?><LinearLayout  xmlns:android=”http://schemas.android.com/apk/res/android”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:orientation=”horizontal”

android:gravity=”center_vertical”>

<ImageView android:id=”@+id/imgIcon”

android:layout_width=”52dp”

android:layout_height=”fill_parent”

android:src=”@drawable/logo”

android:padding=”10px”>

</ImageView>

<LinearLayout   xmlns:android=”http://schemas.android.com/apk/res/android”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:orientation=”vertical”

android:gravity=”center_vertical”>

<TextView android:id=”@+id/txtTitle”

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:gravity=”center_vertical”

android:focusable=”false”>

</TextView>

<TextView android:id=”@+id/txtDesc”

android:layout_width=”wrap_content”

android:layout_height=”wrap_content”

android:gravity=”center_vertical”

android:focusable=”false”>

</TextView>

</LinearLayout>

</LinearLayout>

It’s a lot of typing. Of course if you did this with some kind of gui all of your layouts would end up being done with the absolute layout manager. I have no idea if that’s considered inefficient. Thankfully a lot of the above can be cut and paste coding. Of course in the above the image is hard coded you’d need to remove the android:src=”@drawable/logo” line and populate the image in your code. It’s worth noting that wrap_content as an option for layout_height/width means make the container JUST big enough to encase the content. For the image view the image I chose is 200x200px so using wrap content when I only want it to be 32px would be wrong. My use of fill_parent for the height there is probably incorrect also but OK as the image is square and I’m forcing the width. It should probably be set manually like the width is.

Posted in Programming | Tagged , | 1 Comment

A small script hacked up to monitor directories.

This is something I knocked up a while ago just to keep an eye on a directory structure for permissions changes, excluding upload directories and other things:


#!/bin/bash

ignore[0]="./public_html/blog/wp-content/uploads"
ignore[1]="./bb-attachments"

cd 

unset unsecure i
i=0
while IFS= read -r -d $'\0' file; do
    unsecure[i++]="$file"        # or however you want to process each file
done < <(find . -perm /go+w -print0 2>/dev/null)

for dodgy in ${unsecure[@]}; do
	guilty=1
	for entry in ${ignore[@]}; do
		if [[ "$dodgy" =~ $entry ]]; then
			unset guilty
		fi
	done
	if [ $guilty ]; then 
		echo "Unknown unsecure directory : $dodgy"
	fi
done
Posted in Programming | Tagged , | Comments Off on A small script hacked up to monitor directories.

Application Accepted!

Well that was quite smooth, the application is now in the App Store Right Here!. In other news it appears I’d completely forgotten in my rush to get the site live to include LV partitions for the site and it’s backup. Cue me completely forgetting the LVM commands and having to have a quick refresh on tldp.org. Oh how things have changed so fast! A few weeks ago I stumbled briefly with grant privs in MySQL!

Posted in Chef's Book, LVM, Miscellany, Programming | Comments Off on Application Accepted!

Application Submitted!

Well actually it was submitted last Friday, but with the announcement of the iPad 2 and everything else I’m not expecting it to go live this week. Suffice to say I’ll be posting a store link or page link here when it’s all gone through.

Posted in Miscellany, Programming | Tagged , , | Comments Off on Application Submitted!

So two weeks work later.

And I have a reasonably themed wordpress install, integrated with a reasonably themed bbpress install. Not bad having never touched html or css before. Now I just need to gather those screenshots up. So, it’s kinda live:

Chef’s Book

I need to fiddle with the blog settings a wee bit and of course there’s bugger all in the way of content, but that is to be expected seeing as the app isn’t submitted yet.

Posted in Miscellany | Comments Off on So two weeks work later.

DynDNS.org + Bash + Router == A tiny bit safer.

Pondering how to lock down a few bits to just an IP or two I hacked together this awful bit of bash:

#!/bin/bash
function valid_ip()
{
    local  ip=$1
    local  stat=1

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

HOME_IP=`host dyndnsname.dyndns-home.com|awk '{print $4};'`

if valid_ip $HOME_IP; then
	OLD_IP=`grep HOME_IP /home/user/public_html/blog/wp-admin/.htaccess|awk '{print $3}'`;
	if [ $OLD_IP != $HOME_IP ]; then
		sed -i -e "s/ [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\} #HOME_IP/ $HOME_IP #HOME_IP/" \
			 /home/user/public_html/blog/wp-admin/.htaccess
		echo "DNS Changed to $HOME_IP from $OLD_IP"
	fi
fi

exit

So if you have a locked down .htaccess something like:

Satisfy any
order deny,allow
allow from 12.12.12.12 #HOME_IP
deny from all

The above script will update it. Bung it in cron and hey presto. Most routers have options to update dyndns and other services to ensure your IP is current generic diovan.

Posted in Programming | Tagged , , | 1 Comment