"ios" entries

Upward Mobility: Android for iOS Developers, Part 4

Sensors indicate activity on the planet's surface, captain!

Our mini-Encyclopedia Galactica can do a few things now, but it’s hardly ready to offer up the recipe for a Pan-Galactic Gargle Blaster yet. If we want to take things to the next level (or at least the next screen), we need to learn how to move between Activities.

Moving between Activities is the Android equivalent of switching between view controllers, but more formalized. In some ways, it’s very similar to the storyboard and segue metaphor introduced in iOS6. To begin with, we need to create a new Activity in ADT, using the File->New->Other menu pick, and then choosing Android Activity from the choices under Android. I chose to name mine DetailActivity, and place it hierarchically under the MainActivity. By doing this, the back button will return me to the original Activity when I hit it.

Screen Shot 2013-07-01 at 10.51.56 PM

Using the blank template, I then added a large TextView at the top, an ImageView, and another TextView set to 10 lines of smaller text at the bottom.

Screen Shot 2013-07-02 at 12.33.29 AMNow we need to teach the MainActivity to navigate to this new page. I added a Button to the MainActivity layout, and then wired a listener up to it in the code in the onCreate method:

final Button button = (Button) findViewById(R.id.moreInfo);
     button.setOnClickListener(new View.OnClickListener() {
         public void onClick(View v) {
        	 Intent a = 
                     new Intent(getApplicationContext(),DetailActivity.class);
        	 a.putExtra("PLANET_NAME", (String) spinner.getSelectedItem());
             a.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
             startActivity(a);
         }
     });

The mechanism that you use to transition between Activities is called an Intent. You create an Intent with the application context and the class of the Activity you wish to navigate to. Notice that unlike iOS, you don’t actually instantiate the new view you’re going to, so there’s no way to pre-populate values in the next view. So how do you pass in values? You stick them on the Intent, using the putExtra method. In this case, we put the planet name into the Intent, so that the detail page will know which planet to display.

One of the big gotchas with passing data via Intents is that you can’t just throw any old object onto it. Because of the Android architecture, it either has to be a simple object like a String, a primitive, or something that implements Serializable. You can also put Bundles or Parcels onto it.
Read more…

Location Functionality in Mobile Apps

OSCON 2013 Speaker Series

Andy Gup (@agup) is a Developer Evangelist at ESRI and OSCON 2013 Speaker. In this interview we talk about location capabilities in apps as well as location analytics.

NOTE: If you are interested in attending OSCON to check out Andy’s talk or the many other cool sessions, click over to the OSCON website where you can use the discount code OS13PROG to get 20% your registration fee.

Key highlights include:

  • Mobile apps must have location capabilities [Discussed at 0:25]
  • Consider your goals when incorporating location into an app [Discussed at 1:05]
  • Is it difficult to add location functionality? [Discussed at 2:19]
  • A real-world example of where it made a big difference [Discussed at 3:32]
  • Location analytics are very powerful [Discussed at 5:11]
  • Augmented reality and location capabilities [Discussed at 6:38]

You can view the full interview here:

Read more…

Upward Mobility: Android for iOS Developers, Part 3

Handling Input Events

Now that our Android app lets you choose what planet you’re on, we should use that information to update our “Hello, world” message based on the selection. To do that, we’re going to need to handle the selection event on the spinner we created.

To do this, we need to set the setOnItemSelectedListener property on our spinner to something that implements the OnItemSelectedListener interface. The simplest way to do this is to implement it in our activity.

