Archive

Posts Tagged ‘iphone’

iPhone Dev Note #24: Using Frank

May 7th, 2011 rupert No comments

If you are into testing iphone apps. You might want to check out Frank (https://github.com/moredip/Frank/) and watch the videos, especially this one Testing Your Mobile Apps with Selenium 2 and Frank March 30th, 2011 by Pete Hodgsen.

Main Tutorial

1. Follow this tutorial for steps 1-7 https://github.com/moredip/Frank/blob/master/tutorial/Tutorial.md Note: A bit outdated when you’re in steps 8.

2. Assuming you already have a working “frankified” project running, check by doing http://localhost:37265/

2-symbiote.png

3. Now do:

frank skeleton

4. This will give you a directory structure below.

1-frank-skeleton.png

5. If you try to run “cucumber”, it will complain that APP_BUNDLE_PATH is not set.

Note: Normally, this will be in a build directory within your project. However, I have my XCode build settings as shown below for the reason sometimes I forget to put the build folder on gitignore or svnignore (Sometimes my global git is a mess). Also, it becomes convenient for me to wipe out the whole directory after doing a “clean all”, makes me feel certain that I wiped it out myself. :) So, this is my personal preference only.

xcode-build-settings.png

So all the builds goes to this folder:
app-bundle-path.png

So, I append the APP_BUNDLE_PATH to my .bash_profile so I don’t have to do this everytime I run cucumber in terminal

  35 export vr=/Volumes/rupert
  36 export vrp=/Volumes/rupert/projects
  37 export vrpr=/Volumes/rupert/projects/rails3
  38 export vrpi=/Volumes/rupert/projects/iphone
  39 
  40 #For XCode Frank Testing
  41 export APP_BUNDLE_PATH=/Volumes/temp/iphone-builds/Debug-iphonesimulator/

Another alternative is to define the APP_BUNDLE_PATH in env.rb

require 'frank-cucumber'
#APP_BUNDLE_PATH = File.dirname(__FILE__) + "/../../build/Debug-iphonesimulator/EmployeeAdmin.app"
APP_BUNDLE_PATH = "/Volumes/temp/iphone-builds/Debug-iphonesimulator/Country-Frankified.app"

6. While the project-fankified.app is running on the iOS simulator, I then ran “cucumber”. However, the app went to a background state. If you double-click the iOS Simulator Home Button, you will get all the apps running in the background. After selecting the app, cucumber ran the tests, since it is trying to connect/ping to the frank HTTP server, and the device rotated as expected. However, this is now what I expected.

7. I expect to

  • Build and Run the Frankified app.
  • Run cucumber and see my tests fail or pass.

8. So I edited “launch_steps.rb” as shown below by commenting lines 6-9. This will prevent “press_home_on_simulator”

   1 Given /^I launch the app$/ do
   2 
   3   # kill the app if it's already running, just in case this helps 
   4   # reduce simulator flakiness when relaunching the app. Use a timeout of 5 seconds to 
   5   # prevent us hanging around for ages waiting for the ping to fail if the app isn't running
   6   #begin
   7   #  Timeout::timeout(5) { press_home_on_simulator if frankly_ping }
   8   #rescue Timeout::Error 
   9   #end
  10 
  11   require 'sim_launcher'
  12 
  13   app_path = ENV['APP_BUNDLE_PATH'] || APP_BUNDLE_PATH
  14   raise "APP_BUNDLE_PATH was not set. \nPlease set a APP_BUNDLE_PATH ruby constant or environment variable to the path of your compiled Frankified iOS app      bundle" if app_path.nil?
  15 
  16   if( ENV['USE_SIM_LAUNCHER_SERVER'] )
  17     simulator = SimLauncher::Client.for_iphone_app( app_path, "4.2" )
  18   else
  19     simulator = SimLauncher::DirectClient.for_iphone_app( app_path, "4.2" )
  20   end
  21 
  22   num_timeouts = 0
  23   loop do
  24     begin
  25       simulator.relaunch
  26       wait_for_frank_to_come_up
  27       break # if we make it this far without an exception then we're good to move on
  28 
  29     rescue Timeout::Error
  30       num_timeouts += 1
  31       puts "Encountered #{num_timeouts} timeouts while launching the app."
  32       if num_timeouts > 3
  33         raise "Encountered #{num_timeouts} timeouts in a row while trying to launch the app."
  34       end
  35     end
  36   end
  37 
  38   # TODO: do some kind of waiting check to see that your initial app UI is ready
  39   # e.g. Then "I wait to see the login screen"
  40 
  41 end

