Archive for the ‘Development’ Category

Identifying Hybrid iOS Apps from binaries

April 23, 2015

Recently I had the opportunity to engage in a bit of a research project to examine commonly used app technologies within a specific industry vertical.   It was useful for this exercise to try to coorelate app store ratings to the underlying technologies which were used to the develop the app, specifically with an eye towards hybrid apps, and how they compare to fully native, and Ahead of Time compiled apps. (Xamarin)

It turns out that many example customers with hybrid providers such as Kony or PhoneGap supply are no longer on the platform, at least for the industry vertical I examined.

In any case I wanted to document the process, as I’m sure it could be automated, and it’s always fun to poke around in large corporations apps to see what can be found.   (Occasionally some pretty *interesting* decisions.)

1. From iTunes on your Mac, Download the app in question.

Screen Shot 2015-04-22 at 10.01.16 PMScreen Shot 2015-04-22 at 9.56.15 PM

2. Open your home directory in the finder, open the Music folder, then Mobile Applications and find the new ipa file. (this is for iTunes 12.1…    other versions may have slightly different folder structures.

Screen Shot 2015-04-22 at 9.58.55 PM

3. Rename the .ipa extension to .zip.

Screen Shot 2015-04-22 at 9.59.14 PM

4. Expand the zip file, then Navigated into the Payload directory, right click on the file inside and then Choose “Show Package Contents”  – Sometimes you can learn something about the app just from the File name in the Payload…   for examples some apps showed Mobiliti as the title which turned out to indicate that this was the Saas which had generated the app.

Screen Shot 2015-04-22 at 9.59.58 PM

5. The tell tale sign (in case you couldn’t tell from your app experience is a html or www folder inside the package: )

Screen Shot 2015-04-22 at 10.00.36 PM

In this case we can see that phone gap was used, as well as google analytics, and a bunch of other javascript frameworks to help drive the HTML in the hybrid app.

Another Tell seems to be lack of .xib files in the package.    Lots of .xib files generally means full Native.     Still working on Xamarin detection.

iOS apps with initial Core Data database image

March 12, 2015

Frequently, it’s useful to ship an iOS app with a preconfigured CoreData database, so that on initial launch the data doesn’t need to be retrieved from a webservice before presenting it to the user / or immediately loaded from a plist shipping with the file.

This used to be easy pre iOS 7.0:   Launch in the simulator and ensure you’ve populated the datastore with the data you want to chip.   Then find the sqlite3 datafile on your mac.   That’s easily done with a breakpoint in the persistentStoreCorrdinator:

FindingTheDbImage

Then copy it into your project

BINGOSABI:bingosabi$ cd /Users/bingosabi/Library/Developer/CoreSimulator/Devices/
7FD9730C-F002-414D-9F72-8D6BAA750A3B/data/Containers/Data/Application/0A3AB0EE-
D557-4FE7-9683-3C337C64A7E9/Documents/db.sqlite
BINGOSABI:Documents$ cp db.sqlite ~/Projects/ExampleApp/Resources

 

In iOS 7.0 however, but Apple updated the shipping version of sqlite3 which enabled a separate journaling file.   Now a database has three files.

BINGOSABI:Documents bingosabi$ ls
db.sqlite	db.sqlite-shm	db.sqlite-wal

So in order to ship a populated databases we have to do a few extra steps.

sqlite> pragma journal_mode=off;
off
sqlite> vacuum
sqlite> .exit

Copy ONLYthe vacuumed db.sqlite to the Xcode project and include it.

BINGOSABI:bingosabi$ cd /Users/bingosabi/Library/Developer/CoreSimulator/Devices/
7FD9730C-F002-414D-9F72-8D6BAA750A3B/data/Containers/Data/Application/0A3AB0EE-
D557-4FE7-9683-3C337C64A7E9/Documents/db.sqlite
BINGOSABI:Documents$ cp db.sqlite ~/Projects/ExampleApp/Resources

If you used the default template for a Core Data app, then replace the default persistentStoreCorrdinator with the following code: (Note that you have to always touch that anyways if you’re submitting to the store due to the abort(); poison pill which Apple has in the template. Not removing the abort(); is a rejection-able offense.

So replace this:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it.
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    // Create the coordinator and store
    
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"db.sqlite"];
    NSError *error = nil;
    NSString *failureReason = @"There was an error creating or loading the application's saved data.";
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        // Report any error we got.
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
        dict[NSLocalizedFailureReasonErrorKey] = failureReason;
        dict[NSUnderlyingErrorKey] = error;
        error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();   //POISON PILL
    }
    
    return _persistentStoreCoordinator;
}

