Adventures in Appleland

by Derek Clarkson (d4rkf1br3 [at] gmail dot com)

Iphone Screen Lock

Recently I was searching for a way to detect when the iPhone is locked by the user. Usualy this is done by pressing the power button so the phone goes to sleep. Reading through a lot of posts on StackOverFlow (my favourite place to hunt for solutions) I found a lot of people saying it could not be done.

But then I dug a little deeper and found a solution posted by some poeple. Basically the answer is that the iPhone actually posts a notification when it has successfully “locked”. The only tricky part of this is that it is a core notification, not a Cocoa NSnotification. So many people didn’t know about it. Here’s the code – you will notice that because its not Cocoa, it’s actually done in raw C.

// Callback function for when the system lock is used. Note this is a C function not a Objective C method.
static void iosLocked(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo);

@implementation YourAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptionsDict {

    // Register the callback for when the system finishes locking. 
    CFNotificationCenterAddObserver(
   	CFNotificationCenterGetDarwinNotifyCenter(), // Notification center
      self,  // observer
      iosLocked, // callback function - see signature above
      CFSTR("com.apple.springboard.lockcomplete"), // Phone locked event
      NULL, // object to be passed ?
      CFNotificationSuspensionBehaviorDeliverImmediately);
}

// Call back
static void iosLocked(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
    YourAppDelegate *delegate = (YourAppDelegate *) observer;
    // Do magic stuff !
}

@end

So far my research on this tells me that it’s perfectly legal to use. No private APIs or restricted stuff. But I’ll confirm that next time we release the app to the store :-)

Posted on 15 October 2013, View comments

Xctest And Bundles

Did realised it’s been so long since I’ve posted anything ! Naughty. So here we go. Over the last little time I’ve been using XCode 5 and XCTest. As it’s new to Xcode and XCode now includes a half decent GUI for it, I decided to try it out and see how it works in comparison to my tried and trusted GHUnit.

Firstly it appears that documentation on XCTest and how it works is thin on the ground. I’ve been unable to surface very little on Apple’s developers site thats of much use. Secondly and the reason for this post is that there are some issues I’ve come up against which would not happen in a GHUnit run.

XCTest runs in a peculiar fashion. It requires that the simulator is started in order to run, but it doesn’t install an app and it does appear to actually run on the simulator. The reason I say that is that the first issue I came across was to do with bundles. If you app contains this code:

...
[NSBundle mainBundle]
...

then you are going to have a problem. When running in a XCTest case the mainBundle method doesn’t return the main bundle of the test target. When means that any resources you might be after are not found. Instead it returns the main bundle of the XCTest program on your system. So how do we get around this?

I’ve had some discussions with Apple through their bug reporter (Yep, I reported this) and the gist of their reply is that XCTest is working correctly by returning it’s own main bundle instead of the bundle of the test target. To fix this, they suggest that we should change our apps to do this:

...
[NSBundle bundleForClass:[self class]]
...

Now this is correct as well and doesn’t have any impact that I can see. But I have to fundamentally disagree with the Apple engineers over this. My reasoning is simple that as a developer I would expect main bundle to return the bundle of the target. Thats the way that it works if I was building an app, and I don’t see why it should be different if I’m running tests. Plus, using their logic then I would expect that main bundle in an app would not return the apps main bundle, but the bundle of the app launcher.

Posted on 07 October 2013, View comments

Introducing The Pieman

It’s been a lot longer coming than I expected, but I’m releasing The Pieman , a command line driver program for Simon, my BDD framework for iOS. Using the Pieman you can now drive Simon BDD tests from the command line and therefore, from a Continuous Integration (CI) server. This means that iOS testing, especially interface testing can now be directly integrated into your build process.

The Pieman does a number of things. Firstly he’s capable of launching the iOS simulator and loading an app into it. This is nothing especially new. There are several launchers already. Waxsim being one that I got a lot of inspiration from when coding. Where the Pieman differs is that he can firstly close down any currently running simulators, reset the simulator and will close it down after the test run has completed.

Simon and the Pieman are more tightly coupled that in most frameworks currently available. The Pieman is launching and monitoring the simulator, and in addition both Simon and the Pieman are running embedded HTTP servers and talking to each other via REST calls. This allows the Pieman to known when Simon is ready to run tests, when each test has finished, when the run has finished and if Simon or the app hang, the Pieman knows through a separate heartbeat which is also being sent between them.

You can find out more in the Pieman’s doco

Posted on 24 October 2012, View comments

Ios Icons And Splash Screens

