DEV Community

Cover image for The 10-minute "everything after you build it" checklist - AppAmbit ๐Ÿ”ต๐ŸŸ 
Roel Leal
Roel Leal

Posted on

The 10-minute "everything after you build it" checklist - AppAmbit ๐Ÿ”ต๐ŸŸ 

You built the app. Now comes the part nobody scoped: crash reporting, analytics, a way to get builds to testers, push notifications, and a switch you can flip when something goes wrong in production.

One install, one dashboard, nine steps. Each step ends with something you can see, not something you have to trust. Ten minutes, start to finish.

Code is Kotlin, Swift, Dart, TypeScript and C# throughout. Find your stack and skip the rest.


The whole thing at a glance

# Outcome Time
1 Your app exists in the dashboard 1 min
2 The SDK is installed and resolving 1 min
3 You see your first session 1 min
4 You see your first event 1 min
5 You catch a crash on purpose 1 min
6 You read the story behind the crash 30 sec
7 You change the app without shipping a release 1.5 min
8 You send a push to your own phone 2 min
9 A tester has your build 1 min

Steps 3 through 6 are the ones that pay for themselves the first time production surprises you. If you only have five minutes, do those.


1. Your app exists in the dashboard

Time: 1 minute ยท Outcome: an App Key in your clipboard

Sign in, go to Apps โ†’ New App. Give it a name, pick the release type and target OS, hit Create.

Open the app's Info page and copy the App Key. That single string is what connects your project to everything else in this checklist: crashes, events, push, remote config and releases. There is no second key to find later.

Done when: you have an App Key and your app is listed on the Apps screen.


2. The SDK is installed and resolving

Time: 1 minute ยท Outcome: a clean build with the package resolved

Every step after this one depends on the core SDK being installed correctly. Do it properly now and nothing later in the list will fail for a reason you cannot see. Find your platform, run the three lines, move on.

Android

Add the dependency to your app level build.gradle.kts:

dependencies {
    implementation("com.appambit:appambit:1.1.0")
}
Enter fullscreen mode Exit fullscreen mode

Or in Groovy, app/build.gradle:

dependencies {
    implementation 'com.appambit:appambit:1.1.0'
}
Enter fullscreen mode Exit fullscreen mode

Run a Gradle sync in Android Studio, then add the two required permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Enter fullscreen mode Exit fullscreen mode

Minimum supported version is Android 5.0, API level 21.

iOS

Add the pod to your Podfile:

pod 'AppAmbitSdk'
Enter fullscreen mode Exit fullscreen mode

Then install:

pod install
Enter fullscreen mode Exit fullscreen mode

If you get Unable to find a specification for AppAmbitSdk, run pod repo update and try again. That error means your local pod spec repo is stale, not that the package is missing.

Add the transport security exception to Info.plist:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>appambit.com</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>
Enter fullscreen mode Exit fullscreen mode

Requirements are iOS 12.0 or later, Xcode 13 or later, and CocoaPods 1.10 or later.

Flutter

Add the package to pubspec.yaml:

dependencies:
  appambit_sdk: ^1.1.0
Enter fullscreen mode Exit fullscreen mode

Then fetch it:

flutter pub get
Enter fullscreen mode Exit fullscreen mode

React Native

npm install appambit
Enter fullscreen mode Exit fullscreen mode

TypeScript definitions ship with the package, so there is no separate types install.

.NET MAUI

dotnet add package com.AppAmbit.Maui
Enter fullscreen mode Exit fullscreen mode

Or right click the project in Visual Studio, choose Manage NuGet Packages, search for AppAmbit and install the latest stable version.

One important limitation before you start: Xamarin is not supported. MAUI is.

Done when: the project builds, the package resolves, and the permissions or plist entries above are in place.


3. You see your first session

Time: 1 minute ยท Outcome: a live session on the Audience screen

The package is installed. Now start it at your app's entry point, using the key from step 1:

