Tuesday, July 15, 2014

New ways to do old things

Web Services are a way of life for mobile developers.  I can't really think of a single mobile app I've written that didn't have a backend of some type, even if it's just Google Analytics.

A very common pattern for web services are to setup a basic set of REST based services.  These let the phone update info on the backend very easily.  One of the fastest and easiest Web Servers to use is Google's AppEngine unfortunately rest frameworks are oddly heavy - meaning that the frameworks use lots of jars and extra libraries to do something that seems like it should be simple - and the heavier the framework the more effort is to get it working.

Here is an example of a restful url to list all the frogs on a backend:
http://welikefrogs.com/listAllFrogs
Another way to create that same functionality would be to create a basic url like this:
http://welikefrogs.com/listServlet?frogs=all

The backend code for both  techniques would be very similar.

Jersey is a framework I've used a lot.  It's a great framework for REST and there are lots of benefits to using a REST framework other than URL pattern matching.  The problem is that Google's AppEngine doesn't seem to like Jersey very much.  Oh sure, you can get it working (eventually).  Some people would probably say it's even easy - but it's actually a huge pain in the butt to configure and run the latest version of Jersey on the latest version of AppEngine.

So that brings us to our blog title is there a new way to do the same thing I've been doing for several years now?  Hopefully one that is less painful than trying to get the Jersey framework to function?

Well good news! Apparently Google has a new framework or API called "Cloud EndPoints".  I started playing with them today and so far it's a little frustrating - so I though I'd share some of it with you.  :)

First of all after reading up on the documentation it sounds pretty cool, it looks to lean pretty heavily on Eclipse and the Google Web plugin which is cool - we all love eclipse right?

As you go through the tutorial you see several mentions to the plugin - and then you get to the part where you build a demo application entirely from maven.  That's cool, we like Maven too right? So the tutorial page tells you to issue this maven command:
  1. mvn archetype:generate -Dappengine-version=1.9.6 -Dfilter=com.google.appengine.archetypes:
and that works just fine also.  Well, at least until you import the new project into eclipse so you can code the app.  After you import the project into eclipse you can expect to get several errors about your pom.xml file and your persistence.xml file.

Really?!  What the hell?  This is so unlike Google - things typically just magically work with their frameworks and libraries.

There are about 6 errors in the POM file that all have to do with "Plugin execution not covered by lifecycle configuration" this turns out to be an eclipse or more specifically an M2E plugin (maven 2 eclipse) error.  It has a pretty easy fix here is the documentation on why this error occurs, here is the snippet you need to add to your pom.xml file (it goes inside the tag):

<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
  <lifecycleMappingMetadata>
    <pluginExecutions>
      <pluginExecution>
        <pluginExecutionFilter>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>versions-maven-plugin</artifactId>
          <versionRange>[2.1,)</versionRange>
          <goals>
                            <goal>display-dependency-updates</goal>
                            <goal>display-plugin-updates</goal>
          </goals>
        </pluginExecutionFilter>
        <action>
          <ignore />
        </action>
      </pluginExecution>
      <pluginExecution>
        <pluginExecutionFilter>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
          <versionRange>[2.5,)</versionRange>
          <goals>
                            <goal>resources</goal>
                            <goal>testResources</goal>
          </goals>
        </pluginExecutionFilter>
        <action>
          <ignore />
        </action>
      </pluginExecution> 
    </pluginExecutions>
  </lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>

</pluginManagement>

Keep in mind this fix is just so that eclipse won't complain about your pom file.  It does not affect your build if you're using Jenkins or building on the command line it is ignored, also to be clear the errors only occur in Eclipse - so it's not really a Google error. 

OK, so now our build is working.  now we have to figure out what's going on with the persistence.xml file - when you look at that file all that is in there is one line:

xml version="1.0" encoding="UTF-8"?>

Well, that is clearly a problem no xml validator would be happy with that, so add just enough xml to make it happy:


xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 
    xmlns="http://java.sun.com/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="dataStore">
   
</persistence-unit>

</persistence>

Keep in mind this is only valid for the XML validation.  Once we get ready to use the data store we will need to fix it the right way.

I'll keep you posted on how the endpoints work out.  It's a tad irritating so far but learning a new way to do something is often frustrating.

Cheers!

-Aaron

Monday, June 23, 2014

Core Data Fears

Fears in general are base on rumors and a fundamental lack of understanding.  Hopefully by the end of this post all of our fears will be resolved and we can move forward with Core Data with rumors dispelled and a solid understanding of what we are doing.

Core Data is maybe on of the best reasons to do development on OSX or iOS it is an amazingly well done framework.  There is one very scary deterrent to it though; if you change the underlying data model incorrectly you can break your app.  The only way to fix it is to force users to uninstall and reinstall your app, which is a scenario you never want to find yourself in.

There is actually a large amount of very technical (and scary) documentation on how to do this correctly:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html

You will definitely need to check out that document to go along with this post, but I'll try to boil it down to keep it simple for us.

Before we get started, I want to stress to you as you begin developing your app with core data, not to take the easy way out.  When you're developing your app and making model changes it is easy to uninstall and reinstall your app when you make data changes, don't do that!
Take the time now to learn the versioning process from the beginning, otherwise when you need to make the data change for an app in production you'll be too scared and unsure of yourself to do it properly.

At a high level this is what you need to do to make changes to your Core Data Model:

  1. Create a new version of your model (see the documentation)
  2. Mark the new model as as active

Once you have gone through and made the data model changes you need to do, you should have the information you need to decide if you can do a lightweight migration or if you will have to use a Migration Manager.

Unfortunately the documentation on when you can and can't use the lightweight migration is not very clear, however as a general rule if you are adding, removing or renaming things (entities or attributes) lightweight migration changes will "probably" work for you.  If you are completely re-working your data model then you will need to to use a migration manager, which may be covered in a future post.

OK, so having said all that let's talk a bit about a simple common scenario.  I am writing a super simple app that collects the user's name and stores it into an entity called TraxUser.   This is what that data model looks like:


Next we want to add another entity so that we can store some info about an iBeacon the user might run across:

So, now we have versioned our data model, made the changes.  Now if you were to be so naive as to run your app, you will get a pretty scary crash that looks like this:
2014-06-23 12:05:54.280 Trax[6122:60b] Unresolved error Error Domain=NSCocoaErrorDomain Code=134100 "The operation couldn’t be completed. (Cocoa error 134100.)" UserInfo=0x14d40530 {metadata={
    NSPersistenceFrameworkVersion = 479;
    NSStoreModelVersionHashes =     {
        TraxUser = <21f8bf1e 14685e4a="" 51457eb2="" 749ddd4d="" 83698e64="" a319d28f="" ceec364d="" db05b3aa="">;
    };
    NSStoreModelVersionHashesVersion = 3;
    NSStoreModelVersionIdentifiers =     (
        ""
    );
    NSStoreType = SQLite;
    NSStoreUUID = "78366830-BF50-4F5A-9142-893EE6C91619";
    "_NSAutoVacuumLevel" = 2;
}, reason=The model used to open the store is incompatible with the one used to create the store}, {
    metadata =     {
        NSPersistenceFrameworkVersion = 479;
        NSStoreModelVersionHashes =         {
            TraxUser = <21f8bf1e 14685e4a="" 51457eb2="" 749ddd4d="" 83698e64="" a319d28f="" ceec364d="" db05b3aa="">;
        };
        NSStoreModelVersionHashesVersion = 3;
        NSStoreModelVersionIdentifiers =         (
            ""
        );
        NSStoreType = SQLite;
        NSStoreUUID = "78366830-BF50-4F5A-9142-893EE6C91619";
        "_NSAutoVacuumLevel" = 2;
    };
    reason = "The model used to open the store is incompatible with the one used to create the store";
}