public class MainActivity extends Activity 
                               implements OnItemSelectedListener {
.
.
.
boolean spinnerActive = false;

public void onItemSelected(AdapterView<?> parent, View view, 
            int pos, long id) {
    	if (!this.spinnerActive) {
    		spinnerActive = true;
    		return;
    	}
    	TextView message = (TextView)findViewById(R.id.textView1);
    	String planet = (String) parent.getItemAtPosition(pos);
    	if (planet.equalsIgnoreCase("pern")) {
    		message.setText("Hello, Lessa");
    	}
    	if (planet.equalsIgnoreCase("barsoom")) {
    		message.setText("Hello, John Carter");
    	}
    	if (planet.equalsIgnoreCase("equestria")) {
    		message.setText("Hello, Twilight Sparkle");
    	}
    	if (planet.equalsIgnoreCase("ringworld")) {
    		message.setText("Hello, Louis Wu");
    	}
    }

    public void onNothingSelected(AdapterView<?> parent) {
    	TextView message = (TextView)findViewById(R.id.textView1);
    	message.setText("Hello, World");
    }

This is pretty straight-forward stuff. You use the parent.getItemAtPosition call (where parent is the spinner) to get the item that you selected. The item is just the string value of the selection, so we can use a simple string compare to select the appropriate message. In the same way that we got the spinner using it’s id, we can get a handle on the text view at the top of the screen, and then set the message using setText.

There are a couple of things to point out here, however. For one, the onItemSelected message gets called when the widget is created, a fact that irritates the heck out of Android developers. This means that you need to ignore the first call to the method. Thus, the purpose of the spinnerActive flag. Secondly, because we’re using the Activity as the listener, you could end up with problems if you have multiple spinners on the screen, since they would all call the same listener. You could either use the AdapterView value to determine which spinner fired the event, or use private inner classes to handle the various spinners.

Now that we’ve got our listeners implements, the last step is to tell the spinner to use the Activity as the handler.

    protected void onCreate(Bundle savedInstanceState) {
.
.
. 
     spinner.setOnItemSelectedListener(this);
    }

Now when we fire up the app and select a value from the spinner, we get a personalized welcome message!

Screen Shot 2013-06-21 at 6.13.45 AM

Upward Mobility: Android for iOS Developers, Part 2

Turning Hello World into something useful

When we last left our application, it was running on the emulator, but didn’t do much.  This week, we’ll add some more controls to our activity and wire up some functionality.

As a reminder, activities are roughly equivalent to view controllers in iOS. Right now, there’s not much in our activity, because all we have is a single label in our activity. As iOS developers, we’re used to never looking at our XIB files in the raw, because they’re pretty much human-unreadable. By contrast Android layout files (which end up in the res/layout folder in a standard Eclipse project) are both readable and intended to be edited. At the moment, here’s what ours looks like:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

Read more…

Upward Mobility: Android for iOS Developers, Part 1

Welcome to the dark side: we have cookies

Like many hardcore iOS developers, I’ve eschewed learning “the other platform” because I was happy in Apple-land. In addition, the few forays I’ve made into Android development seem to show that it was a more complex and difficult platform to work with. However, recent developments (e.g., a new job) have placed me in a position where I’m going to need to have at least a casual familiarity with Android development, so I thought I’d bring you all along on my journey.

Getting the Development Environment Set Up on OS X

The good news is that, unlike iOS, Android is a development-platform agnostic OS. If you go to the Android developers site on a Mac, you’re going to be immediately presented with the option to download a zip file with the complete Android Developer Tools package. Fair warning that it’s pretty large, nearly 400MB. Of course, for folks used to downloading multi-gigabyte XCode drops, that may seem pretty reasonable.

Once it’s downloaded, unzip the file and move the resultant adt-bundle-mac folder into somewhere permanent. The install documentation recommends the /Developer folder, but since /Developer has been deprecated lately, you might want to just create a top level /Android folder instead.)

# sudo mkdir /Android
# sudo mv adt-bundle-mac-x86_64-20130522 /Android/
# cd /Android/adt-bundle-mac-x86_64-20130522/eclipse/
# mv Eclipse.app ADT-Eclipse.app

One additional step I took was to rename the version of Eclipse that ships with ADT to ADT-Eclipse. I already have a version of Eclipse that I use for general Java development, and I don’t want to launch the wrong one from Spotlight accidentally.

Once you’ve finished installing ADT, you can launch their version of Eclipse from Spotlight and, just like every other version of Eclipse, the first thing you’ll be asked is where you want to create your workspace. Just as with the renaming of the Eclipse application, I gave my workspace a unique Android-y name, and told Eclipse not to ask me again. This should prevent me from accidentally opening my normal Java development workspace.

Writing Your First App

Once Eclipse launches, it’s going to ask you if you want to send anonymous statistics to Google. This is a choice between you, your maker, and the NSA, so choose whatever makes you feel happy. The next thing that you’ll be presented with is a welcome popup, with the option to either create a new Android app, or run through some basic tutorials.