Some time ago I answered a question onStackOverFlow about the sizes and names of splash screens for iOS devices. Since then it regularly gets an up vote, I suspect from people looking for a good summary. However it is quite out of data as I’ve never gone back to visit it. So because of it’s popularity, I’ve decided to do and update and to copy the information here as well. So here goes (please let me know if there is missing or incorrect data here).

iOS versions and finding icons

Common Icons

iOS 3.1.3 Filename Dimensions
(Retina)
Notes
Icon-Small(@2x).png 29 × 29
(58 × 58)
Settings/search icon.
iTunesArtwork(@2x) 512 × 512
(1024 × 1024)
Used for apps which being installed via adhoc distribution. The file must be a PNG file, but without a file extension.

iPhone Icons

iOS 3.1.3 Filename Dimensions
(Retina)
Notes
Icon(@2x).png 57 × 57
(114 × 114)
Apps icon. Required.

iPad Icons

iOS 3.1.3 Filename Dimensions
(Retina)
Notes
Icon-72(@2x).png 72 × 72
(144 × 144)
Apps icon. Required.
Icon-Small-50(@2x).png 50 × 50
(100 × 100)
Search results icon.

Launch images

Just like icons, launch images are searched for based on the base name of the files and you don’t specify the @2x or .png file extension. There are two choices for how you name your launch images (splash screens):

Note for Universal apps: there are two other Info.plist keys that can be used – (Launch Image (iPhone)) UILaunchImageFile~iphone and (Launch Image (iPad)) UILaunchImageFile~ipad. These are useful because they allow you to use separate naming of iPhone and iPad launch images stored in the same app.

iPhone Splash Screens

Note: iPhones do not support a landscape splash screen.
Physical screen sizes: 320 × 480 (640 × 960)

iOS 3.1.3 Filename Dimensions
(Retina)
Notes
Default(@2x).png 320 × 480
(640 × 960)
Default launch image.

iPad Splash Screens

With iPads, the filename which is more specific will always be used first. For example, Default-LandscapeLeft.png will always be picked before Default-Landscape.png. So the files are listed below in the order that iOS will pick them.

Note also that with launch images that are for universal apps where you are using the same file names for both iPhone and iPad, you can add a device specific modifier (~iphone or ~ipad) to the end of the file name to separate them. For example, here is a launch image file name for a landscape Retina iPad for a universal app: MyLaunchImage-LandscapeLeft@2x~ipad.png

Physical screen sizes: 768 × 1024 (1536 × 2048)

iOS 3.1.3 Filename Dimensions Notes
Default-PortraitUpsideDown(@2x).png 768 × 1024
(1536 × 2048)
Default-Portrait(@2x).png 768 × 1024
(1536 × 2048)
Default-LandscapeLeft(@2x).png 1024 × 768
(2048 × 1536)
Default-LandscapeRight(@2x).png 1024 × 768
(2048 × 1536)
Default-Landscape(@2x).png 1024 × 768
(2048 × 1536)
Default(@2x).png 768 × 1024
(1536 × 2048)
Try to use more specific ones above.

Schema launch images

Finally you can also provide launch images for when you app is launched as a result of a custom URL schema. These launch images are the same as those above, however they also include the scheme as part of the file name. For example, if you app has registered as responding to the scheme myurlscheme:, then using the example above, the launch image would be called: MyLaunchImage-myurlschemeLandscapeLeft@2x~ipad.png

Posted on 30 July 2012, View comments

New Technology Git Submodules And Xcode Subprojects

It’s been some time since I’ve posted anything because I’ve been very busy. In the last while Ive been looking into some new project management methods. Two in particular – git submodules and XCode sub projects.

Old school

Prior to playing with this my methodology for wrapping up common code and building re-useable was to build an XCode project that generates a static iOS framework and use the scripts from dUsefulStuff . I wrote these scripts to perform several tasks:

Pros:

Cons:

New School

Recently I’ve noticed a new trend which is quite different to the techniques I had been using above. It involves two separate technologies being employed:

Pros:

Cons:

Future School

Whilst trying various options and experimenting with all of this I came across a 3rd option which I haven’t seen anyone use yet: XCode workspaces. Workspaces are a new idea in XCode, basically you create a workspace and then add your projects to it. A single project can exist in multiple workspaces at the same time which means each project only has to exist once on your drive. All projects are displayed at the same level in a workspace and you can work on any of them.

Dependencies appears to be a simple matter of references the libraries built by framework projects in the same way as you would when referencing sub-projects. In other words, through the Link binaries with libraries build phase. When you select to add a binary in this way, XCode shows you all the binaries you workspace can build as options. But you don’t need to specify these projects as dependencies. XCode works out the build order automatically.

Pros:

Cons:

Summary