If/when you get this error it feels like the world has just ended and you go into panic mode big time.  Fear not! There is good news! You can go back to the previous version of your model and make it active and your app will start just again.  

For our change (adding a new entity) we can simply add this code into our app delegate under this method
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator  

by default this method has a large block of comments in it guiding you a bit on what needs to be done.  In all actuality the code change to make the migration happen is very simple.  

The default code that you are given should look like this:
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Trax.sqlite"];
    
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])

All that needs to change for this to work is to add a new dictionary to this method:

    
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],
                             NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES],
                             NSInferMappingModelAutomaticallyOptionnil];
and change this line:
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
to use the new dictionary that was just created:
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])

Adding this dictionary in to the options you are telling the core data framework to migrate the data automatically and to infer the changes without help.  Easy enough!

And that is all there is to it!  By versioning the changes you make to your data model and making those two small changes to your AppDelegate you can make small incremental changes to your map very easily.

Good Luck using and versioning Core Data.  Remember to start getting familiar with CoreData early in your development process so when the inevitable time comes to change your data you can do it with confidence.

Happy Coding!

-Aaron

Wednesday, January 08, 2014

2 weeks of vacation

Went to Colorado over the holidays, had a great time skiing.  This is a view from one of the windows in the lodge we stayed at.


When I finally came back to work I realized I forgot about this sad little guy:



Not sure if he will recover...

Posted via Blogaway

Wednesday, October 16, 2013

Android Studio + Gradle + Android Annotations

I've been trying to migrate to Android Studio and Gradle for a little while now and I think I finally figured it out.

So far these are the key points you need to know.

  • You don't need to really configure Android Studio to do packaging or code generation 
    • this is a small but significant difference when moving from Eclipse.  Android Studio fully uses the Gradle build system so tweaking options in it will either have no effect or screw things up.  
  • Android Studio is really focused on editing code not building applications (that's where Gradle comes in).  
    • I haven't deviated a whole lot from their default structure - I'm pretty certain you can anticipate "issues" if you convert a project or try to deviate from the convention.
    • The project structure in Android Studio is similar to Maven, although it can be overridden in your gradle file.


I was pretty new coming into Gradle and watching this video helped quite a bit: http://youtu.be/LCJAgPkpmR0 - it's the Goolge I/O 2013 New Android Build System talk.

To get up and running very quickly all you have to do is create a new project. In the same directory as your source directory create two directories :compileLibs and libs.  AndroidAnnotations has a code generation library called androidannotations-2.7.1.jar and their api (similarly) named androidannotations-api-2.7.1.jar.  The api library goes into the libs directory and the non-api lib goes into compile libs.

Once you have that setup all you have to do is drop this Gradle file in and you're off and running:
buildscript {
    repositories {
        mavenLocal()
        maven { url 'http://repo1.maven.org/maven2'}
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:0.5.+'
    }
}

apply plugin: 'android'

repositories {
    mavenCentral()
    maven {
        url 'https://oss.sonatype.org/content/repositories/snapshots/'
    }
}

configurations {
    compile
    androidannotations.extendsFrom(compile)
}

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    androidannotations fileTree(dir: 'compileLibs', include: '*.jar')
}

android {
    compileSdkVersion 17
    buildToolsVersion "17.0.0"
}

/*
over riding the android annotations output directory:
https://www.flexlabs.org/2013/05/support-android-annotations-in-gradle-projects
 */
def annotationDirs = file('src/main/aa_gen')

task annotationsDir {
    outputs.dir annotationDirs

    doFirst {
        if (!annotationDirs.isDirectory()) {
            println 'Creating: ' + annotationDirs
            annotationDirs.mkdirs()
        }
    }
}

tasks.clean.dependsOn tasks.cleanAnnotationsDir

