Crash Analysis with HP AppPulse Mobile on iOS
Written by Eddie Freeman   
Monday, 11 April 2016
Article Index
Crash Analysis with HP AppPulse Mobile on iOS
Debugging in AppPulse Mobile

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. 
 
tryapppulse

 

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:

  1. Enter your name, which is saved to NSUserDefaults

  2. Select a Good Feels message

  3. Select people to whom you want to text the message

  4. 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:

 

appulse1

 

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.

 



Last Updated ( Tuesday, 12 April 2016 )