Binary Lion Studios

I code for fun and for food.

Alter table in SQLite when table contains data

SQLite’s alter table command only supports renaming the table and adding a column. If you need to alter a column (say, to change it’s length), the high-level process looks like this: export the data, drop the table, recreate the table with the updated column, then import the data. You can use SQLite’s dot commands to achieve that with minimal fuss.

1
2
3
4
5
6
7
8
$ sqlite3 dbfile.db
sqlite> .mode insert [tablename]
sqlite> .output [tablename].txt
sqlite> select * from [tablename];
sqlite> drop table [tablename];
sqlite> create table [tablename] ...;
sqlite> .read [tablename].txt
[ctrl+d]

If you’re using Django, it’ll take care of creating the table for you. Just update your models.py to reflect your new changes, then the workflow looks like this:

1
2
3
4
5
6
7
8
9
$ sqlite3 dbfile.db
sqlite> .mode insert [tablename]
sqlite> .output [tablename].txt
sqlite> select * from [tablename];
sqlite> drop table [tablename];
[ctrl+d]
$ python manage.py syncdb
sqlite> .read [tablename].txt
[ctrl+d]

You could also consider looking at the South project which is billed as intelligent schema and data migrations for Django.

RegexKitLite 3.3

If you’re looking for RegexKitLite 3.3, it’s a little less than intuitive to find it. I eventually found it here after just grabbing the URL for the 4.0 release and changing it to 3.3.

Why would you want an older version? According this this post on the Cocoa Dev mailing list apps have been rejected for using RegexKitLite 4.0. You can either replace your 4.0 version with 3.3, or just add the -DRKL_BLOCKS=0 flag to your OTHER_CFLAGS build settings in Xcode (much simpler in my opinion).

Programmatically get device id

An easy way to get the unique identifier for your iPhone or iPad:

1
NSString *deviceId = [[UIDevice currentDevice] uniqueIdentifier];

Read more about it in the Apple docs.

iPhone convert device token to string

When you are trying to setup push notifications for an iOS application, you will need to send your device token to a provider. The device token is passed to you by the operating system as binary data. If you need to talk to your provider over HTTP, you’ll need to convert the device token to a string.

1
2
3
4
NSString *deviceTokenStr = [[[deviceToken description]
    stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]
    stringByReplacingOccurrencesOfString:@" "
    withString:@""];

At this point, deviceTokenStr will contain the device token as a string, so it can be passed to your provider over HTTP. You might consider throwing that snippet into a category if you need to use it in various spots throughout your app.

NSURLConnection view raw request

If you want to view the raw request that NSURLConnection is making, you can use the tcpdump command line tool that is bundled with OSX.

1
$ sudo tcpdump -l -q -A "host (Specified Host) and tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)"

Two (semi) obvious notes:

  • Replace (Specified Host) in the command above with your host (ie: binarylionstudios.com).
  • You cannot use this method to view SSL traffic (port 443).

Stolen directly from: http://www.benzado.com/blog/post/317/dont-printf-when-you-can-tcpdump

Objective-C category gotcha

Say you have a simple User object that contains a Name object with two properties: first and last. It might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation User
-(Name *)name {
  return _name;
}
@end

@implementation Name
-(NSString *)first {
  return _first;
}

-(NSString *)last {
  return _last;
}
@end

Now you want to create a category that uses some mock data for the first and last name.

1
2
3
4
5
6
7
8
9
@implementation Name (MockData)
-(NSString *)first {
  return @"Test";
}

-(NSString *)last {
  return @"User";
}
@end

Now in a view controller, you try to use the category.

1
2
3
4
5
6
7
8
9
10
@implementation MyViewController
-(void)doSomethingAwesome {
  User *user = [[User alloc] init];

  // output - Your name is:
  NSLog(@"Your name is: %@ %@", user.name.first, user.name.last);

  [user release];
}
@end

The NSLog statement above will output Your name is:. Why does it not use your category? The reason makes sense now, but was a bit confusing at first. In the example above, the name property on the User object in the view controller is actually nil. So when you ask for the first property on the Name object, it actually sends the message to a nil pointer, which is not a Name object. Thus, it never hits your category.

