Archive

Archive for the ‘iPhone’ Category

Kiwi as a static framework or Universal Library

January 27, 2012 Leave a comment

A problem commonly encountered when using open-source iOS frameworks is the lack of a fully-functional framework facility in xCode. Part of the issue is that Apple does not allow dynamic linking on iOS devices, the other is that there are two different architectures that need to be supported by libraries targeting both armv6 (up to iPhone 3G) and armv7 devices (iPhone 3GS and later). On top of that, we also need a binary that will run on the simulator (x86).

The easiest solution to the library problem in XCode is using project dependencies to build libraries in the configuration you need them. When taking a source dependency is not desirable, you are pretty much left to your own if the OSS project doesn’t provide binaries.

Fortunately enough, it’s not too difficult to build your own universal frameworks. Below are the steps I use for building a version of Kiwi:

  1. Grab the Universal Framework XCode templates from https://github.com/kstenerud/iOS-Universal-Framework
  2. Install the Fake framework flavor (although the Real framework flavor should work as well)
  3. Create a new xCode project with the Fake framework template
  4. Add all source files of Kiwi (make sure to check the Copy to destination group folder box)
  5. Select the Kiwi static library target, project editor, build phases, Copy Headers, select all headers in the Project Group, right click and select move to Public
  6. Select the Kiwi static library target, project editor, build phases, link binary with libraries and add SenTestingKit.framework
  7. Build
  8. Go to the  Project Navigator (Cmd-1) and select Products Kiwi.framework. Right-Click and select “Show in Finder”
  9. You should see two folders: Kiwi.framework and Kiwi.embeddedframework – Kiwi.framework is the one we need
  10. Copy the Kiwi.framework folder into your lib folder
  11. Open the project you want to use Kiwi.framework in and select your target, project editor, build phases, link binary with libraries, click + and add Kiwi.framework from your lib folder

That’s it. Takes less than two minutes once you know the trick.

Categories: iPhone, Objective-C, Tools

Continous Deployment for Apps via testflightapp

January 26, 2012 Leave a comment

The benefits of continous integration are widely known. By extending the ideas of continous integration to the full software lifecycle, continous delivery becomes an inevitable practice. Especially in the context of managing a beta program for mobile devices, to most of which I as a developer have no physical access, the ability to have fully automated deployments is crucial.

Testflightapp.com provides a great service to iOS developers by managing app provisioning and deployments. They do also provide easy to use instrumentation facilities.

Continous deployment with testflightapp.com is a breaze, all you need your build-server to do is interact with a straightforward web-api to upload your ipa packages.
Here’s the script I’m using for RowMotion:

#!/bin/bash
# 

# testflightapp.com tokens
API_TOKEN="YOUR_API_TOKEN"
TEAM_TOKEN="YOUR_TEAM_TOKEN"

PRODUCT_NAME="RowMotion"
ARTEFACTS="$PWD/Artefacts"

SIGNING_IDENTITY="iPhone Distribution"
PROVISIONING_PROFILE="$PWD/id/RowMotionAdHoc.mobileprovision"

# calculated vars
OUT_IPA="${ARTEFACTS}/${PRODUCT_NAME}.ipa"
OUT_DSYM="${ARTEFACTS}/${PRODUCT_NAME}.dSYM.zip"

# kill artefacts directory
rm -rf $ARTEFACTS
mkdir $ARTEFACTS

# compile
echo "##teamcity[compilationStarted compiler='xcodebuild']"
xcodebuild -workspace RowMotion.xcworkspace -scheme RowMotion -sdk iphoneos5.0 -configuration Release build archive
buildSucess=$?

if [[ $buildSucess != 0 ]] ; then
echo "##teamcity[message text='compiler error' status='ERROR']"
echo "##teamcity[compilationFinished compiler='xcodebuild']"
exit $buildSucess
fi

echo "##teamcity[compilationFinished compiler='xcodebuild']"