afterEvaluate { project ->
    android.applicationVariants.each { variant ->
        variant.javaCompile.dependsOn annotationsDir
        variant.javaCompile.options.compilerArgs += [
                '-classpath', configurations.compile.asPath,
                '-processorpath', configurations.androidannotations.asPath,
                '-processor', 'com.googlecode.androidannotations.AndroidAnnotationProcessor',
                '-AandroidManifestFile=' + variant.processResources.manifestFile,
                '-s', annotationDirs
        ]
    }
}

Once I executed a gradle assemble command I did have to right click on the aa_gen folder and say "Mark Directory as Sources Root"

I think that's all I had to do in the end.  You should be able to be up and running in minutes (instead of weeks like me).

Happy Coding!

-Aaron


Friday, September 20, 2013

A clever command

Problem:
I have a bunch of log files in a directory on my SDCard on my Android device.  I want to pull them off the devices but you can't use ADB to pull multiple files - so this is a command to do that:

adb shell ls /some/dir | awk '{print "/some/dir"$0}' | tr '\r' ' ' | xargs -n1 adb pull

ok break that down: here is what's going on:

adb shell ls /some/dir - this outputs the files in the path but just the file name
| awk '{ print "/some/dir"$0   - this changes the file names to full path names
| tr '\r' ' ' - this formats the text so that xargs can understand the command correctly
xargs -n1 adb pull - executes the adb pull command with the absolute path name

Wednesday, December 05, 2012

Core Data Helper Macro

I pretty much love core data.  It can be frustrating sometimes but for the most part it's pretty awesome.

One of the crazy things about it is if you get an error and try to figure things out by doing this:


    NSError *error;
    if (![managedObjectContext save:&error])
    {
        NSLog(@"Error saving User: %@", [error localizedDescription]);
    }
    

You get a good error - something like "Operation could not be completed: cocoa error code 1560".

Not very helpful.  I just stumbled across this little nugget of information from back in 2009!  All of these years of suffering I have been through.  Basically the gist of the article is you create a macro like this:

#define FT_SAVE_MOC(_ft_moc) \
do { \
    NSError* _ft_save_error; \
    if(![_ft_moc save:&_ft_save_error]) { \
        NSLog(@"Failed to save to data store: %@", [_ft_save_error localizedDescription]); \
        NSArray* _ft_detailedErrors = [[_ft_save_error userInfo] objectForKey:NSDetailedErrorsKey]; \
        if(_ft_detailedErrors != nil && [_ft_detailedErrors count] > 0) { \
            for(NSError* _ft_detailedError in _ft_detailedErrors) { \
                NSLog(@"DetailedError: %@", [_ft_detailedError userInfo]); \
            } \
        } \
        else { \
            NSLog(@"%@", [_ft_save_error userInfo]); \
        } \
    } \
} while(0);

and when you save like this:

FT_SAVE_MOC(managedObjectContext);



If you get an error you actually get useful information! 

Core Data just got better!

-Aaron

Monday, November 19, 2012

Irritating things I seem to forget how to do (Android Version)

