After releasing my first RubyMotion app (“FC St.Pauli chants”:http://nofail.de/chant/), I’m currently working on “another one”:https://github.com/phoet/freifunk_ios/ for “Freifunk Hamburg”:http://hamburg.freifunk.net/.
I am using “Testflight”:https://testflightapp.com/ with a group of 10+ beta testers, to verify that my app is working properly and has no issues whatsoever.
h2. The Review Process
When I think that an app is close to be feature complete, I create a version that can be pushed to the AppStore. This process involves setting up a distribution certificate and other stuff in iTunes connect. It’s a little time consuming and the review-process usually takes some weeks, especially for new apps.
I was not totally surprised that the first revision got rejected by Apple, eventhough they stated, that the app crashed on startup, which none of my beta testers had experienced. I thought that this might be due to some iOS 7 related changes.
So I just submitted an updated version to the review process. Unfortunately that version also got rejected…
h3. The Crash Report
The latest reject was also due to a crash on startup. In case of a crash, Apple provides a “Crash Report”:https://gist.github.com/phoet/abfe691b2c4fce9d8c0d in the Resolution Center (it’s super easy to find… NOT: iTunes Connect>Manage Your Apps>[The App Icon]>View Details>Links>Resolution Center).
There is a lot of info in such a crash report. It starts with some basic infos about the device and app environment:
Incident Identifier: 7B2A05A2-E9C3-47F8-90DC-3C37BA634D69 CrashReporter Key: edb549cbc4b5919b7c8a04beb0db00da1ada7c41 Hardware Model: xxx Process: freifunk  Path: /var/mobile/Applications/44317BE9-04A0-4B3E-A3D8-24134AE1B06C/freifunk.app/freifunk Identifier: de.nofail.freifunk Version: 0.0.1 (0.0.1) Code Type: ARM (Native) Parent Process: launchd  Date/Time: 2013-09-13 08:06:36.406 -0700 OS Version: iOS 7.0 (11A465) Report Version: 104
then comes the root cause:
Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Triggered by Thread: 0
followed by the backtrace when the crash occured:
Last Exception Backtrace: (0x2fe08f4e 0x3a6156aa 0x2f85ac 0x2f86d8 0x2608f4 0x25df9c 0x2c78f8 0x2e887c 0x2e003a 0x2c81c4 0x232d50 0x2e8854 0x2e003a 0x22f9e8 0x22fc60 0x2302f0 0x325eacb2 0x325ea702 0x325e4d0e 0x3257f6a2 0x3257e9a4 0x325e44f8 0x34a38708 0x34a382f2 0x2fdd39e2 0x2fdd397e 0x2fdd2152 0x2fd3cce2 0x2fd3cac6 0x325e3794 0x325dea3c 0x42d9c 0x42860)
infos about the threads that were active during the crash:
Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: [...] 9 CoreFoundation 0x2fd3cd58 CFRunLoopRunSpecific + 636 10 CoreFoundation 0x2fd3cac6 CFRunLoopRunInMode + 102 11 UIKit 0x325e3794 -[UIApplication _run] + 756 12 UIKit 0x325dea3c UIApplicationMain + 1132 13 freifunk 0x00042d9c 0x32000 + 69020 14 freifunk 0x00042860 0x32000 + 67680 Thread 1 name: Dispatch queue: com.apple.libdispatch-manager Thread 1: 0 libsystem_kernel.dylib 0x3abc183c kevent64 + 24 1 libdispatch.dylib 0x3ab02220 _dispatch_mgr_invoke + 228 2 libdispatch.dylib 0x3ab01fa6 _dispatch_mgr_thread$VARIANT$mp + 34 Thread 2: 0 libsystem_kernel.dylib 0x3abd4c7c __workq_kernreturn + 8 1 libsystem_pthread.dylib 0x3ac3adc6 _pthread_wqthread + 306 2 libsystem_pthread.dylib 0x3ac3ac80 start_wqthread + 4 Thread 3 name: WebThread Thread 3: [...] 6 WebCore 0x37ebebae RunWebThread(void*) + 414 7 libsystem_pthread.dylib 0x3ac3cc1a _pthread_body + 138 8 libsystem_pthread.dylib 0x3ac3cb8a _pthread_start + 98 9 libsystem_pthread.dylib 0x3ac3ac8c thread_start + 4 Thread 4: 0 libsystem_kernel.dylib 0x3abd4c7c __workq_kernreturn + 8 1 libsystem_pthread.dylib 0x3ac3adc6 _pthread_wqthread + 306 2 libsystem_pthread.dylib 0x3ac3ac80 start_wqthread + 4 Thread 0 crashed with ARM Thread State (32-bit): r0: 0x00000000 r1: 0x00000000 r2: 0x00000000 r3: 0x00002060 r4: 0x00000006 r5: 0x3ca9318c r6: 0x00000000 r7: 0x27dce594 r8: 0x1450efa0 r9: 0x00000001 r10: 0x14569fb0 r11: 0x00000000 ip: 0x00000148 sp: 0x27dce588 lr: 0x3ac3da33 pc: 0x3abd41fc cpsr: 0x00000010
and the address space:
Binary Images: 0x32000 - 0x334ffb freifunk armv7 <3ae0aadc89773fe8a07f885fdaff2237> /var/mobile/Applications/44317BE9-04A0-4B3E-A3D8-24134AE1B06C/freifunk.app/freifunk 0x2bee8000 - 0x2bf0878a dyld armv7
/usr/lib/dyld 0x2edd9000 - 0x2edd9fff Accelerate armv7 <8e17835efc9234da89e3080c47fad906> /System/Library/Frameworks/Accelerate.framework/Accelerate [...]
h3. Analyzing the Report
It’s not obvious what to do with such a report so Apple gives a “Technical Note”:https://developer.apple.com/library/ios/technotes/tn2151/_index.html on this topic. You can also find some information in the “Debugging Guide”:http://www.rubymotion.com/developer-center/articles/debugging/#_debugging_symbols of the RubyMotion docs.
The big problem with the crash report are the missing identifiers. There is no hint for what might stand behind any of the memory addresses noted in the backtrace or whatever. That’s were “Symbolication”:http://stackoverflow.com/questions/1460892/symbolicating-iphone-app-crash-reports comes into play.
h3. Symbolicating your RubyMotion Crash Report
Symbolication is the the process of converting the numeric addresses in the stacktrace to matching parts of the codebase. In order to get this working, you will have to keep the
*.dSYM files that were generated with your release build. They are in the same folder as your
.ipa file, that you are uploading to the AppStore (ie
_It’s a good idea to backup those files!_ Otherwise you won’t have the possibility to ever know why a crash happened.
Symbolication is pretty easy “once you know what to do”:https://groups.google.com/forum/#!topic/rubymotion/ib3wHcHNFWY. You can use the
atos command line utility to “convert numeric addresses to symbols of binary images or processes”. Go to the release folder of your app and run the command with the exception backtrace, use the first address of the binary image as your base address and point to the executable binary:
cd build/iPhoneOS-5.1-Release atos -o freifunk -l 0x32000 -arch armv7 0x2fe08f4e 0x3a6156aa 0x2f85ac 0x2f86d8 0x2608f4 0x25df9c 0x2c78f8 0x2e887c 0x2e003a 0x2c81c4 0x232d50 0x2e8854 0x2e003a 0x22f9e8 0x22fc60 0x2302f0 0x325eacb2 0x325ea702 0x325e4d0e 0x3257f6a2 0x3257e9a4 0x325e44f8 0x34a38708 0x34a382f2 0x2fdd39e2 0x2fdd397e 0x2fdd2152 0x2fd3cce2 0x2fd3cac6 0x325e3794 0x325dea3c 0x42d9c 0x42860
This produces a somewhat readable output:
0x2fe08f4e 0x3a6156aa __vm_raise() (in freifunk) + 468 rb_vm_raise (in freifunk) + 280 rb_exc_raise (in freifunk) + 4 rb_name_error (in freifunk) + 92 rb_mod_const_missing (in freifunk) + 116 dispatch_rimp_caller(objc_object* (*)(objc_object*, objc_selector*, ...), unsigned long, objc_selector, int, unsigned long const*) (in freifunk) + 272 rb_vm_dispatch (in freifunk) + 4502 rb_const_get_0 (in freifunk) + 684 rb_scope__init_testflight__ (in freifunk) (app_delegate.rb:45) dispatch_rimp_caller(objc_object* (*)(objc_object*, objc_selector*, ...), unsigned long, objc_selector, int, unsigned long const*) (in freifunk) + 232 rb_vm_dispatch (in freifunk) + 4502 vm_dispatch (in freifunk) + 504 rb_scope__application:didFinishLaunchingWithOptions:__ (in freifunk) (app_delegate.rb:3) __unnamed_2 (in freifunk) + 316 0x325eacb2 0x325ea702 0x325e4d0e 0x3257f6a2 0x3257e9a4 0x325e44f8 0x34a38708 0x34a382f2 0x2fdd39e2 0x2fdd397e 0x2fdd2152 0x2fd3cce2 0x2fd3cac6 0x325e3794 0x325dea3c main (in freifunk) (main.mm:15) start (in freifunk) + 36
The interesting parts here in this stack are
rb_scope__application:didFinishLaunchingWithOptions:__ (in freifunk) (app_delegate.rb:3) and
rb_scope__init_testflight__ (in freifunk) (app_delegate.rb:45). They show that the app crashes during initialization in
didFinishLaunchingWithOptions when calling the method
init_testflight with the error
const_missing. This is the matching code part:
def init_testflight TestFlight.takeOff(NSBundle.mainBundle.objectForInfoDictionaryKey('testflight_apitoken')) end
So the problem seems to be that TestFlight was missing somehow. This was kind of weird, because I was using TestFlight for distributing my beta builds and all worked fine. Doing some
grep in the compiled sources, I saw that the TestFlight SDK was not included in the distribution build. The underlying problem was, that the TestFlight source path was only set in an development block:
app.development do app.testflight.sdk = 'vendor/TestFlightSDK' [...] end
Moving out of there fixed my crashing App version.
So the “Freifunk App finally made it and you can get the in the AppStore”:http://appstore.com/nofail/freifunk!