Friday 9 November 2012

Key Value Observing iOS / KVO


You can observe changes across different objects/classes. I think the problem is in the options parameter of addObserver:forKeyPath:options:context:.
There are various options for the type of observing you want to do. The KVO Guide is a good starting point, but you probably want NSKeyValueObservingOptionNew, which I use in the example below.
First, "name" should be a public property in ClassB.
Second, you probably don't need to add the observer to "b" in viewWillAppear in ClassA, because you don't need to add it everytime the ClassA view is going to appear. You just need to add the observer once, when you create the ClassB view. Once you've added the observer, theobserveValueForKeyPath:ofObject:change:context: method will be executed in ClassA, so you can do the update to the ClassA UI from there. You shouldn't need to do anything every time ClassA is about to appear.
In Class A, you should probably create ClassB just before you are going to push ClassB onto the controller stack, presumably in the event handler for some action the user took. Immediately after you create ClassB, add the observer in ClassA with the correct NSKeyValueObservingOption value.
If you just want to be notified whenever the public property "name" in ClassB is changed, then try this:
ClassB
@interface ClassB : UIViewController {
}

@property (retain) NSString* name;

- (void) aMethodThatModifiesName:(NSString*)newName;

@end


@implementation ClassB 

@synthesize name;

- (void) aMethodThatModifiesName:(NSString*)newName {
    // do some stuff
    self.name = newName;
}

@end
ClassA
@interface ClassA : UIViewController {
}

@property (nonatomic, retain) IBOutlet UILabel* nameLabel;

- (IBAction) someEventHandler:(id)sender;

@end

@implementation ClassA