#ipa
echo "##teamcity[progressMessage 'Creating .ipa for ${PRODUCT_NAME}']"

DATE=$( /bin/date +"%Y-%m-%d" )
ARCHIVE=$( /bin/ls -t "${HOME}/Library/Developer/Xcode/Archives/${DATE}" | /usr/bin/grep xcarchive | /usr/bin/sed -n 1p )
DSYM="${HOME}/Library/Developer/Xcode/Archives/${DATE}/${ARCHIVE}/dSYMs/${PRODUCT_NAME}.app.dSYM"
APP="${HOME}/Library/Developer/Xcode/Archives/${DATE}/${ARCHIVE}/Products/Applications/${PRODUCT_NAME}.app"

/usr/bin/xcrun -sdk iphoneos PackageApplication -v "${APP}" -o "${OUT_IPA}" --sign "${SIGNING_IDENTITY}" --embed "${PROVISIONING_PROFILE}"

#symbols
echo "##teamcity[progressMessage 'Zipping .dSYM for ${PRODUCT_NAME}']"
/usr/bin/zip -r "${OUT_DSYM}" "${DSYM}"

# prepare build notes
NOTES=`hg tip`

#upload
echo "##teamcity[progressMessage 'Uploading ${PRODUCT_NAME} to TestFlight']"

/usr/bin/curl "http://testflightapp.com/api/builds.json" \
-F file=@"${OUT_IPA}" \
-F dsym=@"${OUT_DSYM}" \
-F api_token="${API_TOKEN}" \
-F team_token="${TEAM_TOKEN}" \
-F notes="${NOTES}" \
-F notify="True" \
-F distribution_lists="Private"

Make sure to adapt the script to your requirements. One trick I’m fond of is automatically providing SCM information in the build notes (the step with executing hg tip does just that).
For deployments I’m using two lists, a private one to which all builds will be published, and a public one to which I can selectively deploy. What’s so great about testflightapp.com is that it will automatically send emails to notify my testers about the new build and will then allow me to monitor installs.

iOS: Detect Personal Hotspot

July 22, 2011 Leave a comment

When you want to detect the type of available connections on an iPhone, the best resource you can find on the web is the sample code from Erica Sadun’s excellent iPhone Cookbook book (which I can wholeheartedly recommend). The sample code can be found on github (look into 02 and 03): https://github.com/erica/iphone-3.0-cookbook-/tree/master/C13-Networking

While the solution presented is great, it fails to work on an iPhone 4 that has the Personal Hotspot feature enabled. In this scenario, the iPhone will create a network interface called “ap0” that bridges through to “en0” (WiFi) and “pdp_ip0” (3G) . Since “en0” will not be marked as AF_INET interface in this scenario, the approach Erica outlined will fail here. Here’s a dump of the available interfaces, their loopback and AF_INET status and their assigned address:

2011-07-22 12:59:07.120 RowMotion[286:707] name: lo0, inet: 0, loopback: 0, adress: 24.3.0.0
2011-07-22 12:59:07.126 RowMotion[286:707] name: lo0, inet: 0, loopback: 0, adress: 0.0.0.0
2011-07-22 12:59:07.129 RowMotion[286:707] name: lo0, inet: 1, loopback: 0, adress: 127.0.0.1
2011-07-22 12:59:07.134 RowMotion[286:707] name: lo0, inet: 0, loopback: 0, adress: 0.0.0.0
2011-07-22 12:59:07.137 RowMotion[286:707] name: en0, inet: 0, loopback: 1, adress: 6.3.6.0
2011-07-22 12:59:07.141 RowMotion[286:707] name: ap0, inet: 0, loopback: 1, adress: 6.3.6.0
2011-07-22 12:59:07.145 RowMotion[286:707] name: pdp_ip0, inet: 0, loopback: 1, adress: 255.7.0.0
2011-07-22 12:59:07.149 RowMotion[286:707] name: pdp_ip0, inet: 1, loopback: 1, adress: 10.217.22.129
2011-07-22 12:59:07.154 RowMotion[286:707] name: pdp_ip1, inet: 0, loopback: 1, adress: 255.7.0.0
2011-07-22 12:59:07.157 RowMotion[286:707] name: pdp_ip2, inet: 0, loopback: 1, adress: 255.7.0.0
2011-07-22 12:59:07.161 RowMotion[286:707] name: pdp_ip3, inet: 0, loopback: 1, adress: 255.7.0.0
2011-07-22 12:59:07.165 RowMotion[286:707] name: en1, inet: 0, loopback: 1, adress: 6.3.6.0
2011-07-22 12:59:07.168 RowMotion[286:707] name: bridge0, inet: 0, loopback: 1, adress: 6.7.6.0
2011-07-22 12:59:07.172 RowMotion[286:707] name: bridge0, inet: 1, loopback: 1, adress: 172.20.10.1