So one of the most basic things you can do in Android is start a new intent it is super simple (this is not what I forget - I'll get to that in a minute):


Intent i = new Intent(this, AwesomeIntent.class);
startActivity(i);

See? very simple.  Now if you want to pass data back from an activity that you just created you have to start the Inent slightly different:

Intent i = new Intent(this, AwesomeIntent.class);
startActivityForResult(i, REQUEST_CODE_FOR_MY_INTENT);

Even that was pretty easy wasn't it?  

It doesn't take long though before you want to pass information into an intent, and of course you have to return data from the intent (and process it as well).  Here is how you do that:

Intent i = new Intent(this, AwesomeIntent.class);
i.putExtra(Key, Value);

The documentation says that should work however this seems to work better:

Intent i = new Intent(this, AwesomeIntent.class);
Bundle b = new Bundle();
b.putString("KEY", "VALUE");
i.putExtras(b);

startActivity(i);


You can get that extra data you passed in (often in the onCreate method) like this:

Bundle b = getIntent().getExtras();
magicKey = b.getString(key);

Now the whole reason I created the post when you are in an Activity that was created by a "startActivityForResult".  Inside the method where your activity finishes up just do this:

Intent intent = this.getIntent();
intent.putExtra("SOMETHING", "EXTRAS");
this.setResult(RESULT_OK, intent);
finish();

And lastly the class that started it all needs to implement this method:

protected void onActivityResult(int requestCode, int resultCode, Intent intentData) 
{
   if (requestCode == REQUEST_CODE_FOR_MY_INTENT)
   {
     //do some cool stuff here
   }
}


Pretty simple basic stuff - but for whatever reason I always seem to have to look it up.

Happy Coding!

-Aaron

Friday, November 16, 2012

Removing SVN cruft

I'm slowly converting some of my old SVN projects to bitbucket (using git).  Bit Bucket is really the coolest thing when it comes to source code control.  If you can get over the GIT learning curve there are so many features that bit bucket gives you free (Issue Tracking, WIKI, source code browsing - just to name a few).  I don't know how or why but I love those guys...

 One of the odd / irritating svn does is create that .svn folder in all of your directories if you check a project out.  I suppose you could use the .gitignore for these but I stumbled upon a much better way to do this.  At the root of your project execute this command:
find . -type d -name .svn -exec rm -rf {} \;

Now if you are running windows... well... stop that go get linux or a mac!

By the way I found this little goody over at stackoverflow.


-Aaron

Tuesday, February 21, 2012

SSH Public Key Authentication

Here is a link to a super simple explanation of how to set this up: http://www.petefreitag.com/item/532.cfm

In case it goes away here is the content copied and pasted - this is not my work it is from Pete Freitag :



Setting up public key authentication over SSH

Every time I want to setup public key authentication over SSH, I have to look it up, and I've never found a simple guide, so here's mine.
Generate key on local machine

ssh-keygen -t rsa
It will ask you for a password but you can leave it blank.
Note you could also pick -t dsa if you prefer.
Ensure that the remote server has a .ssh directory

Make sure the server your connecting to has a .ssh directory in your home directory. If it doesn't exist you can run the ssh-keygen command above, and it will create one with the correct permissions.
Copy your local public key to the remote server

If your remote server doesn't have a file called ~/.ssh/authorized_keys2 then we can create it. If that file already exists, you need to append to it instead of overwriting it, which the command below would do:
scp ~/.ssh/id_rsa.pub remote.server.com:.ssh/authorized_keys2
Now ssh to the remote server

Now you can ssh to the remote server without entering your password.
Security

Now keep in mind that all someone needs to login to the remote server, is the file on your local machine~/.ssh/id_rsa, so make sure it is secure.

Tuesday, January 17, 2012

Git and Dropbox


I've been using git along with Dropbox for a little while and think it is a pretty awesome way to setup a code repository.

This is usually how I end up doing.  Create a new project happily coding along and then it occurs to me I should probably start versioning this little gem.  Which is very easy to do with git simply "git init" and you're done.

So that is all find and good but then I start to get nervous... what if my hard drive crashes... how can I keep this repository save and sound?

Again git has a very easy to use command that if you put it in your dropbox folder you have a code repository that easy to manage and access!

Here is the command for git:

git clone --bare  /gitrepo/gitproject.git

I get tired of having to type git push /gitrepo/gitproject.git  whenever I want to push code out into the safe region of Dropbox so I usually end up writing a simple little script that looks like this:


#!/bin/sh
git push /gitrepo/gitproject.git

obviously pull is just as easy....

Enjoy!

-A

Monday, January 09, 2012

Making Xcode less difficult

Writing Objective-C code in Xcode can be very difficult.  Especially if you're coming from a different language like Java; not only do you have to learn the new syntax of Objective-C you also have to learn the new development environment and remember what it's like to code in a non-managed environment (i.e. not having the JVM hold your hand when things go awry).

I started working with Xcode and Objective-C about 4 years ago and after many frustrating days (and nights) I found out some great information in an iTunes University video called Advanced IOS Development put out by Madison College.  

It's a very long video, about 3 hours and I'm only about half way through it but I have found some great information in it already that I wanted to share.

Problem: you're running your app on the emulator and it seeming crashes randomly.  You don't get any information about why it crashed the app just quits.

Solution: in Xcode you can set global break points that can stop your app when it crashes, the debugger puts you right on the line that causes the problem!  
Here is how you do it:
  • In Xcode open the Breakpoint Navigator
  • In the bottom left corner of the view is a plus sign click on it to add a new breakpoint.  You will be asked to add an "Exception Breakpoint" or a "Symbolic Breakpoint" choose symbolic.
  • After selecting "Symbolic Breakpoint" a popover window will open fill it in as follows:
    • Symbol: objc_exception_throw
    • Module: libobjc.A.dylib

  • Follow those same steps again to add a new symbolic breakpoint as follows:
    • Symbol: -[NSException raise]
    • Module: CoreFoundation


  • To make the breakpoints global right click (command click - double finger tab - whatever) the break points to move them to the User context


And there you have next time you make a mistake Xcode will stop your app where it breaks - assuming it breaks in your code.  If you tell the frameworks to do something that doesn't work (such as loading a ViewController that won't load for some reason) you're back to the drawing board.