// Android: MainActivity.onCreate
AppAmbit.start(this, "<YOUR-APPKEY>")
Enter fullscreen mode Exit fullscreen mode
// iOS: App init
AppAmbit.start(appKey: "<YOUR-APPKEY>")
Enter fullscreen mode Exit fullscreen mode
// Flutter: main(), before runApp
AppAmbitSdk.start("<YOUR-APPKEY>");
Enter fullscreen mode Exit fullscreen mode
// React Native: App entry
AppAmbit.start("<YOUR_APPKEY>");
Enter fullscreen mode Exit fullscreen mode
// .NET MAUI: MauiProgram
builder.UseMauiApp<App>().UseAppAmbit("<YOUR-APPKEY>");
Enter fullscreen mode Exit fullscreen mode

That is the whole integration for sessions. From here the SDK tracks sessions and device details such as model and OS version, with no further code. Sessions are automatic by default. Manual session control exists if you need it, but you do not need it today.

AppAmbit Sessions

One thing worth doing properly rather than quickly: keep the key out of source control. It is not recommended to paste it directly into code you commit.

Run the app. Open Audience in the dashboard.

Done when: you see a session with your device model and OS version on it.


4. You see your first event

Time: 1 minute ยท Outcome: a named event with properties, in the dashboard

No extra package here. Analytics is part of the core SDK you installed in step 2.

There is a built in call whose only job is to prove the pipe works:

Analytics.generateTestEvent()
Enter fullscreen mode Exit fullscreen mode

Fire it, confirm it lands, then replace it with something you actually care about:

// Kotlin
val properties = mapOf(
    "Category" to "Music",
    "FileName" to "favorite.mp3"
)
Analytics.trackEvent("Audio started", properties)
Enter fullscreen mode Exit fullscreen mode
// Swift
Analytics.trackEvent(eventTitle: "Audio started", data: [
    "Category": "Music",
    "FileName": "favorite.mp3"
])
Enter fullscreen mode Exit fullscreen mode
// Dart
await AppAmbitSdk.trackEvent('Audio started', <String, String>{
    'Category': 'Music',
    'FileName': 'favorite.mp3'
});
Enter fullscreen mode Exit fullscreen mode
// TypeScript
trackEvent("Audio started", {
    "Category": "Music",
    "FileName": "favorite.mp3"
});
Enter fullscreen mode Exit fullscreen mode
// C#
Analytics.TrackEvent("Audio started", new Dictionary<string, string> {
    { "Category", "Music" },
    { "FileName", "favorite.mp3" }
});
Enter fullscreen mode Exit fullscreen mode

Properties are optional. trackEvent("Music clicked") on its own is valid.

Three limits worth knowing before you design your event schema: 150 distinct event names, 120 characters per name, and 80 characters per property name and value.

Pick the one moment that means your app worked for someone, such as signup completed, first file saved, or checkout done. Track that one first. You can add the rest later. What you cannot do is go back and collect data you never sent.

Done when: your event appears with its properties attached.


5. You catch a crash on purpose

Time: 1 minute ยท Outcome: a grouped crash with a stack trace

Still no extra package. Crash reporting is in the core SDK too, and it is already running.

One prerequisite that is easy to miss: do not ship a second crash reporting library alongside this one. Two handlers competing for the same signals is how you end up with neither of them working.

Same idea as the test event, there is a call that exists purely so you can verify crash reporting before you need it:

Crashes.generateTestCrash()
Enter fullscreen mode Exit fullscreen mode

Crash the app, then reopen it. That second part matters. The crash log is written to local storage at the moment of the crash and uploaded when the app next starts. This is also why crashes survive a device that was offline when it happened.

Now the more useful half, the errors that do not kill the app but do ruin the experience:

// Kotlin
try {
    // Your code
} catch (exception: Exception) {
    val properties = mapOf("user_id" to "1")
    Crashes.logError(exception, properties)
}
Enter fullscreen mode Exit fullscreen mode
// Swift
do {
    throw NSError(domain: "com.appambit", code: -1,
                  userInfo: [NSLocalizedDescriptionKey: "Division by zero"])
} catch let error {
    Crashes.logError(exception: error, properties: ["user_id": "1"])
}
Enter fullscreen mode Exit fullscreen mode
// Dart
try {
    throw Exception('Test with Properties');
} catch (e, st) {
    await AppAmbitSdk.logError(
        exception: e,
        stackTrace: st,
        properties: <String, String>{'user_id': '1'},
    );
}
Enter fullscreen mode Exit fullscreen mode
// C#
try {
    // Your code
} catch (Exception exception) {
    var properties = new Dictionary<string, string> {
        { "user_data_role", "admin" },
        { "battery_level", "90" }
    };
    Crashes.LogError(exception, properties);
}
Enter fullscreen mode Exit fullscreen mode