The tutorials aren’t actually built into the ADT. Instead, clicking on one of the links will bring you to a web page with a walkthrough. There’s not a lot of value in having me take you through a tutorial that’s already been written in detail, so I’ll hit the high points.

To start, create a new Android project. If you still have the welcome screen up, you can do that by clicking on the New Android Application... button. Otherwise, you can get to it from the File->New->Android Application Project toolbar item. You’ll immediately be presented with the New Android Application dialog.

The Android New Project Screen

The Android New Project Screen

There are a couple of important decisions you need to make here. The first is the package name for the project. This is equivalent to the bundle identifier in iOS development, and it needs to be unique. Luckily, iOS chose to adopt a Java-like reversed domain-name structure for bundle identifiers, so you should already be familiar with this concept.

The other big decision is the minimum version of the SDK that you will support. In iOS, we’re used to dropping support for old versions of iOS on a regular basis. Most iOS developers don’t support iOS4 anymore, and it’s not uncommon to see iOS6 as the required version for newer apps. This is because Apple has been very aggressive about carrying forward devices to new versions of the operating system; about the only things left behind by iOS6 were the original iPad and some iPod touch models.

By contrast, most Android users are at the mercy of their carriers for OS upgrades, and the story isn’t pretty. If you check Google’s own adoption stats, nearly half of all Android users are still stuck back on Gingerbread, a release now more than two years old. And, in fact, the wizard recommends you go back even further, to Froyo.

Does this mean that you can’t take advantage of the latest and greatest features in Android, and still deploy an app that the majority of users can run? No, but it does mean that you’re going to have to fail gracefully when new features aren’t available. iOS developers have had to occasionally do this in the past. For example, when new callback methods were delivered in iOS6, we had to keep both versions around to make iOS5 work. But the long, long version tail of Android means that you will have to take this practice to the next level.
Read more…

Upward Mobility: Give Your iOS Table Cells Some Class

You're not stuck with the stock options when creating tables

UITableView is the meat and potatoes of many iOS UIs, but if you restrict yourself to the off-the-shelf table cell styles, you’re missing out on a lot of opportunities for customization. By using a combination of variable cell heights and a custom UITableViewCell class, you can make UIs that look nothing like a standard table.

To see how you can make this happen in your applications, let’s start with the world’s most boring table example, a list of my favorite foods.

simpletable

The implementation for this is the stock table view code you’ll see in any iOS tutorial:

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

-(UITableViewCell *)tableView:(UITableView *)tableView 
                        cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSArray *myFavoriteFoods = 
                   @[@"Pizza", @"Sushi", @"Steak", @"Chinese", @"Pasta"];
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = 
      [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]
                initWithStyle:UITableViewCellStyleDefault
                reuseIdentifier:CellIdentifier];
    }

    // Configure the cell.
    cell.textLabel.text = myFavoriteFoods[indexPath.row];
    return cell;
}

-(NSInteger)tableView:(UITableView *)tableView 
                         numberOfRowsInSection:(NSInteger)section {
    return 5;
}

So, what can we do to take this design and make it our own? We can start by designing our own class that extends UITableViewCell, and that has its own XIB file, which we’ll call FavoriteFoodCell. There’s not much to it:

@interface FavoriteFoodCell : UITableViewCell
@property (strong, nonatomic) IBOutlet UILabel *foodName;
@property (strong, nonatomic) IBOutlet UIImageView *foodImage;

@end

Next, create a new Interface Builder XIB file of type View. Set the class of the View to FavoriteFoodCell (not the FileOwner; keep that as a generic UIViewController.) We’ll add a label and an image view, so that the cell looks like this:

tablecell_in_ib

 
Read more…

Upward Mobility: Special Effects Wizardry

Dress up your UIViews with a few simple tricks

Most developers aren’t great UI designers (although, as with everything, there are exceptions). But there are a few quick tricks that can dress up an app, even if you don’t eat and breathe Photoshop. Let’s look at a simple iPad single-view app with two views, each of which contains a label and a text box.

iOS Simulator Screen shot May 20, 2013 7.05.42 AM

This is about as Plain Jane as you can get in an application; the only concession to visual appeal that has been made is to use a grey background with white UIViews bounding the labels and text boxes. One easy tweak we can use is to make the UIView corners rounded instead of square, which will lend a bit of flare to the design.

This is actually harder than it should be. To set rounded corners, you need to dig down into the CALayer underneath the view:

