Cannonball iOS Tutorial

We created Fabric to make it easy to take your app to the next level, from awesome idea to fully fledged business. As app developers ourselves, we wanted to make it simple and delightful to build with Fabric, while also solving the biggest challenges mobile developers face when building mobile apps: stability, distribution, revenue and identity.

To showcase our mobile platform and highlight some of the features provided by each Kit, we created a sample app called Cannonball. Cannonball for iOS is available on the App Store but also open sourced on GitHub.

We love games, so we decided to bring to mobile one of our favorite games from the office, magnetic poetry. If you’re not familiar with magnetic poetry, it’s a game where you get a particular set of words that you stick on the fridge, and you can start arranging them to compose stories.

In this tutorial, you will learn how we integrated Crashlytics, Twitter, and Digits Kits in the Cannonball app for iOS, and which benefits we got by using Fabric along the way, from building to shipping the app.

Note

We also built Cannonball for Android. It’s available on the Play Store and open sourced on GitHub. You can also read the tutorial to build Cannonball for Android with Fabric.

Overview

With Cannonball, we wanted to take the very simple idea of magnetic poetry and bring it to mobile.

We started off with a way to pick different themes and sample words to build your poem. We also added pictures and a timer to make the game a bit more exciting. You can swipe through to select the picture and start tapping on the words to compose a poem.

In order to provide a seamless experience on mobile and a way to carry data from one device to another, we always need an idea of users and login, which is why we decided here to bring a way to sign in using Twitter or their phone number with Digits.

Users can keep a history of the poems they created, share their best stories to Twitter and enjoy poems that have been shared on Twitter.

../../_images/cannonball-overview.png

From left to right: Sign In, Theme Chooser, Poem Composer and Poem History screens.

How to Follow This Tutorial

If you want take a look at the app before reading the tutorial, you can install Cannonball from the App Store.

Install Cannonball for iOS Clone GitHub repository

Get the Code, Setup Xcode and the Fabric OS X app

The best way is to start by cloning the repository and following the instructions on GitHub. This approach is particularly important if you want to see how Fabric dashboards such as Crash Reporting, Beta or Answers work. Remember that you are going to need an account on fabric.io to do that.

Download the Fabric Mac app and move the Fabric OS X app into your ~/Applications directory in you haven’t already done so. Run the Fabric OS X app and sign into your Fabric account.

Open the Cannonball app in Xcode. Create a new app by selecting Cannonball.xcodeproj and pick an organization for this app.

App Structure

The app has six View Controllers, located under the View Controllers group in the Xcode project:

AboutViewController.swift About screen with instructions and Sign Out button
PoemComposerViewController.swift Main interface to tap words and compose poems
PoemHistoryViewController.swift History of poems
PoemTimelineViewController.swift Twitter Timeline with poems recently shared
SignInViewController.swift Sign In with Twitter and Digits (or Skip)
ThemeChooserViewController.swift Screen to select a theme and get started

Please note that for simplicity, we don’t have a backend linked to this sample app. We only store some data locally in NSUserDefaults, especially poems as defined in PoemPersistence.swift.

The user credentials are being stored in the KeyChain, but this is completely transparent as this is handled seamlessly by the Twitter Kit and the Digits Kit.

While we don’t cover them in detail in this tutorial, you can also see in Cannonball examples of iOS functionalities used in Swift including QuartzCore animations with the CountdownView or how to extend UITableView and UICollectionView to build user interfaces.

1. Initializing Fabric

The Fabric OS X app makes it easy to install Fabric frameworks and get the snippets of code you need to integrate all the main functionalities.

Fabric is a modular platform which separates functionality into modules called Kits. As developers, we can pick and choose the Kits we want through the app.

When we hit “Install” on the first Kit, the Fabric OS X app will invite us to add a “Run Script Build Phase”. On our main Xcode project screen, we select the tab “Build Phases”, click the “+” icon and choose “New Run Script Phase”. We simply copy and paste the line from the Fabric OS X app here and build the app with ⌘B to register it with Fabric.

