Advertisements

Archive

Posts Tagged ‘xcode’

PhoneGap Tutorial Series – #2 Using the PhoneGap API

March 17, 2011 Leave a comment

 

Using the PhoneGap API

 
Welcome to the second installment of my PhoneGap Tutorial Series! This post is a continuation of PhoneGap Project Structure and Internals so if you haven’t already, you may want to peruse that post before continuing on.

We already know that PhoneGap is an open source framework for writing applications using typical web technologies like HTML, CSS, and JavaScript and that the PhoneGap architecture supplies JavaScript wrappers to access native phone features. What you may NOT know is how extensive those features really are or how to actually use them.

This article is all about getting familiar with the current features of the PhoneGap API and how to use them with the iPhone iOS.
 

So What Can PhoneGap Do For Me???

 
Well, turns out it can do a lot – right out of the box with “no coding” required (hahahahahaha! yeah right). No really, PhoneGap has an extensive library that already provides access to many of the iPhone’s basic features.

The following table outlines the PhoneGap API’s current features:

API Details
Accelerometer Tap into the device’s motion sensor.
Camera Capture a photo using the device’s camera.
Compass Obtain the direction that the device is pointing.
Contacts Work with the devices contact database.
Device Gather device specific information.
Events Hook into native events through JavaScript.
File Hook into native file system through JavaScript.
Geolocation Make your application location aware.
Media Record and play back audio files.
Network Quickly check the network state.
Notification Visual, audible, and tactile device notifications.
Storage Hook into the devices native storage options.

 

So How Do I Use These Great Features?

 
It’s actually pretty simple. If you have read the previous post on PhoneGap Project Structure and Internals then you already have a PhoneGap project started – if not – you can get the starter project here.

The PhoneGap documentation has a lot of examples to help you figure out how to code against the API. I have put together a project that exercises these examples and provides a working iPhone application for you to tinker with to see what PhoneGap can really do for you right out of the box.

In later posts I will extend the PhoneGap functionality, add additional third party plugins, and write my own plugin. All of the source code for the project is located on my github repository – https://github.com/hutley/HelloPhoneGap.

Note: I am updating the sample for each API as I get to it so if it says “Under Construction” just check back later for that sample.


Basic Pattern

Each PhoneGap API typically follows a pretty basic pattern where you provide onSuccess and onFailure JavaScript callback functions to each API call which means that when you call a PhoneGap method you will explicitly provide pointers to the JavaScript methods that are to be invoked when the API either succeeds or fails.

Some of the APIs perform this as an asynchronous operation so you cannot count on a specific order of events. If you need a synchronous pipeline – then you need to chain the events through the callback methods. Check the API…



Accelerometer Example

The following code snippet is an HTML page that includes PhoneGap and exercises the Accelerometer API.

There are several things to notice here:

  • Line 5 – PhoneGap.js must be included on the page (at least before third party plugins or custom plugins).
  • Line 9 – onBodyLoad() function adds a event listener for “deviceready”, when this event is triggered, then PhoneGap is initialized.
  • Line 18 – navigator.accelerometer. getCurrentAcceleration(onAccelerationSuccess, onError) – call to PhoneGap API passing OUR success/failure functions.
  • Line 34 – button to execute the getCurrentAcceleration() function


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
         <meta http-equiv="Content-type" content="text/html; charset=utf-8">
            <script type="text/javascript" charset="utf-8" src="phonegap.0.9.4.min.js"></script>
            <script type="text/javascript" charset="utf-8">
                function onBodyLoad()
                {
                    document.addEventListener("deviceready",onDeviceReady,false);
                }
                
                /* When this function is called, PhoneGap has been initialized */
                function onDeviceReady()
                {
                }
                
                function getCurrentAcceleration() {
                    navigator.accelerometer.getCurrentAcceleration(onAccelerationSuccess, onError);
                }
                
                // onSuccess: Get a snapshot of the current acceleration
                function onAccelerationSuccess(acceleration) {
                    alert( 'Acceleration X: ' + acceleration.x + '<BR>' + 'Acceleration Y: ' + acceleration.y + '<BR>' 
                             + 'Acceleration Z: ' + acceleration.z + '<BR>');
                }

                // onError: Failed to get the acceleration
                function onError() {
                    alert ("onError");
                }
                </script>
            </head>
    <body onload="onBodyLoad()">
                        <button  onclick="getCurrentAcceleration();">getCurrentAcceleration()</button>  
    </body>