Hope that helps!

-Aaron




Monday, May 23, 2011

Universal iOs Applications

I've spent this afternoon looking at building a universal application for an iPhone application.

First of all I would like to go on the record that I think in general this is a bad software engineering practice - at least in theory .

Having your code check to see if what kind of device it is running and then execute a different set of functions based on the answer feels very hackish to me - at least from a conceptual level.

However, from a user perspective it is sweet software magic that is incredibly awesome - especially when dealing with "markets" similar to what Apple and Google have. For example you only can buy an application once and it can be run on the iPhone or iPad. It's like getting two great applications for the price of one.

I finally decided to buckle down and work through this and as you may expect it is much easier than I expected it to be. I started by:

  • creating a brand new iPhone application
  • then clicked on Application in the top left pane
  • and selected "Universal" in the Deployment Target drop down

Here is a screen shot:

Once xCode runs through it's magic you get a new folder called iPad that contains a new "Window" called "MainWindow-iPad.xib".

Now if you run the application you will be able to run in full iPad mode but the layout will be used by both the iPhone and iPad emulator and that is not what I intended to happen... To get this to change first of all you need another ViewController for the iPad to run so go ahead and create one of those using:
File -> New -> New File... c'mon you know the rest...

So... how do we get our new controller instantiated? Probably have to link into the app delegate, check the InterfaceIdiom to find out what kind of device we're running right?

Nope - not at all. You can handle all of this magic in the Interface Designer. No code change is actually needed! Here is how to do it:
Click on the new "MainWindow-iPad.xib" that xCode created for us
Select the View object in the hierarchy
and change the class under the "Identity Inspector"




Now if you run it you will still get the old layout (and probably crash). What you have to do is tell interface builder to load a different nib file:


Now you can run either emulator and you will get the correct view controller magically instantiated and the correct nib will be laoded.

That doesn't feel very hackish at all now does it?

-A





Monday, February 21, 2011

Google Makes me feel like a Genius!

It's true, just about every time I work on my Android applications - I get that weird maniacal evil genius laugh... You know what I'm talking about - admit it.

If you use CBI you should know that there is a backup feature included that exports your collection as a CSV file [CSV is kind of like an open source spread sheet format].

I've been wanting to send the backup file to Google Docs for awhile now but haven't gotten around to it. Last week I downloaded the source to Google's Client API source code and have been tinkering around with it. And have it backing up my spreadsheet now.