After building the project, the Fabric OS X app invites us to do a simple drag-and-drop of the frameworks and provide the snippets to register them simply using Fabric.with().

Fabric.with() is how we wire up individual Kits from Fabric. This is typically done in the App Delegate inside the application:didFinishLaunchingWithOptions: method.

Note

We are going to cover all the Kits in this tutorial, so feel free to onboard them all right now to save a few seconds as we did in the snippet below. You can also add them one by one once required and the Fabric OS X app will invite you to add them here to Fabric.with(). Of course, depending on which app you are building, you would only pick and choose the Kits you need.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {

    // Register Crashlytics, Twitter, and Digits with Fabric.
    Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self()])

    return true
}

2. Making Cannonball Stable with Crashlytics

All mobile apps crash. It’s nothing to be ashamed of, but what’s important is to be proactive about fixing these crashes. We cannot rely exclusively on local environments and devices to test an app and ensure it has no bugs. Stability is critical in mobile apps. Any bad experiences for users can lead to uninstalls and poor reviews.

That’s where Fabric comes in. Crashlytics is here to help us build the best apps and making sure they’re stable and reliable.

Integrating the Crashlytics Kit takes only one line of code. After clicking “Install” on Crashlytics in the Fabric OS X app, all we need to do is to include Crashlytics in Fabric.with(), and we already just did that!

With Crashlytics enabled, the next time the app crashes, we’ll get a full report with the files and specific lines that caused the crash. The team has worked hard to bring Swift support to Crashlytics and provide full Swift stack traces by demangling the code. Of course we have always perfectly supported Objective-C as well.

../../_images/crashlytics-crash-reporting.png

Crash reporting dashboard for the Cannonball app.

We’ll also get all the details on the individual devices that experienced the crashes. So we’re equipped to understand and solve any problems.

Optionally, we can also add more context to crash reports by using Custom Logging and Custom Keys. We instrumented the Cannonball code to add more context where it was relevant so our code contains several occurrences of that.

Here is an example of Custom Logging using CLSLogv:

// Enhance Crashlytics reports with Advanced Custom Logging.
CLSLogv("Finished Poem: %d words in theme %@ with picture %@.", getVaList([poem.words.count, poem.theme, poem.picture]))

Here is an example for Custom Keys with setObjectValue:

// Tie this selected theme to any crashes in Crashlytics.
Crashlytics.sharedInstance().setObjectValue(themes[row].name, forKey: "Theme")

This additional context will appear in each crash that happens in our app and be visible on the dashboard.

Later in this tutorial, once we implement sign in functionality, we will see how we can also tie user information directly to crash reports to help debug issues.

3. Sharing Content to Twitter

Now that our app will be more stable thanks to Crashlytics, we want to make the game more fun! The cool thing about magnetic poetry is that some friends or colleagues would come by, rearrange some words on the fridge and leave a poem for us to discover.

In the context of a mobile app, once a user has created a poem, it would be great to share it with their friends. By sharing content to Twitter, users actually play a huge role in app marketing since these Tweets can have high engagements and drive organic app installs.

The Twitter Kit includes TWTRComposer, a Tweet Composer, which makes this task really simple. Let’s see the code which is triggered when the user taps the “Share” button to tweet a poem from the history screen:

func poemCellWantsToSharePoem(poemCell: PoemCell) {
    // Find the poem displayed at this index path.
    let indexPath = tableView.mp_indexPathForCell(poemCell)
    let poem = poems[indexPath.row]

    // Generate the image of the poem.
    let poemImage = poemCell.capturePoemImage()

    // Use the TwitterKit to create a Tweet composer.
    let composer = TWTRComposer()

    // Prepare the Tweet with the poem and image.
    composer.setText("Just composed a poem! #cannonballapp #\(poem.theme.lowercaseString)")
    composer.setImage(poemImage)

    // Present the composer to the user.
    composer.showFromViewController(self) { result in
        if result == .Done {
            println("Tweet composition completed.")
        } else if result == .Cancelled {
            println("Tweet composition cancelled.")
        }
    }
}

All we need to do is set the suggested Tweet text, automatically appending the relevant hashtags like #cannonballapp, and attach the poem image. A method named setURL() is also available to share links.

