Page 1 of 2 Every app developer faces the challenge of tracking down issues that face users once the app has been released into the wild. We can test for many scenarios, but something will inevitably slip through the cracks. HP's AppPulse Mobile helps capture these elusive cases by providing developers with actionable data to resolve issues that go beyond a stack trace.
This article gives a practical demonstration of how AppPulse Mobile can identify scenarios that can cause an uncaught crash, and the benefits of going beyond the stack trace to determine the culprit of the crashes. To follow along, you can register for an AppPulse Mobile trial account by clicking this link.

The App - Good Feels
The app "Good Feels" which we are using for this tutorial was written with Swift 2.1 in XCode 7 for iOS 9.x using the Contacts and MessageUI frameworks. Also included, are 3rd-Party libraries Firebase and Pop.
This app helps you spread good feelings among your friends and family via a quick SMS from a list of inspiring / motivational / uplifting phrases. User Actions include:
-
Enter your name, which is saved to NSUserDefaults
-
Select a Good Feels message
-
Select people to whom you want to text the message
-
Send the pre-made SMS
To do the above, we'll first need to collect the user's name and then save it for subsequent app loads. For simplicity we won't skip this view but instead allow them to change their name if they want to on a new app load.
Second, we'll need to collect the contacts from the device using the Contacts framework and filter by those contacts which have a phone number. To prevent the UI from locking, this process should get sent to a background thread. We do this as follows:
func fetchUnifiedContacts() { dispatch_async(concurrentContactsQueue) { do { try self.contactStore. enumerateContactsWithFetchRequest( CNContactFetchRequest( keysToFetch: self.keysToFetch)) { (contact, cursor) -> Void in if !contact.phoneNumbers.isEmpty && !
GoodFeelsClient.sharedInstance.
contacts.contains(contact) {
GoodFeelsClient. sharedInstance.contacts. append(contact) } } } catch let error as NSError { print(error.description, separator: "", terminator: "\n") } dispatch_async(GlobalMainQueue) { self.postContentAddedNotification() } } }
In the end it should look like this:

Stay synced with a backend service (such as Firebase) to increase the number of inspirational quotes / good vibes. Below is a code snippet that accomplishes this with our simple Array<String>:
func getSynchronizedMessages() -> Array<String> { messages += syncChanges() return messages } func syncChanges() -> Array<String> { var newMessages = [String]() rootRef.observeEventType(.ChildAdded, withBlock: { snapshot in let data = snapshot.value if data is Array<String> { newMessages.appendContentsOf( data as! Array<String>) } else if data is String { newMessages.append(data as! String) } }, withCancelBlock: { error in print(error.description) }) return newMessages } func fetchMessages() -> Array<String> { // get a full dump rootRef.observeEventType(.Value, withBlock: { snapshot in self.messages = snapshot.value as! [String] }, withCancelBlock: { error in print(error.description) }) return messages }
Lastly, after the message is chosen and contacts are selected, we can send an SMS using the MessageUI framework. Let's create a quick MessageComposer class:
class MessageComposer: NSObject, MFMessageComposeViewControllerDelegate { static let instance = MessageComposer() // A wrapper function to indicate whether or not
//a text message can be sent
//from the user's device func canSendText() -> Bool { return MFMessageComposeViewController. canSendText() } // Configures and returns a // MFMessageComposeViewController instance func configuredMessageComposeViewController( textMessageRecipients:[String] , textBody body:String) -> MFMessageComposeViewController { let messageComposeVC = MFMessageComposeViewController()
// Make sure to set this property to self,
// so that the controller can be dismissed! messageComposeVC.messageComposeDelegate = self
messageComposeVC.recipients = textMessageRecipients messageComposeVC.body = body
return messageComposeVC }
// MFMessageComposeViewControllerDelegate callback
// dismisses the view controller // when the user is finished with it func messageComposeViewController( controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) { // Maybe do other stuff: // send to Messages Screen, etc controller.dismissViewControllerAnimated( true, completion: nil) } }
For a fully working example check out this branch of the project on GitHub. However, for this article we'll focus on when things go wrong on this branch.
Tracking Down Crash Scenarios
AppPulse Mobile takes the common approach of connecting to the system crash handler, but then goes beyond that. Here we'll show commonly captured system failures and then not so common ones, within the context of the "Good Feels" app using the tools provided to debug each issue.
|