See that last line? Yep, that’s the bridge interface we need to use to communicate with other devices on our “personal hotspot”. Here’s how to ammend Erica’s code to make personal hotspots transparent:

// Matt Brown's get WiFi IP addy solution
// http://mattbsoftware.blogspot.com/2009/04/how-to-get-ip-address-of-iphone-os-v221.html
+ (NSString *) localWiFiIPAddress
{
    BOOL success;
    struct ifaddrs * addrs;
    const struct ifaddrs * cursor;
    
    success = getifaddrs(&addrs) == 0;
    if (success) {
        cursor = addrs;
        while (cursor != NULL) {
            
            NSString *name = [NSString stringWithUTF8String:cursor->ifa_name];
            
            NSLog(@"available network interfaces: name: %@, inet: %d, loopback: %d, adress: %@", name, cursor->ifa_addr->sa_family == AF_INET, (cursor->ifa_flags & IFF_LOOPBACK) == 0, [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)]);
            
            // the second test keeps from picking up the loopback address
            if (cursor->ifa_addr->sa_family == AF_INET && (cursor->ifa_flags & IFF_LOOPBACK) == 0) 
            {
                if ([name isEqualToString:@"en0"] || [name isEqualToString:@"bridge0"])  //  Wi-Fi adapter, or iPhone 4 Personal hotspot bridge adapter
                    return [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
            }
            cursor = cursor->ifa_next;
        }
        freeifaddrs(addrs);
    }
    return nil;
}

+ (BOOL) activeWLAN
{
    return ([self localWiFiIPAddress] != nil);
}

+ (BOOL) activePersonalHotspot
{

    // Personal hotspot is fixed to 172.20.10
    return ([self activeWLAN] && [ hasPrefix:@"172.20.10"]);
}

+ (BOOL) activeWLAN
{
    return ([self localWiFiIPAddress] != nil);
}

+ (BOOL) activePersonalHotspot
{
    // Personal hotspot is fixed to 172.20.10
    NSString* localWifiAddress = [self localWiFiIPAddress];
    return (localWifiAddress != nil && [localWifiAddress hasPrefix:@"172.20.10"]);
}

I hope this will find it’s way into the sample code soon. Pull request is pending.

Objective-C Pitfall: Synthesized Properties without backing field

June 29, 2011 2 comments

This is just a quick and short post about an Objective-C pitfall I have encountered today. When using synthesized properties, you do normally supply a backing field:

@property (nonatomic, readwrite, retain) Message* message = message_;

This will synthesize a getter and setter, that will use message_ as its backing field. Since I found out one can go clever and ommit the backing field, so a simple line like this will work too:

@property (nonatomic, readwrite, retain) Message* message;

However, now we get into a bit of trouble when accessing the property. Contrary to the behavior in Java or C#, you now get something different when accessing 

self.message

 vs.

message

. While the former will use the synthesized getter, the latter will use the synthesized backing field directly. This is a bit unexpected (I thought the backing field would be anonymous). So, my general advice would be to always use backing fields in your synthesized backing fields, so you don’t accidentally forget a “self.”. (This is water on the mills of people that advocate _not_ using the dot syntax for properties).

 

 