with this:

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    //This is required for cached data lest Apple reject for immediately writing data to Documents which isn't user data and has default iCloud backup attribute.
    NSError *error = nil;
    BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                  forKey: NSURLIsExcludedFromBackupKey error: &error];
    if(!success){
        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
    }
    return success;
}

- (void) copyDatabaseFromBundle:(NSURL *)storeURL {
    NSError *error = nil;
    NSString *databaseName = [[storeURL lastPathComponent] stringByDeletingPathExtension];
    NSString *databaseExtension = [storeURL pathExtension];
    NSURL *shippingDatabaseImage = [[NSBundle mainBundle] URLForResource:databaseName withExtension:databaseExtension];
    
    NSURL * toURL = [[[self applicationDocumentsDirectory] URLByAppendingPathComponent:databaseName] URLByAppendingPathExtension:databaseExtension];
    
    [[NSFileManager defaultManager] copyItemAtURL:shippingDatabaseImage toURL:toURL  error:&error];
    
    [self addSkipBackupAttributeToItemAtURL:toURL];
}

- (void) deleteDatabase:(NSURL *)storeURL {
    //Delete all the write ahead log files. 
    [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
    [[NSFileManager defaultManager] removeItemAtURL:[NSURL URLWithString:[storeURL.absoluteString stringByAppendingString:@"-wal"]] error:nil];
    [[NSFileManager defaultManager] removeItemAtURL:[NSURL URLWithString:[storeURL.absoluteString stringByAppendingString:@"-shm"]] error:nil];
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (!_persistentStoreCoordinator) {
        NSManagedObjectModel *model = [self managedObjectModel];
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            // Create the coordinator and store
            _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
            NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"db.sqlite"];
            NSError *error = nil;
            NSString *failureReason = @"There was an error creating or loading the application's saved data.";
            
            if ([storeURL checkResourceIsReachableAndReturnError:&error] == NO) {
                [self copyDatabaseFromBundle:storeURL];
            }
            
            //Handle lightweight version migration
            NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                     [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
            
            if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
                
                //Just Start Over.
                [self deleteDatabase:storeURL];
                
                if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
                    
                    // Report any error we got.
                    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
                    dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
                    dict[NSLocalizedFailureReasonErrorKey] = failureReason;
                    dict[NSUnderlyingErrorKey] = error;
                    error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
                    
                    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
                }
            }
        });
        
    }
    
    return _persistentStoreCoordinator;
}

C’est tout!

iOS 8 Suggested Apps

October 15, 2014

New in iOS 8 are lock-screen suggested apps on iOS 8.    There has been much speculation on how there work, but no definitive confirmation from Apple, even in their forums.   I now have a request from a client to leverage these, which is difficult given the typical opacity from Apple.      It seems here is what is known:

1) Based on location (DUH)

2) Installed apps use a different suggestion mechanism than apps which aren’t installed.

3) Installed apps can pay attention to app established geofences, etc.  And if you’re within one of their registered geofences, (or an iBeacon range) then an app will appear on the lockscreen in the lower left.

4) Apps that haven’t been installed rely on some undisclosed Apple Algorithm, likely related to their correlation of an App, and their Maps data for that’s businesses locations.  There doesn’t appear to be anyway to influence Apple on this currently through the app submission process… However I have a few levers to pull to ferret this out.  This is the use case which I think most App developers are interest in, as it’s a novel way to promote an app apart from the chaos that is the app store.

5) Yes they can be turned off in Settings->iTunes & App Store->My Apps / App Store…     annoying that most the posts are on how to disable the feature, rather than how to leverage them.

6) Feature is enabled by default.

Will test this out in the next few days and report back.

Xcode Shortcuts FTW!!!

December 30, 2013

Best shortcut ever in Xcode:   ctrl-command Up Arrow   (Toggles to .h or .m file in editor)    Also Awesome… add Option into the mix to turn the secondary editor back into “Counterpart” mode.    So…    ctrl-option-command-uparrow

WWDC 2013 Wishlist

April 25, 2013

WWDC

Apple’s platform has gotten…   well..   a bit stale.    Here’s my list of things that Apple could announce at WWDC which would reinvigorate the iOS platform for me personally.