The solution is to either instantiate an empty Name object when you create a user, or change your category to extend the User object and return a populated Name object when the name property is accessed.

SVN view revision in browser

Generally when you view an SVN repository over HTTP, you are looking at the HEAD revision. Using the process described below, you can view a specific revision of the repository.

First, a couple of assumptions:

  • Base repo url: http://repourl.com/svn
  • Relative path to file: /com/company/File.m
  • Revision you want to view: 700

Use this url to view revision 700 of File.m:

1
http://repourl.com/svn/!svn/bc/700/com/company/File.m

Sweet.

Source: http://svn.haxx.se/users/archive-2006-08/0866.shtml

Transparent UITableView

To make a UITableView transparent, you just need to set it’s opaque property to NO and make the background clear.

1
2
3
4
5
6
7
8
-(void)viewDidLoad {
  [super viewDidLoad];

  // assuming you are in a view controller and it contains
  // a property called tableView
  self.tableView.opaque = NO;
  self.tableView.backgroundColor = [UIColor clearColor];
}

Now whatever you have behind your UITableView will shine through. This technique works for all other UIView subclasses too (UITextView, etc…).

NSFetchedResultsController crashing

If you are using an NSFetchedResultsController and fetching the data using an NSPredicate, you may have seen this nasty message:

1
2
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** - \
[CALayerArray evaluateWithObject:]: unrecognized selector

In my case, this was due to releasing the NSPredicate object too soon. Simply remove the release message and things will work fine again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (NSFetchedResultsController *)fetchedResultsController {
  if (fetchedResultsController == nil) {
      NSFetchRequest *request = [[NSFetchRequest alloc] init];
      [request setEntity:[NSEntityDescription entityForName:@"Task" inManagedObjectContext:managedObjectContext]];
      [request setReturnsObjectsAsFaults:NO];
      
      NSPredicate *pred = [NSPredicate predicateWithFormat:@"taskList = %@", taskList];
      [request setPredicate:pred];
      
      NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
      [request setSortDescriptors:[NSArray arrayWithObject:sort]];
      
      NSFetchedResultsController *aFRC = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
      aFRC.delegate = self;
      self.fetchedResultsController = aFRC;
      
      [aFRC release];
      [sort release];
      // [pred release]; don't do this ... shouldn't be releasing it anyway (I didn't allocate the memory for it).
      [request release];
  }
  return fetchedResultsController;
}

NHibernate mapping attributes

1
2
3
4
5
6
System.NullReferenceException: Object reference not set to an instance of an object
  at NHibernate.Cfg.XmlHbmBinding.ClassBinder.BindClass (System.Xml.XmlNode node, IDecoratable classMapping, NHibernate.Mapping.PersistentClass model, IDictionary`2 inheritedMetas) [0x00000] in :0 
  at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.Bind (System.Xml.XmlNode node, NHibernate.Cfg.MappingSchema.HbmClass classSchema, IDictionary`2 inheritedMetas) [0x00000] in :0 
  at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddRootClasses (System.Xml.XmlNode parentNode, IDictionary`2 inheritedMetas) [0x00000] in :0 
  at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind (System.Xml.XmlNode node) [0x00000] in :0 
  at NHibernate.Cfg.Configuration.AddValidatedDocument (NHibernate.Cfg.NamedXmlDocument doc) [0x00000] in :0

I got this stacktrace while trying to get a simple app using NHibernate up and running. I eventually found the solution in this forum post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// [Class(Table="Task")] - This was the problem.
[Class(Name="Project.Models.Task, Project", Table="Task")]
public class Task
{  
    [Id(Name="Id")]
    [Generator(1, Class="native")]
    public virtual string Id { get; set; }
    [Property]
    public virtual string Description { get; set; }
    [Property]
    public virtual bool Complete{ get; set; }
    [Property]
    public virtual DateTime CreatedAt { get; set; }
    [Property]
    public virtual DateTime UpdatedAt { get; set; }
}

I needed to add the FQDN of the class and the assembly name. In the example above, replace Project with your assembly name.