Wrap your three riskiest paths: the network call, the file write, and the parse of something a server handed you. Attach whatever you would want to know at 2am.

AppAmbit Crash & Error panel

Done when: the crash appears in the dashboard, grouped, with a stack trace and the affected device.


6. You read the story behind the crash

Time: 30 seconds ยท Outcome: the sequence of events before the failure

Open the crash you just made and look at the session attached to it. Session Timeline is on by default and it is already recording app lifecycle changes, screen transitions, connectivity drops, your tracked events, and the errors themselves, in order, with timestamps relative to session start.

Session Timeline with Breadcrumbs

This is the difference between "NullPointerException in ProfileView" and "went offline, tapped retry twice, then NullPointerException in ProfileView". One of those you can reproduce.

Native apps get screen breadcrumbs with no setup. Hybrid apps need one line so the SDK can listen to your navigator:

// React Native
<NavigationContainer
  ref={navigationRef}
  onReady={() => registerNavigationTracking(navigationRef)}
>
Enter fullscreen mode Exit fullscreen mode
// Flutter
MaterialApp(
  navigatorObservers: [AppAmbitSdk()],
)
Enter fullscreen mode Exit fullscreen mode

Name your routes while you are in there. new_screen reads better than MaterialPageRoute#3 at 2am. On iOS, set navigationTitle in SwiftUI or title in UIKit for the same reason.

Done when: you can see the ordered trail of what happened before your test crash.


7. You change the app without shipping a release

Time: 1.5 minutes ยท Outcome: a value you control from the dashboard

Remote Config ships inside the core SDK. Nothing new to install, which is rather the point of the single package.

Go to Remote Config in the dashboard, add a parameter with a key, a value, a value type and the enabled toggle, then publish it. Version targeting is there if you want the change to apply to specific builds only.

Turn it on in code before you start the SDK. Order matters here and getting it wrong is the most common reason config appears to do nothing:

// Kotlin
RemoteConfig.enable()
AppAmbit.start(this, "<YOUR-APPKEY>")
Enter fullscreen mode Exit fullscreen mode
// Swift
RemoteConfig.enable()
AppAmbit.start(appKey: "<YOUR-APPKEY>")
Enter fullscreen mode Exit fullscreen mode
// Dart
AppAmbitSdk.enableConfig();
AppAmbitSdk.start(appKey: '<YOUR-APPKEY>');
Enter fullscreen mode Exit fullscreen mode
// TypeScript
AppAmbit.enableConfig();
AppAmbit.start("<YOUR-APPKEY>");
Enter fullscreen mode Exit fullscreen mode
// C#
RemoteConfig.Enable();
AppAmbitSdk.Start("<YOUR-APPKEY>");
Enter fullscreen mode Exit fullscreen mode

Then read values wherever you need them:

val banner = RemoteConfig.getBoolean("banner")
val message = RemoteConfig.getString("data")
val maxUpload = RemoteConfig.getDouble("max_upload")
Enter fullscreen mode Exit fullscreen mode

One expectation to set now so you do not debug a non problem later: updated values take about 5 minutes to reach devices. Config is not a real time channel. If nothing has changed after that, check two things. Did you call enable before start, and did you actually publish the parameter rather than just saving it.

The one to set up before you need it is a kill switch, a boolean that hides your newest feature. The day a release goes wrong, you will flip it in seconds instead of waiting on app review.

Done when: a value you changed in the dashboard shows up in the running app.


8. You send a push to your own phone

Time: 2 minutes ยท Outcome: a notification on your lock screen

This is the one step that needs a second package and a real prerequisite. Push runs through FCM on Android and APNs on iOS, so you need credentials from Google or Apple registered in AppAmbit before any of this works. If you already have them, this is quick. If not, the FCM and APNs requirement guides walk through generating them.

Install the push package

Android. Both packages, plus Firebase messaging:

