Sunday, November 7, 2010

Linking .a library with XCode

Open the Info page for your Target (right-click on the target's node and choose Get Info) and locate the Library Search Path option. It (possibly amongst other folders) contains a reference to the folder you just dragged the libRemObjectsSDK.a from, such as "/Developer/RemObjects Software/Bin/iOS/Debug-iphoneos". To make switching between different platforms and configurations easier, select Configuration: All Configurations at the top of the dialog, and adjust the Library Search Path to replace "Debug-iphoneos" (or whatever other configuration/combination it currently shows) with "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)", so that the full folder name looks something like:

/Developer/RemObjects Software/Bin/iOS/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
This way, Xcode will automatically pick the right version of libRemObjectsSDK.a or libDataAbstract.a, regardless of whether you build Debug or Release, or whether you are working with the Simulator or on the real device.

See full article

Saturday, October 30, 2010

NSDictionary & NSMutableDictionary keys

NSDictionary keys must implement NSCopying. I required a map that used an UIView as a key, which does not implement NSCopying. The solution:


UISwitch* view = [[UISwitch alloc] init];


NSMutableDictionary* controls = [[NSMutableDictionary alloc] init];

[controls setObject:value forKey:[NSValue valueWithNonretainedObject:view]];


value = [controls objectForKey:[NSValue valueWithNonretainedObject: view]];

Thursday, October 28, 2010

Renaming XIB problem

Renaming an XIB using XCode's 'Rename' caused a crash. After app launched it failed trying to load the XIB because it was trying to load it with the previous name. The fix was to open the main window XIB with an XML editor (Dashcode) and manually change the view controller name [key=IBUINibName].

Sunday, October 24, 2010

OCUnit/SenTestingKit Notes

1. NSLog can be used from test cases, view output with .../Applications/Utilities/Console File->Open Console Log

2. Loading resources during unit testing doesn't work from mainBundle e.g. use:

NSString* path=[[NSBundle bundleForClass:[self class]] pathForResource:@"fname" ofType:@"plist"];

VS

NSString* path = [[NSBundle mainBundle] pathForResource:@"fname" ofType:@"plist"];


3. CGRectZero causes tests failure, CGRectMake(0, 0, 0, 0) does not.

Tuesday, October 12, 2010

Remove NSLog from release builds

Add the following to project's .pch

#ifndef __OPTIMIZE__
    #define NSLog(...) NSLog(__VA_ARGS__)
#else
    #define NSLog(...) {}
#endif


More info

Thursday, April 8, 2010

Click events on iPad


Designing a web page to be run on an iPhone, either as an embedded UIWebView or under Safari, you may want to consider how you handle click events. Click events feel sluggish because they don't trigger right away due to a delay to make sure the click isn't a double click. If your site requires snappy interaction (e.g. a game) then you have two options:
  1. Use 'touchstart' instead of 'click' when binding events
  2. If your app requires that you bind to 'touchend' then you'll need to remove the delay (good article)
iPhone developers are likely debugging their pages using Safari, so they need a way to be able to have code work in a click (Safari) and a touch (iPhone/iPad) environment. Here's an example of how to achieve this using jQuery.

First define a way to test if you're on an iPad:

(function($) {
    var userAgent = navigator.userAgent.toLowerCase();
    $.browser = {
        version : (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [ 0'0' ])[1],
        ipad : /ipad/.test(userAgent),
        safari : /webkit/.test(userAgent),
        opera : /opera/.test(userAgent),
        msie : /msie/.test(userAgent) && !/opera/.test(userAgent),
        mozilla : /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)
    };
})(jQuery);

Then set a global variable with the type of event you want to look for e.g.

window.clickEvent = 'click';
if($.browser.ipad) {
    window.clickEvent = 'touchstart';
}

Bind events based on window.clickEvent e.g.

    $('.someEl').bind(window.clickEvent, function(event) {
        handleSomeElEvent();
    });

Now you can debug your app in Safari and run your app on the iPad without code changes.

Friday, March 26, 2010

JMerge

JMerge JavaScript File Merger