After setting these attributes, we can then call composer.showWithCompletion() method to execute the action. Inside the completion block, we will be informed whether or not the user has completed or cancelled the sharing action.

On iOS, TWTRComposer uses the native share sheet and the Twitter accounts in the device settings to share this content. Here is a screenshot of the sharing action:

../../_images/cannonball-twitter-sharing.png

Sharing a poem from Cannonball to Twitter.

4. Embedding Tweets in the App

Users can now share poems to Twitter, but that’s not all we want to do. What if we’re taking all the awesome poems people have shared and bringing this content back into Cannonball so users can enjoy them without leaving the app?

The Twitter Kit enables us to embed public, real-time, relevant Tweets to complement the content in our app and make our app more engaging.

There are a handful of features the Twitter Kit offers when it comes to bringing Twitter content inside mobile apps. We can natively embed Tweets in different formats but also complete Timelines, as well as interacting with the Twitter API.

People are sharing Tweets with the hashtag #cannonballapp along with the theme and a picture, so what we need for Cannonball is to bring in Tweets which match the keywords below:

// Search query for Tweets matching the right hashtags and containing an attached poem picture.
let poemSearchQuery = "#cannonballapp AND pic.twitter.com AND (#adventure OR #romance OR #nature OR #mystery)"

This is a complex search query with operators that we can type in any Twitter app or using the Twitter Search API.

Now we just need to implement a native Timeline of Tweets matching this query. The Twitter Kit provides different kinds of Timelines. The Search Timeline is exactly what we need.

First, we declare that our PoemTimelineViewController inherits from TWTRTimelineViewController instead of a more generic UITableView.

The TWTRTimelineViewController takes care of everything, from wrapping the API requests, handling the JSON responses and displaying Tweets natively with an infinite pagination. Not only is this extremely easy for us developers, this also displays Tweets in a way that is compliant with Twitter’s Display Guidelines and keeps the user experience consistent across all apps. We provide the ability to change the look and feel of these Tweets, for instance if an app has a dark background.

override func viewDidLoad() {
    super.viewDidLoad()

    let client = Twitter.sharedInstance().APIClient
    self.dataSource = TWTRSearchTimelineDataSource(searchQuery: self.poemSearchQuery, APIClient: client)
}

The first very interesting thing to note in the code above is that TWTRTimelineViewController lets us leverage the guest authentication. This enables us to bring public Twitter content without the need to sign in with a Twitter account in the app.

Then, when we have a valid session, all we need is to assign the dataSource of our TWTRTimelineViewController as an instance of TWTRSearchTimelineDataSource with the searchQuery we previously defined.

Here is a screenshot of the Poem Timeline displayed in Cannonball:

../../_images/cannonball-twitter-timeline.png

Twitter Timeline showing recently shared poems on Cannonball.

Note

The Twitter Kit requires a Twitter API key. When the Fabric OS X app and installing the Twitter Kit, a Twitter app is automatically generated with guest authentication enabled, and the key and secret are located in our Info.plist file. If you would like to use an existing Twitter app instead, please take a look at the Twitter Kit Advanced Setup documentation page.

5. Distributing Beta Builds and Getting Feedback

There is another great tool that Crashlytics provides during a development process. After spending time implementing a new app or new features, it is very important to distribute this app to a larger group in order to make sure it works well and get feedback before releasing it to the App Store.

Crashlytics provides a solution called Beta. This allows us to easily distribute beta builds of our app.

All we need is to do is simply hit “Product > Archive” in Xcode. This will create an IPA, and as soon as the build is ready in a few seconds, the Fabric OS X app will automatically detect this to suggest we share this build with our team, friends or family. It’s so simple, we don’t even have to leave Xcode.

../../_images/crashlytics-beta.png

Dashboad showing the status of every tester in Beta by Crashlytics.

Note

Fabric takes care of all the complexity when it comes to gathering UDIDs for iOS devices. Once your testers accept the invite, the next time you distribute a build, the Fabric OS X app will give you the list of UDIDs to add to your Provisioning Profile to sign the app for their devices.