From looking at the above it would appear that there is no one single solution that works well for co-developing a group of projects. Every solution I have found so far has both pros and cons. I’m still in two minds about the best way for me to work. Especially as the git submodule, XCode sub-project system seems to be being adopted by developers, yet as far as I can tell, still has a number of issues.

Finally Luke from Cocoaheadsau reminded me of something I missed. If you are dealing with projects from other developers (sourced from GitHub for example), it’s a good idea to fork them first so that you can commit your changes and track them. You can fork locally or on github depending on what you want to do.

Posted on 25 July 2012, View comments

Fixing Nslog Output 2

In my dUsefulStuff static library project I have a define which defines a logging macro depending on whether a preprocessor debug flag is set. It looks something like this

#ifdef DC_DEBUG
    #define DC_LOG(s, ...) \
        NSLog(@"%s(%d) %@", __PRETTY_FUNCTION__, \
        __LINE__, \
        [NSString stringWithFormat:(s), ## __VA_ARGS__])
#else
    // Effectively remove the logging.
    #define DC_LOG(s, ...)
#endif

This makes things really simple when writing code because I can effectively include a pile of logging when developing and it all just disappears when I compile a release version. The output from NSLog is controlled by NSLog and looks like this:

2012-05-02 14:08:49.452 dUsefulStuff simulator unit tests[21353:f803] -[UIColor(dUsefulStuff) isEqualToColor:](15) My message!

As you can see it prints out these items of information:

The problem I have is that this takes up a lot of space and some of it I’m just not interested in. This has been bugging me for a while now and I’ve finally found a solution – swap out NSLog for CFShow like this:

#ifdef DC_DEBUG
    #define DC_LOG(s, ...) \
        { \
            NSDate *now = [NSDate date]; \
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; \
            [formatter setDateFormat:@"mm:ss.SSS"]; \
            NSString *dateString = [formatter stringFromDate:now]; \
            CFShow([NSString stringWithFormat:@"%@ %x %s(%d) " s, dateString, pthread_mach_thread_np(pthread_self()), __PRETTY_FUNCTION__, __LINE__, ## __VA_ARGS__]); \
            [formatter release]; \
        }
#else
    // Effectively remove the logging.
    #define DC_LOG(s, ...)
#endif

which prints out a line like this:

08:49.452 f803 -[UIColor(dUsefulStuff) isEqualToColor:](15) My message!

Here all I’m printing is the current minutes:seconds:milliseconds, thread-id, location and then my text. It may seem like this is not much different but it can make quite a difference when there looking through a log.

Posted on 02 May 2012, View comments

New Version Of Simon V0.1.8

I’m pleased to release Simon build v0.1.8 The emphasis for this release is a re-worked UI. It’s been cleaned up and new functionality added. Now you can re-run all tests, some test or an individual test. This is particularly useful when debugging.

Posted on 08 April 2012, View comments

Fixing Xcode Permissions

Running XCode from a non-Admin user works reasonably well. However there are a few little quirks. One is that when you attempt to run your application in the Simulator, XCode will prompt for permission to modify system settings so that it can go into debug mode. This is rather annoying because it does this every time.

The root of the problem is that non-admin users are not in the “Developer Tools” security group. Even more annoying is that if you look in “users and groups” in settings, there is no such group present. The group exists, but it’s a unix group so you have to add the user at the command line. Googling around I found two commands that will do this for you:

# Option 1
sudo dscl . append /Groups/_developer GroupMembership <user>

# Option 2
dseditgroup -o edit -u <admin_user> -t user -a <user> _developer

Notice that the name of the group is _developer. I have not looked up these commands in detail to see exactly how they work, but they do the job. I went with option 2 which I found worked very well. It prompted me for the admins password and everything was fixed. I can now run my app without having to enter the admins userid and password every time.

Posted on 02 April 2012, View comments

New Version Of Simon V0.1.7

It’s been a while since I’ve announced a new version of Simon so I thought I’d drop a note to say that I’ve just uploaded the DMG file for version 0.1.7. This release contains a lot of new stuff and fixes, including the ability to enter text and wait for animations to finish. Enjoy!

Posted on 24 March 2012, View comments

Accessing Yellow Page Data Using Sapi

(Disclaimer – yes I work for Sensis :-)

Recently I was asked to do an upgrade a web site at work to replace an ageing search function with Sensis’s new SAPI search engine so I thought I’d put up a small discussion of how I went about it.

SAPI is a restful JSON based service which external developers can use to search both Yellow and White Pages listings combined. Providing a web based API allows developers to get creative and come up with new ways to utilise the information. In my case it was a fairly simple replacement of an established search with the SAPI search so it’s a good example of the basics of using this API.

To achieve this result I decided to make use of 3 core technologies apart from SAPI itself:

So lets get started

The first thing I had to do was to was to setup a HttpClient object ready to access the SAPI website and build a url to query the data. I’ll use some hard coded data here, but it’s easy to figure out where you would replace it with data from your input form. Note also that to be able to query the SAPI search engines you also need a SAPI Search API Key which is free for both development and production use.

// Get a client
DefaultHttpClient httpclient = new DefaultHttpClient();

// If you are behind a firewall and proxy you will probably need to enable this code.
// HttpHost proxy = new HttpHost("my.proxy.host.com.au", 8080);
// httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

// Build the parameters we are going to add to the url. 
// Note: There are many more than this you can use to refine this.
List<NameValuePair> parms = new ArrayList<NameValuePair>();
parms.add(new BasicNameValuePair("query", "Plumbers"));
parms.add(new BasicNameValuePair("rows", "20"));
parms.add(new BasicNameValuePair("page", "1"));
parms.add(new BasicNameValuePair("key", "xxxxxxxxxxxxxxxxxxxxx")); // This is your API key.
parms.add(new BasicNameValuePair("location", "Richmond"));
parms.add(new BasicNameValuePair("state", "VIC"));

// Use the developer connection URL.
URI uri = URIUtils.createURI("http", "api.sensis.com.au", "-1", "/ob-20110511/test/search", URLEncodedUtils.format(parms, "UTF-8"), null);

It’s pretty much what you are looking for, where and paging controls. SAPI has a lot of options you can add to refine your queries even further. For example, you can specify to include listings within a radius around a central location, or filter out potentially unsafe links for minors. Now lets execute the query and map the results into some java beans:

try {
	// Send the search Request to SAPI.
	HttpUriRequest request = new HttpGet(uri);
	HttpResponse response = httpclient.execute(request);
	
	// Get the Jackson object mapper which can read the results.
	ObjectMapper objMapper = new ObjectMapper();
	
	// Tell the object mapper to ignore JSON values we do not have a bean property for.
	// Otherwise it will throw an exception.
	objMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	
	// Tell the object mapper to use Mr Bean to create the bean classes.
	objMapper.registerModule(new MrBeanModule());
	
	// Unmarshal the JSON response into beans.
	SapiSearchResult sapiResult = objMapper.readValue(response.getEntity().getContent(), SapiSearchResult.class);
		
} finally {
	httpclient.getConnectionManager().shutdown();
}

So that’s the SAPI, Httpclient and Jackson parts done and we now have java beans all ready for using in our UI. But wait … Where did those beans come from?

Mr Bean

The reason I have not started by defining beans is because Mr Bean makes it so easy as to be trivial. Prior to this you would have had to define your java beans as fully realised classes. For example, here’s a simple version of SapiSearchResult (there are a lot more properties you can add! ):

public class SapiSearchResult {

	private int totalResults;
	
	private int code;
	
	private String message;
	
	private List<SapiListing> results;

	int getTotalResults() {
		return totalResults;
	}

	void setTotalResults(int totalResults) {
		this.totalResults = totalResults;
	}

	int getCode() {
		return code;
	}

	void setCode(int code) {
		this.code = code;
	}

	String getMessage() {
		return message;
	}

	void setMessage(String message) {
		this.message = message;
	}

	List<SapiListing> getResults() {
		return results;
	}

	void setResults(List<SapiListing> results) {
		this.results = results;
	}

}

Phew! image what you would have to write for the full API if your JSON processing API (unlike Jackson) cannot handle un-mapped properties and you have to boiler plate everything – SAPI can return a lot of data! Luckily I’m using Jackson so I can use two different tricks to avoid all this work:

With Mr Bean we can simply define a Java interface for the properties we want from the JSON response and let Mr Bean dynamically create the Java implementation of the bean class on the fly. So SapiSearchResult now becomes:

public interface SapiSearchResult {

	int getTotalResults();

	int getCode();

	String getMessage();

	List<SapiListing> getResults();
}

That is a huge difference. Mr Bean will handle creating the setters, constructors and implementations. It handles arrays, maps, lists, Enums, and everything else. The only thing it needs is the getter definition to tell it what to create. You can even add custom code simply by defining an abstract class instead of an interface and adding in the custom methods you want. Wicked.

Summary

I would have to say that between the ease of use of the SAPI search engine, HttpClient and the Jackson/Mr Bean combination, it has taken a surprisingly small amount of time to get a working result. Even when factoring in other stuff such as exception handling and UI.

I would also have to say that the SAPI web site is excellant. As a developer, it’s exactly what I want to see when it comes to documentation and support. The site also contains plenty of examples of using their API in Java, PHP and Ruby. Well done guys.

Posted on 08 March 2012, View comments

Older posts