- (IBAction) someEventHandler:(id)sender {
    ClassB* b = [[ClassB alloc]init];
    [b addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
    [self.navigationController pushViewController:b animated:YES];
    [b release];
}

- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {
    if ([keyPath isEqual:@"name"]) {
        NSString* changedName = [change objectForKey:NSKeyValueChangeNewKey];
        // do something with the changedName - call a method or update the UI here
        self.nameLabel.text = changedName;
    }
}

@end

Difference between Notification and Delegate or Delegation / Notification


Is it generally considered best practise to reduce the coupling or dependencies between classes in your application. A key benefit is that your code becomes easier to maintain since a change in one class is less likely to impact another dependent class. It also becomes easier to reuse classes across applications.
Cocoa implements a number of features such as delegates and notifications that make this easier to achieve though it can be confusing at first to know when you should use one or the other (or both) or these techniques.

Delegation

The concept of delegation as its name suggests allows an object to send a message to another object (the delegate) so that it can customise the handling of an event. The UITableViewController provides a common example of this in an iPhone application. The delegate of the UITableViewController is informed when a user will do something such as select a row allowing the delegate to prevent or change the action. A further call is made to the delegate when the user did do something allowing the delegate to take further action such as saving data or triggering the update of a view.
The important point about delegation is the way it is implemented allows for minimal dependency between the delegating object and its delegate. The delegating object needs to have a reference to its delegate so that it can call methods in the delegate. However this reference is declared using the anonymous id type:
id delegate;
This way the delegating object does not need to know the type of its delegate which removes a big potential dependency between the classes. This weak reference between the two classes means that the compiler cannot verify that a delegate class actually implements the methods expected by the delegating object.
To avoid unexpected runtime exceptions a delegating object can test if a delegate responds to a certain message before invoking it:
if ([delegate respondsToSelector:@selector(myMethod)]) {
  [delegate myMethod];
}
This allows a delegate to optionally implement some methods. The ability to specify some delegate methods as mandatory or optional can be formally defined (and checked at compile time) through the use of protocols. There is an example on using delegates with protocols in the post on iPad Modal View Controllers so I will skip ahead to the sending and receiving of notifications.

Notification

The concept of notification differs from delegation in that it allows a message to be sent to more than one object. It is more like a broadcast rather than a straight communication between two objects. It removes dependencies between the sending and receiving object(s) by using a notification center to manage the sending and receiving of notifications. The sender does not need to know if there are any receivers registered with the notification center. There can be one, many or even no receivers of the notification registered with the notification center.
The other difference between notifications and delegates is that there is no possibility for the receiver of a notification to return a value to the sender. Remember the earlier example of a delegate method indicating that a user will select a row in a table. The delegate has the opportunity to return a value which prevents the user from selecting a row. If we use a notification to inform another object that the user is selecting a row there is no way to return a value to influence this action.
Typical uses of notifications might be to allow different objects with an application to be informed of an event such as a file download completing or a user changing an application preference. The receiver of the notification might then perform additional actions such as processing the downloaded file or updating the display.

The Default Notification Center

Cocoa allows you to create multiple notification centers but in practise on the iPhone you will probably only ever need to use the default notification center. You access this default notification center using a class method as follows:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

Naming a Notification

A notification consists of a name, an object and an optional dictionary to contain user information. The name is used to identify the notification so you should try to choose something unique to your application. Using a two or three letter prefix specific to your application is a good way to do this. Apple also recommends that you define the name as a global constant string variable rather than using the hard coded string value in your code. If you look at the way Apple defines notification names you will also see the name often follows the pattern 

Friday 19 October 2012

Extending PhoneGap with native plugins for iOS


This article examines native plugins for PhoneGap (also known as Apache Cordova) applications in Xcode, targeting iOS devices. If you are just starting out with PhoneGap or if you need to review the fundamentals of PhoneGap, readGetting started with PhoneGap in Xcode for iOS before continuing.
The terms Cordova and PhoneGap are used interchangeably within this article to refer to the same open source application platform that lets you create natively-installed mobile applications using HTML and JavaScript. The PhoneGap codebase moved to open source at Apache Software Foundation under the name Cordova. Adobe is still distributing it under the name PhoneGap. For more information, check out Brian Leroux's blog post "PhoneGap, Cordova, and what's in a name?" As Brian says in the post, "Currently, the only difference is in the name of the download package and will remain so for some time."
Not only does PhoneGap enable you to build user interfaces for natively installed mobile applications using web technologies, PhoneGap also provides a JavaScript-based API to interact with native device functionality. By default, PhoneGap provides access to the device camera, accelerometer, file system, GPS location, and media playback among other capabilities. However, PhoneGap does not expose every native API for use within your JavaScript applications. If you want PhoneGap to do more than its default feature set, you can use the PhoneGap native plugin model to extend the capabilities of the core PhoneGap API.
Native plugins in PhoneGap are not like plugins in desktop browsers; rather they provide a way for you to plug incustom code to add to what the PhoneGap application framework can do. PhoneGap native plugins enable you to create completely new, custom functionality in native code, and expose that to your PhoneGap applications via PhoneGap's native-to-JavaScript bridge. This means that you can expose any native library or framework for use within your JavaScript-based PhoneGap applications.

Understanding the PhoneGap native plugin structure

Before you start to write PhoneGap native plugins, it will help to understand how the PhoneGap application container exposes native operating system functionality to JavaScript-based applications.
All Cordova APIs consist of two related parts: a JavaScript-based interface that can be accessed within your applications, and the corresponding native class for performing operations in native code. Typically, the JavaScript classes and the native classes have APIs that mirrors each other, so that they are easy to follow. The JavaScript class invokes the native code using the Cordova.exec() function. When it invokes Cordova.exec , it can pass in a result handler function, an error handler function, and an array of parameters to be passed into native code, as well as a reference to the native class's name and native function name. Cordova will manage the JavaScript-to-native communication, and you can focus on building your application.
To learn more about PhoneGap native plugins, take a look at the core API's source code, available at the Cordova wiki. The entire PhoneGap framework is built upon the same paradigm you'll find there.

Building your first plugin

To start building your first PhoneGap native plugin, you'll need to create a new PhoneGap project following the steps outlined in the article Getting started with PhoneGap in Xcode for iOS. I named my project MyFirstPhoneGapNativePlugin.

The JavaScript class

Once you have set up your Hello Xcode project, you're ready to create the JavaScript interface for the native plugin. You will need to create a class with functions that will mirror the logic exposed by the native code. Under the www folder, create a JavaScript file named HelloPlugin.js that contains the simple JavaScript class shown below.
var HelloPlugin = { callNativeFunction: function (success, fail, resultType) { return Cordova.exec( success, fail, "com.tricedesigns.HelloPlugin", "nativeFunction", [resultType]); } };
The HelloPlugin class has a single function named callNativeFunction , which accepts a success callback function, an error callback function, and a resultType string parameter. The callNativeFunction function wraps the Cordova.exec function, which will invoke the actual native code. There is no additional JavaScript inside of this class, but you can add JavaScript code here if you need to.
When Cordova.exec is invoked, it expects five parameters:
  • a reference to a success callback function (a function that will be invoked upon a successful response from the native code layer)
  • an error callback function (a function that will be invoked upon an error response from the native layer)
  • a string reference to the native code class (I cover this in more detail below)
  • a string reference to the name of the function that should be invoked
  • an array of parameters to be passed into the native code
Keep in mind that code execution between the JavaScript and native code layers is not synchronous, so you'll need to use callback functions and asynchronous coding practices when developing PhoneGap native plugins.

The Native class

To create the native code layer, start by creating a new native Objective-C class that extends the CDVPlugin class from the core Cordova API:
  1. Right-click the Plugins directory inside of your PhoneGap project, and select New File (see Figure 1).
Figure 1. Creating the new file.
Figure 1. Creating the new file.
  1. In the New File wizard, select the Objective-C class template and click Next (see Figure 2).
Figure 2. Selecting the Object-C class template.
Figure 2. Selecting the Object-C class template.
  1. Type HelloPlugin for the new class name and make the class a subclass of CDVPlugin (see Figure 3).
Figure 3. Naming the class.
Figure 3. Naming the class.
The CDVPlugin class is the parent class that all Cordova classes must extend. The CDVPlugin class encapsulates all logic necessary for native-JavaScript communication via the PhoneGAP API. The PhoneGap.exec function enables you to invoke functions on this new class. The CDVPlugin class has a core function named writeJavascript , which enables you to invoke JavaScript within the PhoneGap application's web view. All communication in the direction of native to web JavaScript must be done using the writeJavascript function.
  1. Click Next.
  2. When prompted specify a location for the new files (preferably the "Plugins" directory within your Xcode project), and then click Finish to proceed.
You should see the new header file (.h) and implementation file (.m) in your PhoneGap project (see Figure 4).
Figure 4. The new native class files.
Figure 4. The new native class files.
  1. Next, add a definition for the nativeFunction function in your .h file; for example:
#import <Cordova/CDV.h> @interface HelloPlugin : CDVPlugin - (void) nativeFunction:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; @end
This definition has two parameters: an NSMutableArray of arguments received from the JavaScript layer and a dictionary (map) of options. For this example, you only need to worry about the array of arguments. The header file only contains method signatures; you do not include any application logic in the .h file.
  1. Once you've created your class signature, add your logic to the nativeFunction function instance in the .m file. Below you will see a sample Objective-C function used inside of this native plugin class.
#import "HelloPlugin.h" @implementation HelloPlugin - (void) nativeFunction:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options { //get the callback id NSString *callbackId = [arguments pop]; NSLog(@"Hello, this is a native function called from PhoneGap/Cordova!"); NSString *resultType = [arguments objectAtIndex:0]; CDVPluginResult *result; if ( [resultType isEqualToString:@"success"] ) { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @"Success :)"]; [self writeJavascript:[result toSuccessCallbackString:callbackId]]; } else { result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString: @"Error :("]; [self writeJavascript:[result toErrorCallbackString:callbackId]]; } } @end
Inside of the nativeFunction method, you first need to get a reference to the NSString callbackId , which is used by the core PhoneGap API to map the response of this function back to the originating JavaScript that invoked this function.
Next this method writes a message out to the Xcode debug console using NSLog ; this just shows that it is executing native code.
After writing to the debug console, the function examines the resultType that was passed into the function, and creates the appropriate CDVPluginResult instance. The resultType value is a simple string. If resultType is "success" , the function will create a success result, and write the success callback to the JavaScript layer using the[self writeJavascript] function. Any other value for the resultType parameter will generate an error result, and the function will write the error callback to the JavaScript layer.
When you write back to the success or error callback functions in JavaScript, always use a CDVPluginResult instance. However, you can also use the writeJavascript function to pass any JavaScript string back to the JavaScript layer. This technique can even be used to push data from the native layer to the JavaScript layer in real time.

Invoking the plugin

Now that you have created a plugin, you can invoke it from within your PhoneGap application.
  1. First, you will need to add a reference to the new plugin's JavaScript interface class (HelloPlugin.js). Add a new<script> tag inside of your index.html file:
<script type="text/javascript" charset="utf-8" src="HelloPlugin.js"></script>
  1. Also after the onDeviceReady() function, add the JavaScript for invoking the native plugin and handling the plugin results. Add JavaScript functions named callNativePlugin , nativePluginResultHandler , andnativePluginErrorHandler as shown below:
function callNativePlugin( returnSuccess ) { HelloPlugin.callNativeFunction( nativePluginResultHandler, nativePluginErrorHandler, returnSuccess ); } function nativePluginResultHandler (result) { alert("SUCCESS: \r\n"+result ); } function nativePluginErrorHandler (error) { alert("ERROR: \r\n"+error ); }
  1. The callNativePlugin function will simply call the JavaScript interface of the native plugin class. When it invokes the callNativeFunction method, it passes the callback functions for success and error status received from the native code layer. The nativePluginResultHandler function will be invoked if there is a success callback from the native layer, and the nativePluginErrorHandler function will be invoked upon an error callback from the native layer.
  1. Next add two JavaScript buttons as shown in the code below to invoke the plugin.
<body onload="onBodyLoad()"> <h1>Hey, it's Cordova!</h1> <button onclick="callNativePlugin('success');">Click to invoke the Native Plugin with an SUCCESS!</button> <button onclick="callNativePlugin('error');">Click to invoke the Native Plugin with an ERROR!</button> </body>
When clicked, the first button will invoke the callNativeFunction method with the parameter "success". PhoneGap will then execute native code and invoke a success callback in the JavaScript layer (it will invoke thenativePluginResultHandler function).
When you click the second button, the callNativeFunction method with will be called with the parameter "error". PhoneGap will execute native code and invoke an error callback in the JavaScript layer (it will invoke thenativePluginErrorHandler function).

Mapping the native code class

At this point you have almost everything wired up and ready to go, but there is still one more step that you have to complete before you will be able to invoke native code from JavaScript.
You have to add a mapping so that Cordova can identify your native code class. Remember the string reference that you used to identify the native class when calling Cordova.exec? You need to map that string to the actual class instance in the Cordova.plist file. The Cordova.plist file contains all configuration information for the current Cordova project.
  1. In the project navigator, open the Supporting Files folder, and click the file named Cordova.plist.
  2. On the right, scroll down to the Plugins entry and click to expand it.
  3. Add an entry with the key com.tricedesigns.HelloPlugin and the value "HelloPlugin" (see Figure 5), and replace com.tricedesigns with your company identifier. This is the string reference that you used to identify the native class in the third parameter when calling Cordova.exec.
The key is the unique string reference that is used by PhoneGap.exec to map to the native code class. The value is the name of the actual native class that will be invoked.
  1. Save your changes.
Figure 5. Editing Cordova.plist.
Figure 5. Editing Cordova.plist.
Now you are ready to launch the application and test it out.
To launch the application click the Run button or choose Product > Run.
Once the application launches in the iOS Simulator (or on a connected device), you will see a simple interface with two buttons (see Figure 6). Click on either button to invoke the native plugin's native code, and an alert message will be displayed via JavaScript upon either a success or error callback.
Figure 6. The application running in the iOS simulator.
Figure 6. The application running in the iOS simulator.