#import <QuartzCore/QuartzCore.h>
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.topView.layer.cornerRadius = 5;
    self.topView.layer.borderWidth = 1;
    self.topView.layer.masksToBounds = YES;
    self.bottomView.layer.cornerRadius = 5;
    self.bottomView.layer.borderWidth = 1;
    self.bottomView.layer.masksToBounds = YES;
}
@end

You need to set masksToBounds, because otherwise the corners will not be transparent, and the background color of the view will block whatever is beneath it in the parent view. The resulting view is subtly more classy. You can get more rounding by adjusting the cornerRadius value.

Screen Shot 2013-05-20 at 7.17.42 AM

 

So far, so good. But that solid grey background is kind of blah, so we should dress it up a bit. An easy way to make a background pop is to use a gradient fill, a technique that the aforementioned Photoshop jockeys know and love.
Read more…

Upward Mobility: Dump Those iOS Delegates

Blocks are the way to go

Because so much of iOS programming involves the delegate pattern (the UITableViewDelegate being a prime example), it’s natural that when programmers are developing their own classes that need to be able to asynchronously call back to a client class, they would use the delegate pattern as well. The problem with delegates is that they are fairly inflexible. For example, let’s consider the following (totally inappropriate) use of a delegate:

@protocol MultiplierDelegate <NSObject>
-(void) handleResult:(float) result;
@end

@interface Multiplier : NSObject
-(void) multiply:(float) v1 and:(float) v2
        delegate:(NSObject<MultiplierDelegate> *) delegate;
@end

#import "Multiplier.h"
@implementation Multiplier
(void) multiply:(float) v1 and:(float) v2
       delegate:(NSObject<MultiplierDelegate> *) delegate {
       [delegate handleResult:v1 * v2];
}
@end

The problem lies when you need to call this method twice from the same class.

#import "Multiplier.h"
@interface MultiplierCallee : NSObject<MultiplierDelegate>
@end

@implementation MultiplierCallee
-(void) example {
    Multiplier *mult = [Multiplier new];
         [mult multiply:3.2 and:23.3 delegate:self];
}

-(void) handleResult:(float)result {
   NSLog(@"Result is %f", result);
}
@end

So far, so good, but what happens if you want to have a second method in the same class call multiply? You can only have a single delegate method, so you have to start jumping through hoops to let the delegate method know where to put the resulting value.

The modern solution to this is to use blocks. People tend to shy away from them, because the syntax is a bit arcane and un-Objective-C, but they are incredibly powerful, and Apple has signaled that they are the future, by starting to use them heavily in areas such as animation.
Read more…

Weekly Highlights and Insights: May 6-10

Fit2Cure, unit testing core data, data skepticism, mobile casual gaming market, and the beehive fence

Fit2CureScreenshot

Fit2Cure: Andy Oram introduces a game that crowdsources the search for drugs to cure under-researched diseases of developing countries.

Unit testing core data: James Turner shows iOS developers a less painful way to set up an OCUnit project to test code that uses Core Data.

Even more data skepticism: Beau Cronin and Mike Loukides continue to weigh in on the dangers of blindly copying data collection and analysis methods.

Developing mobile casual games: Jesse Freeman on the impact of HTML5 on game development and the mobile casual gaming market.

Beehive fence saves African elephants: Lucy King’s inventive fence exploits an elephant’s fear of bees to save lives and crops.

Upward Mobility: Unit Testing Core Data

There's no reason to leave your database-centric code untested

One of the more common issues that arises in creating OCUnit tests in iOS is how to test code that uses Core Data. There are several challenges, but with a little foresight, you can be sailing right along.

The first issue that most people encounter is getting access to the managed object context. If, like most people, you’ve followed the code snippets that Apple provides, or followed a tutorial on the web, you hung it off the AppDelegate, and use

[[UIApplication sharedApplication] delegate]

to get a handle on the context when you need it. Alternatively, some people pass the context all the way down the view container chain, making it a property on each controller. I find this incredibly painful, and since most applications only have a single database instance open at once, there’s no reason not to keep the context around globally.

In any event, the problem with having your Core Data specific classes get the context from the sharedApplication is that when you run OCUnit tests, sharedApplication returns nil, and there’s no way to mock it. But, this is easily solved by creating a helper class that you use to access the context.
Read more…