Notes and Issues

1. ld: duplicate symbol _OBJC_METACLASS_$_SBJsonParser
Frank comes with “json”, “uispec”, “cocoahttpserver”. Since my project also has json dependency, I just removed the json folder from frank/lib under “Group & Files”. Note: Only delete the references and don’t move to trash.
duplicate-json.png

2. I get “Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[UIImageView panoramaID]“
My project has MapKit as a dependency. I added this to main.m. See https://github.com/moredip/Frank/blob/d8a76223cad7df8143d8b6d3524c12494a65d069/main.m.sample

#ifdef FRANK
//UNCOMMENT THIS SECTION IF YOU'RE USING MapKit AND YOU ARE SEEING CRASHES IN iOS4
//Work around the issue in iOS 4 where exceptions thrown within a NSInvocation are not catchable. This was causing crashes in UIQuery::describeView when trying to dump the DOM w. Symbiote see http://groups.google.com/group/uispec/browse_thread/thread/1879741ebae978d/a90001a8956290af
@implementation NSObject (MapKitUISpecHack) 
- (id)_mapkit_hasPanoramaID { 
	return nil; 
} 
@end
#endif

3. Query a custom graphic ‘Home’ ToolbarButton
toolbar.png
In IB, you cannot specify an accessibilityLabel for a UIBarButtonItem inside a UIToolbar. So, to query this using

toolbarButton accessibilityLabel:'Home'

We need to specify this using code explicitly in viewDidLoad.

- (void)viewDidLoad {
	...
 
	#ifdef FRANK
	barButtonHome.accessibilityLabel = @"Home";
	barButtonSearch.accessibilityLabel = @"Search";
	barButtonMoreResults.accessibilityLabel = @"More";
	barButtonPageCurl.accessibilityLabel = @"Map Settings";
	#endif
 
	...
}

For the search button, the second one from the left and has a magnifying glass icon, it can be queried directly without specifying any code.

toolbarButton accessibilityLabel:'Search'
Categories: iphone Tags:

iPhone Dev Note #23: Downgrade an iPhone to iOS 3.1

October 16th, 2010 rupert No comments

One of the main reasons of downgrading the iOS version is for binary app testing. XCode3.2.3 allows you to compile with the latest iOS SDK using the Base SDK and set the targetiPhone version in the iPhone OS Deployment Target.

xcode-1.png

xcode-2.png

On the iPhone simulator, you could only test your application using 3.2 or 4.x. So, to test for devices > 3.1, we actually need a dedicated device for it. The iPhone3G that I passed on to my wife will suit the scenario.

Before doing anything, it is necessary to make backups.
1. Currently the iPhone 3G is on 4.1. Let’s Backup/Sync. Just in case we need to go back to 4.1. (Note: you can’t use this backup when 3.1 is loaded, yeah it sucks, find out later on.)

2. Contacts. Sync the contacts with Google Contacts. From there, we could have them exported to CSV later on just in case something goes wrong.

Below are the steps taken to downgrade iPhone 3G to iOS3.1:

1. Download the firmware from http://www.iclarified.com/entry/index.php?enid=750. I downloaded 3.1.0 (3G): iPhone1,2_3.1_7C144_Restore.ipsw

2. From XCode’s Organizer, choose the 3.1.0 and hit Restore.

3. During the restore process: “the baseband cannot be rolled back.”

baseband.gif

On the device, you will only see the Apple Logo and the progress bar to its full bar. Don’t panic.