Do you have a website with multiple JavaScript files? Each one of these files adds an HTTP request to your pages, effectively increasing the load time. JMerge is a simple web application that fetches and combines JavaScript files from a URL (using the PQLite PHP library) to reduce the number of HTTP requests. In essence, it does the work for you. All you do is type in a URL, pick the files you want to combine, and voila!

Wednesday, March 24, 2010

iPad hybrid application retrospective

Having just completed Sudoku Takeout for iPad I'm left impressed with how easy Apple has made it to write a 'hybrid' app. Hybrid iPhone/iTouch/iPad apps are just like other apps except they contain a browser view (UIWebView). UIWebView enables your app to embed a web page as one of its views and allows your Objective-C code to invoke JavaScript functions in that page.

Advantages:
  • Reuse your JavaScript
  • Very little Objective-C required
  • Files (e.g. CSS, HTML, JavaScript, images, etc.) can be local so no network connection is required
  • Safari is fast, supports HTML5, and has strong debugging tools

UIWebView limitations:
  • UIWebView does not support onOrientationChanged events, your app must listen for these events and then forward them into JavaScript
  • Doesn't play HTML5 Audio
  • Debugging JavaScript can only be done in Safari, not in the Simulator or device
  • localStorage.setItem throws intermittent QUOTA_EXCEEDED_ERR exceptions, calling removeItem before setItem seems to solve this
  • There is no way to get double clicks from JavaScript and apps cannot override touchesBegan event for UIWebView in Obj-C. Some people put a transparent view over UIWebView to catch touches.

Strategies that worked:
  • Generalized events - By using platform independent events (e.g. handleGuess vs. handleClick) the same logic used to handle a guess triggered by a keyboard on a web site can be reused for a guess triggered by a touch to a graphical picker on an iPad.
  • Create a persistence API - hiding the persistence implementation behind an API allowed the game logic to be unaffected when switching between cookies, localStorage or an ajax based server persistence.
  • CSS - put everything in CSS and use jQuery's add/removeClass to manipulate images and positions. Leverage off of selectors (e.g. [class~="portrait"]) in your CSS to handle orientation changes.
The 'hybrid' approach required some work arounds but in the end, created an exciting fully featured iPad app. Can you tell Sudoku Takeout is a hybrid?


Useful hybrid application links:

Sunday, March 7, 2010

Google Visualization APIs

Google has put out some great API for creating charts, gauges and tables. BUT they can't be used offline! This seems so un-Google like, wish I understood the reasoning behind that decision.

Firebug Lite

Firebug is an extension for Firefox, but what happens when you need to test your pages in Internet Explorer, Opera, and Safari?

The solution is Firebug Lite, a JavaScript file you can insert into your pages to simulate some Firebug features in browsers that are not named "Firefox".

Haven't tried this yet, but want to try it with IE.

Google Code Playground

Google Code Playground gives examples of not just their product offerings and API (e.g. Map, Calendar, Blogger, etc.) but also best practices used at Google (e.g. class structure, javascript/css loading, etc.). Lots of examples.

Saturday, March 6, 2010

CSS Sprite Generator

CSS Sprite Generator is a great free service that will take your web site images and combine them into a combined image as well as generate the CSS offsets for each image.

LocalStorage

HTML 5 provides several options to persist data on the client. One of these options is LocalStorage, a simple way to save key value pairs. The API are
  • getItem(key)
  • setItem(key, value)
  • removeItem(key)
  • clearAll()
It couldn't be easier, it's like the browser provides a hash table and handles persisting it. Unfortunately, the keys and values are strings. It's very similar to cookies except the information is not passed with the HTTP request.

localStorage and sessionStorage are supported by Firefox 3.5, Safari 4.0, and IE8. localStorage databases are scoped to an HTML5 origin, basically the tuple (scheme, host, port). This means that the database is shared across all pages on the same domain, even concurrently by multiple browser tabs. However, a page connecting over http:// cannot see a database that was created during an https:// session.

Storage events are fired and can be listened to so additional windows and or tabs can update based on data changes.

Notes: Got error QUOTA_EXCEEDED_ERR 22 when running in Safari with Private Browsing on. Error codes.