I still have to get the restoration working but I think that will be even easier than the backup.... Maybe I'll post some code for posterity in a bit but right now I have to get back to my mad genius cackling....

Here is a link to my spreadsheet so you can check out the format: My Collection

Tell me what other Comic Book Application will let you do that!

-A


Tuesday, February 15, 2011

Objective C can really stink

Let me preface this by saying I am not a very good Objective C programmer. I come from a strong background in Java and enjoy Objective C becuase it's a new technology (for me) and it solves problems different from Java... well at least the API backed by Cocoa Touch solves them in a different way.

Coming from Java I will tell you this - Exception handling in Objective C is horrible! I've spent all morning dinking around trying to get a table view to display. I've been doing Objective C for 2 years now (not exclusively - but I'm not a complete newbie) so to be having problems with something this basic is pretty frustrating.

The problem really starts with me becoming more familiar with the language. As comfort levels increase I begin exploring how to solve things in slightly different ways... This is what happened today. I'm using a NavigationController to pop on a series of views (successfully until this morning).

I have this little block of boiler plate I toss around to pop a view on the NavController that looks like this:

NewViewController *vc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];
NSArray *items = [NSArray arrayWithObjects:@"Item 1", @"Item 2", @"Item 3", nil];
[vc setItems:items];
[vc setTitle:@"Items"];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
[items release];

Here is the logic behind the code -

  1. Create a new view controller
  2. Create a new list to slam into the table view for the view controller and pop it in
  3. Set the items array into the view controller
  4. push the view controller into the navigation controller
  5. releace the memory for the view controller as it is now owned by the navigation controller
  6. and release the array since it is now owned by the view controller

Even now just looking at the code I just figured out what the problem is - I'm not actually creating a new instance of the items array. So by releasing it - my new view controller crashes because it's retain call got mistakenly released by someone else.

Simple problem - no big deal at all. My frustration really is that the app just crashes without any type of error at all... just a *blip* application died... Occasionally it would spit out this error:

Program received signal: “EXC_BAD_ACCESS”.

Which is not helpful at all - basically that means we accessed memory that we shouldn't have.

I agree whole heartedly that this is not a problem with Objective C - it all comes from my ignorance and lack of experience - however, it is still very frustrating.

-A

Thursday, February 10, 2011

Anxious, Silly things

I have a 1st grader at home. She goes through boughts of anxiety here and there - nothing major but enough to be mildly concerning... At night she often makes sure the windows and doors are locked. Of course there is the general fear of the dark - and as of late does not want to go to school - because she misses her family...

Nothing major or probably outside of normal behavior - but I find myself going through all kinds of things in the morning to get her mind off of the looming drive to school - that often ends in tears...

Today she was rather sad so I drew a picture of her face and hair, in an attempt to get her to smile (no luck) but she did end up drawing in the body and crown(?!) to go with it...


The morning drop off went decent this morning - I'm not sure the picture helped but it was silly drawing on a anxious morning...

-A

Sunday, February 06, 2011

Best Wallpaper

So, I have this little app called Comic Book Inventory or CBI for short. CBI (as the name might imply) lets you inventory your comic book collection, once you get your collection added you can look up issues by Title, Author, Penciler, Inker, Colorist etc.

You can also do some pretty fancy things like find missing issues, get the weekly release list of comics, create a "pull list" (and lots more). My favorite feature of CBI is that you can set your android wallpaper to a comic book cover; actually you can select up to 4 covers and CBI will mash them together.

So here is one of my favorite desktops I've created lately: It's the cover of "Lady Mechanika" #0




Here is another desktop showing a multi-cover desktop.


If you haven't read Lady Mechanika you should check it out. It is creator owned comic published by Aspen Comics and owned by Joe Benitez.

There are only 2 issues out (#0 and #1) but so far the story and art are fantastic!

-Aaron