Categories: iPhone, Objective-C

GoodReader and Mercurial for the Ultimate Student Workflow

June 8, 2011 Leave a comment

Being the proud owner of a shiny new iPad2 for the last month or so, I found it to be a valuable companion at University. No, not for browsing stackoverflow, keeping up with RSS and E-Mail, but for managing lecture slide decks, assignments etc.

In the past semester, I started managing my documents in a mercurial repository that is synced against my private mercurial server installation. This made it easier for me to keep my multiple devices synchronized (who would need that iCloud thing…). My first attempts at using the iPad for these tasks was using iBooks. iBooks is not bad, but its so utterly limited that it sucks really hard from times to times. Especially annoying is that it has absolutely no file management capabilities whatsoever.

I found GoodReader to be a great alternative. It has excellent file management and supports annotating pdfs. But the best thing is its support for synchronizing your files:

  1. Make sure your iPad is connected to the same network as your host computer
  2. Launch GoodReader, open the WiFi sync mode via the WiFi symbol
  3. Mount http://yourIpad’sIpAdress:8080 has a network folder
  4. Open a terminal, cd into your mount point  and run hg clone /yourCentralRepository
  5. The next time you want to sync run hg pull -U
It takes less than 5 minutes to set up, and is a pretty damn smart workflow!
Categories: iPhone, Tools

iOS Development Continous Integration Setup

June 7, 2011 Leave a comment

After almost a year of absence, I’m in the middle of getting back to iOS Development. Since my departure from the Apple ranch, a lot has changed and new developer tools have emerged. Professional software development has become significantly easier on this platform, but I feel the tooling still isn’t on par with what other ecosystems provide.

Nonetheless, where there’s will there’s a way. In this Series of posts, I’m going to outline the setup I’m currently running. I’ll point to resources that helped me along the way and will describe how to combine the pieces to make it all fit together. I don’t plan publishing in any particular order and since business goes first, I am going to write when I find the time for it.

All posts of the series will be put into the category iOS Continous Integration Series, which also is the best place to find them.

The setup will consist of:
The Dev’s Private Cloud (aka a HyperV Server) hosting:
– Teamcity CI Server
– Build Agents
– Mercurial via hgweb.cgi

OCUnit
Kiwi for Acceptance Testing
OCMock as Isolation framework
GCov for Code Coverage

Planned Posts:

Running Ubuntu Server in HyperVi
Setting up a TeamCity Server on Ubuntu
Setting up a Mercurial Server
iOS Testing Frameworks revisited
Running OCUnit on a build agent
Connecting OCUnit to Teamcity
Integrating OCMock
Using Kiwi for Acceptance Testing
Retrieving Coverage information with GCov
XCode Alternatives

iPhone resetting experience

December 3, 2009 Leave a comment

My iPhone has started to behave somewhat “laggy” recently, taking too long waking up from standby when pressing a button, repeated hangs when using the virtual keyboard to write  emails etc.

My device also had accumulated a lot of “history” in terms of apps I don’t use any longer but was too lazy to delete and has been jailbreaked once.So I decided it would be the easiest to just reset the firmware, went into iTunes and hit the restore button.

iTunes proceeded taking a full backup of my decvice and restored the firmware afterwards, which took an hour approximately. After I reactivated the iPhone, iTunes asked me if I wanted to restore my personal settings and sync my apps, leaving me to choose what apps should go on the device again and let me even sort them on the springboard from within iTunes (makes it so much easier!). After that I synced my music and pictures and I was pretty impressed with the result:

  1. No hacking required, everything easily click-through
  2. All my apps data was conserved (notes, gps trails etc.)
  3. All my email settings was still there
  4. My music was all there
  5. My photos were all there

The complete procedure took me probably half an hour, rest of the time waiting for iTunes to finish :-)

Categories: iPhone
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: