Saturday, February 27, 2016

Xcode coloured logs | Swift

For Obj-C I used to love this one: https://github.com/robbiehanson/XcodeColors It gave me superman-like capabilities, and I could have various type of console log outputs, which I could turn on/off, and with any colour I desired. Fantastic!


But then I started playing around with Swift. But the same library is not done in the same quality for Swift. Yes - I get coloured logs, but one of XcodeColors features I loved was that it also logged out pretty timestamp and function from which it was called.  I could simply write

DLog();

And it would output in console something like:


And it was great! but for swift it produces:



So, I was not in search of something similar to Obj-C alternative.

Of course if you simply replace print with NSLog, then it at once becomes more usable:


But still, no function origin.

So I searched the web, StackOverflow until I could gather together a simple class, which gives me just those capabilities: https://github.com/GuntisTreulands/ColorLogger-Swift



So now I can get pretty timestamp, original function name, and coloured output. And usage is as similar to Obj-C alternative as possible!


Log()

Log(window)
LogRed("Blue test")
LogBlue("Red fountain")
LogOrange("Yellow submarine")
LogBlue("text count \(5) and object \(window)")

LogOrange(("text", "bext", 5, temp))


Replace alpha in image with white colour | Mac

Bunch of images with alpha colour. Task is to post those images (one at a time, every few days) to Facebook, Twitter.  Problem is - once uploaded - alpha is removed and it shows black background instead.

Ugly!

So, I found this cool little app, that removes Alpha channel from images. (Drag and drop) But it then again is black. I tweaked code a little bit and uploaded it to GitHub - maybe it will be handy to someone also. It sure was helpful for me... (100 images in few seconds, and I can move on with my life on some more interesting tasks :) )



Lightweight Core Data Migration | Obj-C

So.. you have an app, that uses Core Data database, and data that is being accumulated in app is offline only. Everything is great but now you need to develop next version, but it requires to adjust database structure (new entities or maybe add new attributes?).

This happened to me, and I tried to put this problem as far as I could but I finally had to deal with it. So, what are the problems?

Basically - if user will install the update, launch the app and it detects that Core Data model has changed - it will not be able to load it, and one of possible safe measures would be simply to delete existing Core Data model/data, create new and continue from that point with a fresh page. But - then you loose your old data, which could be cumbersome for users. (If you can simply download data from internet again - whew, no problem then!)

So, what do you need to do, in order to add support for data Core Data migration from previous version to new one?

It all depends on the changes. In my case, we introduced new feature - new module in app. Thus we needed to add new entity in Core Data model. So - old data will not be touched.

It turns out - for iOS 9 you simply need to add options when setting up NSPersistentStoreCoordinator persistent store.

_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
 
    NSDictionary *options = @{
  NSMigratePersistentStoresAutomaticallyOption:@(YES),
  NSInferMappingModelAutomaticallyOption:@(YES)};
 
 
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURLFromCaches options:options error:&error])
    {
        NSLog(@"Unresolved error on adding Persistent Store With Type %@, %@, %@", error, [error userInfo], [error localizedFailureReason]);
  
        error = nil;
        
        [[NSFileManager defaultManager] removeItemAtURL:storeURLFromCaches error:&error];
        
        if (error == nil)
        {
            _persistentStoreCoordinator = nil;
            
            return [self persistentStoreCoordinator];
        }

    }

If you provide such options - when opening app with old existing Core Data model, it will now automatically try to map it to the new model). If you did not really complicate things (removed previously existing entities, or changed them heavily), then there should not be any problems!

Of course, it is preferred that after all this, you download AppStore version, set it up, fill some data, and then compile over it your new version, and see if all goes smoothly.

 ------------------------

Ok, but it turns out for iOS 8 this was only part of solution. iOS 8 could not automatically adjust previous model and it always ended up with error and so app had to remove previous database file, and start from scratch.

So, it turns out - you need to help out iOS - by adding versions in Core Data models.


In your xcode project select dbModel.xcdatamodeld and in the top status bar menu - choose "Add Model Version...".


And add a new name, so you can easily know which model version is which. I tend to add version next to the name. Select model version you are continuing from. (Should always be the oldest version).


At this point you have a new model version! This version should be activated - that can be done on the right side of xcode: (Model Version - Current:)


Your original model version should be in a state as it was for the first version, and all the changes should be done only for the new version, otherwise it will not work! Unfortunately I did not know this, and I ended up overwriting original version and only then realise the problem. I could not revert back the changes done to the model (xcode project was closed and opened), And no matter how much I tried to adjust old model - it still had some differences from the original model. So there was only one possibility how to get the original model - from the App Store .ipa file. Here is more info how to do it: (http://stackoverflow.com/a/28054459/894671) So now that you have original model, and new model - iOS (at least iOS 8 and iOS 9) will successfully migrate previously existing model to the new one.

------------------------

 A whole different story is in case you need to adjust existing Core Data database data because of the new model. In that case you need to write up some functionality that will detect application version and will go through all necessary data and .. do the thing you need to do to adjust it (But only one time). (For example, add a new relationship, or add a new attribute with calculated value...)

This is up to you to find/figure out the best/easiest way to deal with it. In my case - on one of the versions I had to do something similar, so...

NSString *currentAppVersion = [NSString stringWithFormat:@"v %@ (%@)",
  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]];
 
 
 if([[NSUserDefaults standardUserDefaults] objectForKey:@"AppVersion"]
  && ![[[NSUserDefaults standardUserDefaults] objectForKey:@"AppVersion"] isEqualToString:currentAppVersion])
 {
  LogRed(@"New app version encountered! Reset country code retrieval lat/lon!");
  
  [AppDelegate.askApi appAdjustmentsBecauseAppVersionChanged];
 }
 
 // This is for iOS Settings and application settings.
    [[NSUserDefaults standardUserDefaults] setObject:currentAppVersion forKey:@"AppVersion"];
    
    [[NSUserDefaults standardUserDefaults] synchronize];

Every time app is launched I detect app version and store it in NSUserDefaults again. So in this case I can detect that app was just opened and version has been changed. So I can call a function to adjust data because of that. (Probably there is a better way to do it, and probably I will change it once I will have to adjust more and more data, but for now.. this is how I deal with it).