device.jpg

4. Hit OK.

5. QUIT XCode. The device blacks out and you will see the connect to Itunes graphic.
iTunes.JPG

6. Install iRecovery and libusb-package.

iRecovery-1.png

Now we need to tell the device to auto-boot again, save the settings. Enter the ff commands:

iRecoverty-2.png

7. Disconnect the USB. Turn off the device (hold home+power button). Turn on device (power button). Open iTunes. We should be greeted with the Activation Screen.

activation.png

8. Restore from the last backup/sync doesn’t work.

backup-fail.png

9. Restore contacts from Google Contacts or accept bashing from wife? I’ll go with the google contacts sync. :)

10. Check version from settings
iphone-3.1.jpg

References:
http://www.iclarified.com/entry/index.php?enid=750
http://www.funkyspacemonkey.com/downgrade-iphone-os-40-313-mac-windows

Categories: iphone Tags:

iPhone Dev Note #22: UIAutomation

October 5th, 2010 rupert No comments

One of the problems with UIAutomation is the lack of documentation. At the time of writing this, the current documentation/reference that I use is found here ( http://developer.apple.com/library/ios/#documentation/DeveloperTools/Reference/UIAutomationRef/_index.html )

1. Works on the device but not on the simulator?

Copy com.apple.Accessibility.plist from 4.0 to 4.0.1

simulator.png

2. I can’t get the mainWindow().buttons(). Why?

Related code shown below have a navigation view added as a subview to window. Don’t confuse yourself that you need to get a subview from mainWindow() like mainWindow().elements[0]? It will not work. There’s nothing wrong with your code, however, open up your xib or Interface Builder, in my case, I will open MenuController.xib.

- (void)applicationDidFinishLaunching:(UIApplication *)application {
	[self createEditableCopyOfDatabaseIfNeeded];
 
	MenuController *mainMenuController = [[MenuController alloc] initWithNibName:@"MenuController" bundle:nil];
	navigationController = [[UINavigationController alloc] initWithRootViewController:mainMenuController];
 
	[window addSubview: [navigationController view]];
	[mainMenuController release];
 
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}

test-1.gif

For some reason, when the “Accessibility” is enabled on the view, it doesn’t work. I am not exactly sure why, but UIAutomation only recognizes the window and not its sub-elements. Unticking this checkbox, allows us to get all the elements under mainWindow(). This has been a bugger and I spent 4 hours figuring this.

test-2.gif

After you finished updating your elements in Interface Builder, make sure you build and deploy the app in the simulator. This will recreate/replace the binary and remember that it is the target app of Instruments. Afterwards, launch the test script and see if it works now.

3. How was I able to see the element heirarchy?

...mainWindow().logElementTree();

As always don’t keep banging your head that there’s something wrong with your view heirarchy. I suggest you read thoroughly the reference posted above to understand what is available for mainWindow().

4. Instrument tips
- Command + R will stop or record the test.
- Your app is not listed in the “Choose target”? Well you could navigate to the /Users/rupert/Library/Application Support/iPhone Simulator/4.0.1/Applications/94EC26E5-D76B-4003-9675-8F0DDE111D01/beforeUdig.app/ (which sucks) OR from XCode > Run > Run With Perfomance Tool > Leaks. Once Leaks opens, it automatically chooses your current app as the target. Close leaks and go back to Instruments. From here, you will see your app listed in the target.

Categories: iphone Tags: , ,

iPhone Bug Note #1: Route-Me and Three20 in the same Project. _aasin dup symbol when building on iOS4

August 14th, 2010 rupert Comments off

It seems route-me and three20 doesn’t mix well on iOS4. When building my project (Philippines), I get an error on _aasin duplicate symbol, which was also discussed on route-me issue 138 at code.google.com

Three20 needs linker flag shown below:

-all_load

The only way to go about this was linking proj4 directly from the project and removing it from route-me, thus no need to remove the “all_load” linker flag. Note: I tried removing the “all_load” linker flag from my project and it builds fine. However, the app crashes.

1. Drag the proj4.xcodeproj from RouteMe to your main project.

2. Afterwards delete the proj4.xcodeproj from RouteMe.
RouteMe.png

3. Your project should now reference both MapView and Proj4.
three20.png

Categories: iphone Tags:

iPhone Dev Note #21: Route-Me Offline Mapping from Database

August 12th, 2010 rupert 22 comments

Part I: Download the osm (openstreetmap) tiles
1. Download the tiles from osm using downloadosmtiles.pl
- Download Geo-OSM-Tiles-0.02.tar.gz from CPAN
- See README file. Compile and build

   perl Makefile.PL
   make
   make test
   make install

- Copy downloadosmtiles.pl to /usr/bin
- Usage:

downloadosmtiles.pl --lat=min_lat:max_lat --lon=min_long:max_long --zoom=min_zoom:max_zoom

How do you set the min_lat, max_lat and min_long, max_long?
- Go to www.openstreetmap.org
- Click on the “Edit” tab
- You will see the extent or bounds of the map under the section “Area to Export”
- Alternatively, you can click on “Manually select a different area” to specifically choose an area.

osm.png

downloadosmtiles.pl --lat=6.9443:7.2261 --lon=125.5082:125.7104 --zoom=6:12

The tiles will be downloaded to the current directory.
dir.png

Part II:Put the tiles in the sqlite database
1. Read Frank’s email to route-me group regarding map2sqlite

2. Download map2sqlite-1.0.tar.bz2.

3. Build the map2sqlite XCodeProj. Afterwards, find map2sqlite and drop it in /usr/bin.

cp map2sqlite /usr/bin

4. Run map2sqlite to import the tiles in sqlite.

map2sqlite -db ph-1.0.0.db -mapdir ph-osm-map/
 
2010-08-12 17:24:40.749 map2sqlite[14113:903] map2sqlite 1.0
2010-08-12 17:24:40.756 map2sqlite[14113:903] Creating ph-1.0.0.db
2010-08-12 17:24:40.761 map2sqlite[14113:903] Importing map tiles at ph-osm-map/
2010-08-12 17:25:03.169 map2sqlite[14113:903] 
2010-08-12 17:25:03.170 map2sqlite[14113:903] Map statistics
2010-08-12 17:25:03.170 map2sqlite[14113:903] --------------
2010-08-12 17:25:03.171 map2sqlite[14113:903] map db:            ph-1.0.0.db
2010-08-12 17:25:03.171 map2sqlite[14113:903] file size:         13758464 bytes
2010-08-12 17:25:03.172 map2sqlite[14113:903] tile directory:    ph-osm-map/
2010-08-12 17:25:03.172 map2sqlite[14113:903] number of tiles:   9091
2010-08-12 17:25:03.173 map2sqlite[14113:903] zoom levels:       6 - 11
2010-08-12 17:25:03.218 map2sqlite[14113:903] zoom level  6:        12 tiles, (    28,    52)x(    31,    54), {x=112.500000,y=21.943047}x{x=129.375000,y=0.000000}
2010-08-12 17:25:03.219 map2sqlite[14113:903] zoom level  7:        35 tiles, (    56,   105)x(    62,   109), {x=115.312500,y=21.943047}x{x=129.375000,y=2.811371}
2010-08-12 17:25:03.222 map2sqlite[14113:903] zoom level  8:       117 tiles, (   112,   210)x(   124,   218), {x=115.312500,y=21.943047}x{x=127.968750,y=4.214943}
2010-08-12 17:25:03.223 map2sqlite[14113:903] zoom level  9:       450 tiles, (   225,   420)x(   249,   437), {x=115.312500,y=21.289375}x{x=127.968750,y=4.214943}
2010-08-12 17:25:03.225 map2sqlite[14113:903] zoom level 10:      1715 tiles, (   450,   841)x(   498,   875), {x=115.664062,y=21.289375}x{x=127.968750,y=4.565474}
2010-08-12 17:25:03.231 map2sqlite[14113:903] zoom level 11:      6762 tiles, (   900,  1683)x(   997,  1751), {x=115.839844,y=21.289375}x{x=127.968750,y=4.565474}

5. ph-osm-map is 43.9 MB but was compressed to ph-1.0.0.db (13.8 MB)

Part III: Downlaod the route-me code from trunk and run some examples.
- Follow this previous tutorial

Part IV: Patch the trunk to incorporate the RMDBMapSource from Frank Schroder

1. What we need to add to the trunk. Download RMDBMapSource.zip

+ RMDBMapSource.h
+ RMDBMapSource.m
+ RMDBTileImage.h
+ RMDBTileImage.m

Copy the files above to the “Map” directory.

route-me-1.png

2. Edit RMTileImage.h and RMTileImage.m base on the patch below.

Index: MapView/Map/RMTileImage.h
===================================================================
--- MapView/Map/RMTileImage.h	(revision 605)
+++ MapView/Map/RMTileImage.h	(working copy)
@@ -37,8 +37,10 @@
 #import "RMNotifications.h"
 #import "RMTile.h"
 #import "RMTileProxy.h"
+#import "FMDatabase.h"
 
 @class RMTileImage;
+@class NSData;
 
 @interface RMTileImage : NSObject {
 	// I know this is a bit nasty.
@@ -64,6 +66,7 @@
 + (RMTileImage*)imageForTile: (RMTile) tile withURL: (NSString*)url;
 + (RMTileImage*)imageForTile: (RMTile) tile fromFile: (NSString*)filename;
 + (RMTileImage*)imageForTile: (RMTile) tile withData: (NSData*)data;
++ (RMTileImage*)imageForTile: (RMTile) tile fromDB: (FMDatabase*)db;
 
 - (void)moveBy: (CGSize) delta;
 - (void)zoomByFactor: (float) zoomFactor near:(CGPoint) center;
Index: MapView/Map/RMTileImage.m
===================================================================
--- MapView/Map/RMTileImage.m	(revision 605)
+++ MapView/Map/RMTileImage.m	(working copy)
@@ -29,6 +29,7 @@
 #import "RMWebTileImage.h"
 #import "RMTileLoader.h"
 #import "RMFileTileImage.h"
+#import "RMDBTileImage.h"
 #import "RMTileCache.h"
 #import "RMPixel.h"
 #import <QuartzCore/QuartzCore.h>
@@ -108,6 +109,11 @@
 	return [image autorelease];
 }
 
++ (RMTileImage*)imageForTile:(RMTile) _tile fromDB: (FMDatabase*)db
+{
+	return [[[RMDBTileImage alloc] initWithTile: _tile fromDB:db] autorelease];
+}
+
 -(void) cancelLoading
 {
 	[[NSNotificationCenter defaultCenter] postNotificationName:RMMapImageLoadingCancelledNotification

We just need to add these lines on RMTileImage.h:

+#import "FMDatabase.h"
...
++ (RMTileImage*)imageForTile: (RMTile) tile fromDB: (FMDatabase*)db;

do the same for RMTileImage.m:

+#import "RMDBTileImage.h"
...
++ (RMTileImage*)imageForTile:(RMTile) _tile fromDB: (FMDatabase*)db
+{
+	return [[[RMDBTileImage alloc] initWithTile: _tile fromDB:db] autorelease];
+}

UPDATE (OCT 15, 2010): I am attaching my current RMTileImage.h and RMTileImage.m so you guys could double-check the changes I made. RMTileImage.zip

4. Still with me? Comment NSAssert on 609 on RMMapContents.m
rmcontents.png

Part V: RouteMeSampleMapDBOffline code
1. Download RouteMeSampleMapDBOffline.zip

2. Drop the project in the samples directory.
sample_proj.png

3. Build. You should be able to build this since the header path is relative to the route-me trunk.
build-config.png

4. Run from the simulator
finish.png

Categories: iphone, sqlite3 Tags: , ,