dependencies {
    implementation("com.appambit:appambit:1.1.0")
    implementation("com.appambit:appambit-push-notifications:1.1.0")

    // Required to align Firebase library versions
    implementation(platform("com.google.firebase:firebase-bom:33.1.2"))
    implementation("com.google.firebase:firebase-messaging:23.4.0")
}
Enter fullscreen mode Exit fullscreen mode

You also need the Google Services plugin classpath in your project level Gradle file, and google-services.json from your Firebase project dropped into the app module.

iOS. In Xcode, File โ†’ Add Packages and add both AppAmbitSdk and AppAmbitPushNotifications to your app target. Or with CocoaPods:

pod 'AppAmbitSdk'
pod 'AppAmbitPushNotifications', '~> 1.1.0'
Enter fullscreen mode Exit fullscreen mode
pod install
Enter fullscreen mode Exit fullscreen mode

Flutter.

flutter pub add appambit_sdk_flutter
flutter pub add appambit_sdk_push_notifications
cd ios && pod install
Enter fullscreen mode Exit fullscreen mode

React Native. Note the minimum, minSdk = 24 in android/app/build.gradle, higher than the core SDK's API 21:

npm install appambit
npm install appambit-push-notifications
cd ios && pod install
Enter fullscreen mode Exit fullscreen mode

.NET MAUI.

dotnet add package com.AppAmbit.Maui
dotnet add package com.AppAmbit.PushNotifications
Enter fullscreen mode Exit fullscreen mode

The native iOS frameworks are bundled inside the NuGet package, so there is no CocoaPods step for MAUI.

Start it, after the core SDK

// Kotlin: MainActivity.onCreate
AppAmbit.start(applicationContext, "<YOUR-APPKEY>")
PushNotifications.start(applicationContext)

// Required for cold start and foreground taps
PushNotifications.handleNotificationOpened(this, intent)
Enter fullscreen mode Exit fullscreen mode
// Swift: SwiftUI
@UIApplicationDelegateAdaptor(AppAmbitAppDelegate.self) var appDelegate

init() {
    PushNotifications.start()
    AppAmbit.start(appKey: "<YOUR-APPKEY>")
}
Enter fullscreen mode Exit fullscreen mode
// Dart
void main() {
    AppAmbitSdk.start(appKey: '<YOUR-APPKEY>');
    PushNotificationsSdk.start();
    PushNotificationsSdk.requestNotificationPermission();
    runApp(const MyApp());
}
Enter fullscreen mode Exit fullscreen mode
// TypeScript: App.tsx
AppAmbit.start("<YOUR-APPKEY>");
PushNotifications.start();
PushNotifications.requestNotificationPermission();
Enter fullscreen mode Exit fullscreen mode

On iOS, add the Push Notifications capability in Xcode. Without the aps-environment entitlement no device token is issued and nothing else will work, however correct your code is.

Then in the dashboard: Push Notifications โ†’ Create Notification. Target Specific Device Token so you are testing on your own phone and nobody else's. Title and body are the only required fields. Leave the schedule empty and it sends immediately.

Done when: the notification arrives, and tapping it opens your app where you expect.


9. A tester has your build

Time: 1 minute ยท Outcome: a release someone else can install

Nothing to install for this one. It happens entirely in the dashboard.

Open Releases โ†’ Upload Artifact, pick your .apk or .ipa, confirm.

Only the extension is validated, so name the file whatever makes sense to you. release_v1.ipa is fine. When the upload finishes it shows up in the Releases table, ready to share.

That is the manual path, and it is the right one for the first build. Once it is routine, the same thing runs from GitHub, Bitbucket or Azure on every merge.

Done when: the release is listed and installable.


What you have now

Nine steps, one App Key, one dashboard. Crashes with stack traces, events with properties, the timeline that explains both, a config switch you control without app review, push going to real devices, and builds getting to testers.

Two packages installed in total, and the second one only if you wanted push.

The part that matters is not any single one of those. It is that a crash spike, the release that caused it, and the users it hit are the same query now, instead of three exports and a spreadsheet.

If you work through this on your own app, do the one thing the list cannot do for you. Decide which single event means your app worked for someone, and track it before you ship.

Docs are at docs.appambit.com.

Top comments (0)