</html>


Little Gotchas!

So in the course of putting this together, I noticed that lots of the samples that I was trying to get working just seemed to do nothing. Turns out that there are a lot of little things that just don’t work in the simulator and PhoneGap chooses to silently perform a no-op. So instead of calling the onFailure methods, PhoneGap just returns from the API call without doing anything.

Awesome? No. Now maybe it should be obvious to me that I can’t take a pic from the simulator – BUT my MacBook has a nice little camera so it wasn’t…. Anyway – after stepping through the Objective-C code – I stumbled on this:

- (void) getPicture:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
{
	NSUInteger argc = [arguments count];
	NSString* successCallback = nil, *errorCallback = nil;
	
	if (argc > 0) successCallback = [arguments objectAtIndex:0];
	if (argc > 1) errorCallback = [arguments objectAtIndex:1];
	
	if (argc < 1) {
		NSLog(@"Camera.getPicture: Missing 1st parameter.");
		return;
	}
	
	NSString* sourceTypeString = [options valueForKey:@"sourceType"];
	UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; // default
	if (sourceTypeString != nil) {
		sourceType = (UIImagePickerControllerSourceType)[sourceTypeString intValue];
	}

	bool hasCamera = [UIImagePickerController isSourceTypeAvailable:sourceType];
	if (!hasCamera) {
		NSLog(@"Camera.getPicture: source type %d not available.", sourceType);
		return;
	}

Notice that in both line 11 and line 23 there are return statements that basically perform a silent failure – yes there are log entries but since I am providing an onFailure method I would think it would get called. That is not the case – so just beware of the quirks when using some of these features.

Well that’s all for now – stay posted for the next installment….

Advertisements

Xcode4 – Is your old project hanging in the simulator?

March 10, 2011 4 comments

 

So you took the plunge and upgraded to Xcode4…

 
Do your old projects build but won’t run on the simulator or even on a device? Well we spent a couple hours today trying to figure out why and after uninstalling/reinstalling, recreating projects, comparing settings, and every combination of simulator and SDK we finally stumbled on a solution.

So it seems I had some corrupt settings or project data that was preventing the app from running. By deleting all the xcuserdata associated with project it magically started to work again.

If you are having the same problem — deleting all the Xcode4 project data with your userid may get you up and running again…

    How to go about deleting the user-specific project data
  1. In terminal – navigate to your project directory
  2. cd into the ${PROJECT_NAME}.xcodeproj directory
  3. run this command: find . -name ‘*yourUserName*’
  4. rm -rf any files or dirs that come up
  5. Reopen Xcode4 – build and *hopefully* run your project

Hope it helps!

Xcode3 – Debugging iOS Unit Tests

March 8, 2011 4 comments

 

So you have some unit tests (yeah!) and they are failing …. now what?

 
Over the course of my career these past few years, I have become a developer that writes more test code than production code in an effort to never have to spend the wee hours of the morning debugging a horrible production issue. As a java developer turned mobile developer, there are several things that I miss about coding in Java but the number one thing is unit tests.

Don’t get me wrong – I know what you are thinking – probably something along the lines of: “Do a little research dummy! Xcode has a nice Unit Test Bundle target that we can use to run unit tests! All you have to do is write them!” Right…..

Or maybe after a little more googling around you may even point me to this excellent Apple documentation that explains the process of setting up and running my newly created unit tests but there is a huge section missing from this guide…

iOS Development Guide: Unit Testing Applications

So I have followed all these steps and everything is running fine and dandy until …I need to debug my tests. Where is the guide for this? What’s the point of having tests if I can’t debug them???

Having spent countless hours reading blog posts and attempting steps I have finally gotten it working with Xcode 3.2 AND iOS 4.2 as of today! Yes today! Not two years ago and not on some old version of Xcode and some weird version of the iOS SDK so to save you from having to experience this same misery keep on reading.
 

Have no fear! Instructions are finally here!

 
This guide assumes that you have the following knowledge, skills, and tools:

  1. Apple computer that has the Developer Tools installed and up to date.
  2. Working knowledge of Xcode 3.2, Xcode projects, Xcode build targets
  3. Working knowledge of Objective-C and iOS development

With the above, you should be able to follow these steps fairly easily so I won’t go into details on the typical tasks. If you need more information on how to use Xcode or setting up targets please see the following guide: Understanding Xcode Projects

Here we go…

    Create a new Xcode project

  1. Open Xcode 3 (instructions coming later for Xcode4)
  2. Create a new Xcode project (iOS window-based application template) — mine is called “MyNewApp”
  3. Build and Run the project for the simulator – if its “just working” – and it should – you should see a blank white screen in the simulator.

    Create a new target and run some tests

  1. Select “Targets” and add a new target (Unit Test Bundle template) — mine is called “MyNewAppTests”
  2. Expand the build phases of the new target and take note of the last step – “Run Script” – this is the step that runs your tests as part of the build.
  3. Add a new Objective-C Test class to the project, make sure you add it to the “MyNewAppTests” target.
  4. Within the new “MyNewAppTests.m” file add the following code:
    - (void) testFail {
    STFail(@"Must fail to succeed.");
    }
    
  5. Select the “MyNewAppTests” as the active target and run the build – it should fail

These steps are a shortened version of this guide: iOS Development Guide: Unit Testing Applications. If you need more detail to get your initial units tests building and running – please see the full documentation.
 

It’s failing … now its time to debug!

 
To debug your tests you need to set up an alternate method to run the tests – the basic “Run Script” in the typical test bundle target actually runs a “RunUnitTests.sh” which ultimately launches an instance of “otest” to run the tests — but its not debuggable and runs standalone.

In order to create your own executable to run “otest” and debug it – you will need to run the following command to find the right otest executable for your current iOS SDK.

Run this in the Terminal: find /Developer -name otest

When I run this I see a number of options — select the one from the latest iPhoneSimulator#.#.sdk — and take note of the path.

[hiedi:/Developer/Tools] % find /Developer -name otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator3.2.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.0.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.1.sdk/Developer/usr/bin/otest
/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/Developer/usr/bin/otest
/Developer/Source/OCUnit/SourceCode/otest

 

    Create another new target and an executable

  1. Select “Targets” and add a new target (Unit Test Bundle template) — mine is called “MyNewAppTestsDebug”
  2. Expand the build phases of the new target — delete the last build phase — “Run Script”
  3. Right click and “Get Info” on the “MyNewAppTests.m” — add “MyNewAppTestsDebug” to the list of included targets.
  4. Select “Executables” and add a new custom executable with any name — mine is “Debuggable Otest” and fill in the path that you found above.
  5. On the arguments tab — add an argument that specifies the test bundle to use — mine is “MyNewAppTestsDebug.octest”.

On the arguments tab — also add the following environment variables:

Variable Value
DYLD_FRAMEWORK_PATH ${SDKROOT}/Developer/Library/Frameworks
DYLD_LIBRARY_PATH ${BUILD_PRODUCTS_DIR}:${DYLD_LIBRARY_PATH}
DYLD_NEW_LOCAL_SHARED_REGIONS YES
DYLD_NO_FIX_PREBINDING YES
DYLD_ROOT_PATH $SDKROOT
IPHONE_SIMULATOR_ROOT $SDKROOT

    Run the new target and executable

  1. Set the new target and the new executable as active and then Build/Run
  2. Open the console – you should see messages relating to the running of your tests and their failure.
  3. Now set a breakpoint in the “MyNewAppTests,m” file then Build/Debug
  4. In the console you should see messages about resolving your breakpoint and then it should stop in the debugger.

Note: These steps have been adapted from an original blog post by the author of Grokking Cocoa and can be found here: How to Debug iPhone Unit Tests