Software

  • Homescreen widgets SDK
  • Make Siri actually understand me more than 20% of the time (being generous)
  • Siri Integration for apps (Assuming above gets fixed)
  • GameCenter Chat support.
  • OCR SDK that doesn’t suck for input. – Tesseract is fickle.  The tech is out there… make it mainstream for input.
  • Fix iCloud & Open it up to other platforms   Or at least a Web Platform.
  • UI Theming / Skin Support.
  • Open up Spolight API so app content can be searchable in Search homescreen.
  • Customizable Spotlight Search Options.   Why can I still not search the appstore from Spotlight?
  • iCloud Repsonsive Web Support
  • iCloud Web app platform

Hardware

Ok.   WWDC isn’t really ever about Hardware Anymore, but when new bizarre things get added to hardware it stimulates the app market for new and interesting mash ups and uses that people hadn’t thought of before.

  • More Phone Sensors (Track Eye Movement, Near Field Communications, Temperature)
  • Failing more sensors… Peripherals with more sensors (iWatch, Glasses)
  • Peripheral Platform and SDK in support of 3rd party wirelessly linked gadgets.
  • Loosen up on the MFI (Made for iPhone) program.     – Currently this just isn’t open to anyone without retained legal council.  Period.
  • Camera Improvements.  UV InfraRed Camera sensitivity  Optical Zoom (fluid lenses?)

Decompiling Android APKs

January 28, 2013

So last week needed to take a look at an existing app in the google play store to see if it was generated using one of the Mobile Enterprise Application Platforms (Appcellerator, / PhoneGap, etc.)  tools.   (Example below is actually one of my own apps…  just for demonstration purposes.)    Figured I’d add it to the blog so all the steps are in one place.   The other mentions I found on how to do it were scattered about.

  1.  Get the APK you want to decompile off of your android device:   Connecting the device to your computer and launching the DDMS in Eclipse won’t actually show you the contents of /data where installed apks and apps are installed unless you root your device.    That seemed like a pain… so instead, I found this:
    1. Install app sender on the device:  AppSender 2.0 (Share APK)
    2. Ensure that  the device ISN’T connected to your computer.   (AppSender thinks it needs the SD card in order to run, and this is unmounted when plugged in on USB. (at least with Moto Droid Razr max 2.3.6 and Mac OS X 10.7.5)
    3. Find the app you want to get off the phone in the list that AppSender presents and tap it.
    4. Tap “Email Attachment”     device-2013-01-28-110728
    5. Send it to yourself using the Mail Intent of your choice.
    6. Save the attached APK file off somwhere on your computer.
  2. Download and install dex2Jar  on your computer:   http://code.google.com/p/dex2jar/downloads/list
  3. Run dex2Jar on the APK:     (This will get you as far as class files on your computer.)
    $ ./dex2jar.sh ../com.tasteseller-1.apk
    this cmd is deprecated, use the d2j-dex2jar if possible
    dex2jar version: translator-0.0.9.12
    dex2jar ../com.tasteseller-1.apk -> ../com.tasteseller-1_dex2jar.jar
    Done.
  4. Download JD-GUI  and Install on your computer. (Java Decompiler) http://java.decompiler.free.fr/?q=jdgui
  5. Run JD-GUI…     Open the jar file:  File->Open File… (select the jar file from above:)
  6. Browse the Code:   (obviosuly if the original APK was obfuscated the results here last step may be totally illegible…   but happily it did just fine on the app I needed to check out.)
    Screen Shot 2013-01-28 at 11.34.20 AM
  7. There is no Step 7.   That was everything!

The follow up article:     How to obfuscate Android Code so that this isn’t doable.

Where are you iPhone Simulator? OH, There You Are!

June 22, 2009

Problem: The iPhone SDK simulator has a habit of losing the simulator window.   This is very annoying when you work with multiple monitors, as the simulator screen can get “lost” or go missing when you unplug the external monitor if the window is positioned in the external display at coordinates that would be completely hidden in the smaller display.    If you relaunch the simulator, it will reposition it on the screen, however, it occasionally leaves it mostly off screen…  It’s actually there… it’s just hiding mostly off screen save 4-5 pixels.

Solution:

  1. Quit and Relaunch the simulator.
  2. Hit the Exposé key on the mac to find the missing iPhone Simulator Window.    This will at least let you find the window, and watch where it animates back out two when you leave Exposé so you can find the 4 pixel swath on the screen and drag it screaming back onto the window.

Filed Bug with Apple…    Which they’ll ignore like they normally do.

    Simulator Window in Exposé

    Simulator Window in Exposé

    See the Simulator Sliver?

    See the Simulator Sliver?