6. Sign In with Digits and Twitter

In order to support any features which rely on identifying a user, such as syncing data to several devices or personalization, Fabric provides two options respectively in the Twitter Kit and the Digits Kit: Sign In with Twitter and Digits.

../../_images/cannonball-sign-in.png

Cannonball Sign In with Twitter and Digits.

Sign In with Digits

Digits lets people create an account or sign into any mobile apps using nothing but their phone number, allowing them to forget about passwords. This service is built on the exact same global and reliable infrastructure Twitter uses to send SMS messages. Digits verifies the user’s phone number via SMS, provides a simple and customizable user interface, and is completely free.

For convenience, Digits provides a button to start the login process that we can quickly embed in our app via the Fabric OS X app, but it also allows us to use our own button and customize the entire user experience. This is great to make Digits match our app look and feel and make it completely part of our app.

Cannonball leverages Digits to offer a simple, elegant, and mobile-friendly way to login. As a quick first step, we prepared a customized UIButton and positioned it in our Storyboard. We also added an @IBAction named signInWithPhone for the “Touch Up Inside” event in order to use this UIButton in our code and trigger the Digits flow for this gesture.

Also, to customize not only the button but also the entire experience, Digits supports theming. So instead of triggering the Digits flow with authenticateWithCompletion, which presents the default screens, we can instead use authenticateWithViewController and pass an instance of DGTAppearance which allows us to customize the colors and make sure it provides a perfectly consistent design with the rest of the experience.

@IBAction func signInWithPhone(sender: UIButton) {
    // Create a Digits appearance with Cannonball colors.
    let configuration = DGTAuthenticationConfiguration(accountFields: .DefaultOptionMask)

    configuration.appearance = DGTAppearance()
    configuration.appearance.backgroundColor = UIColor.cannonballBeigeColor()
    configuration.appearance.accentColor = UIColor.cannonballGreenColor()

    // Start the Digits authentication flow with the custom appearance.
    Digits.sharedInstance().authenticateWithViewController(nil, configuration:configuration) { (session, error) in
        if session != nil {
            // We now have access to the user’s verified phone number and to a unique, stable, userID.
            println(session.phoneNumber)
            println(session.userID)

            // Tie crashes to a Digits user ID in Crashlytics.
            Crashlytics.sharedInstance().setUserIdentifier(session.userID)

            // Navigate to the main app screen to select a theme.
            self.navigateToMainAppScreen()
        }
    }
}

When the user is able to confirm their phone number, the completion block will let us access a DGTSession instance, containing a stable, unique ID with session.userID as well as the verified phone number in session.phoneNumber.

Please note here the ability to tie additional user information to Crashlytics with setUserIdentifier the next time a crash happens.

In the screenshot below we can see the main Digits screen triggered when the user taps the “Sign In with Phone” button. After typing their phone number, the user will get an SMS message with a confirmation code to enter on the next screen. Once the authentication is successful, the user is redirected back to app and it is then possible to access and save the user information.

../../_images/cannonball-digits.png

Digits inviting the user to enter their phone number.

While not implemented in Cannonball, Digits also supports advanced functionality such as Friend Finding to leverage the contacts in the Address Book and match friends both ways.

Sign In with Twitter

In Cannonball we also added a way to login with Twitter. Sign In with Twitter enables an app to personalize the experience based on the information from Twitter by doing API calls on behalf of the user to retrieve their profile, Tweets or followers for instance.

The integration is very similar to Digits as it involves adding a button, instantiating it and implementing the completion. The great advantage of using the Twitter Kit is that we don’t need to worry about the complexity of OAuth 1.0a in our app or handling JSON structures returned by the Twitter API.

Once again the Twitter Kit provides a button, but we can use our own. It is very similar to what we did for Digits above, we trigger the Twitter flow in the @IBAction:

@IBAction func signInWithTwitter(sender: UIButton) {
    Twitter.sharedInstance().logInWithCompletion { session, error in
        if session != nil {
            // Navigate to the main app screen to select a theme.
            self.navigateToMainAppScreen()

            // Tie crashes to a Twitter user ID and username in Crashlytics.
            Crashlytics.sharedInstance().setUserIdentifier(session.userID)
            Crashlytics.sharedInstance().setUserName(session.userName)
        }
    }
}

If the sign in is successful, in the completion block we will get a TWTRSession instance that will provide us with the information about the user who just logged into our app.

Please note here as well the ability to tie additional user information to Crashlytics with setUserIdentifier and setUserName the next time a crash happens.

Now when the user clicks on the “Sign In with Twitter” button, Cannonball automatically leverages the accounts inside the iOS Settings to avoid tapping any passwords.

../../_images/cannonball-twitter-accounts-access.png

Cannonball app requesting access to Twitter accounts.

If a user has more than one Twitter account listed in iOS Settings, they will be presented with a way to select the account they would like to use for this app:

../../_images/cannonball-twitter-accounts-list.png

User invited to choose which Twitter account to access from their iOS Settings.

7. Understanding Growth with Answers

When setting up Crashlytics, we also get access to Answers, a real-time analytics product that will help us understand the key growth and retention metrics for our app, with no impact on performance.

Answers puts the most important metrics front and center including daily active users, month active users, crash-free users thanks to its relationship with crash reporting, and will also proactively highlight any metrics that should get our attention, all this without any configuration. Here is what the Answers dashboard looks like for Cannonball:

../../_images/answers-dashboard.png

Answers dashboard for Cannonball.

With Answers, we can also track specific actions by instrumenting our code with Custom Events sent to Answers. We added Custom Events in the Cannonball code when users are interacting with some of the features, for instance when they finish composing a poem or stop creating one, or when they are viewing their history or popular poems. Below is an example:

// Log Answers Custom Event.
Answers.logCustomEventWithName("Finished Composing Poem",
    customAttributes: [
        "Poem": poem.getSentence(),
        "Theme": poem.theme,
        "Length": poem.words.count,
        "Picture": poem.picture
    ]
)

The method logCustomEventWithName not only accepts an event name, you can also pass extra information with customAttributes that will be tracked as well like we did in the above snippet, giving better insights on how users are interacting with this feature. The dashboard will show all the metrics around this event:

../../_images/answers-custom-events.png

Answers dashboard on the Custom Event triggered after composing a poem.

Answers also provides by default some predefined events for standard actions in mobile apps, for instance around login and sign up, in order to structure the outcome. We used the method logLoginWithMethod in the Digits and Twitter flows, passing the number to reflect the success or failure, as well as custom attributes about the user or the error that happened.

// Start the Digits authentication flow with the custom appearance.
Digits.sharedInstance().authenticateWithViewController(nil, configuration:configuration) { (session, error) in
    if session != nil {
        // Navigate to the main app screen to select a theme.
        self.navigateToMainAppScreen()

        // Tie crashes to a Digits user ID in Crashlytics.
        Crashlytics.sharedInstance().setUserIdentifier(session.userID)

        // Log Answers Custom Event.
        Answers.logLoginWithMethod("Digits", success: true, customAttributes: ["User ID": session.userID])
    } else {
        // Log Answers Custom Event.
        Answers.logLoginWithMethod("Digits", success: false, customAttributes: ["Error": error.localizedDescription])
    }
}

We also used the Answers predefined method logShareWithMethod when sharing content to Twitter in PoemHistoryViewController.swift.

Recap

We built the Cannonball app to show how easy it is to integrate all Fabric Kits in a mobile app.

First, we improved our app stability, gathered user feedback and had deep insights thanks to the Crashlytics Kit that provides crash reporting, beta distributions, and metrics via Answers.

Using the Twitter Kit, we were able to make our game more social, let our users share poems but also browse popular poems others have shared on Twitter, bringing real-time content inside the game.

Lastly, we provided very simple ways to sign in, in particular with the Digits Kit which enables a beautiful and seamless phone number sign in for users, with no passwords to remember.

If you want to dive into Fabric even more, browse our documentation or view our community forums. We can’t wait to see the great apps you are going to build with Fabric.