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")
}
Or in Groovy, app/build.gradle:
dependencies {
implementation 'com.appambit:appambit:1.1.0'
}
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" />
Minimum supported version is Android 5.0, API level 21.
iOS
Add the pod to your Podfile:
pod 'AppAmbitSdk'
Then install:
pod install
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>
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
Then fetch it:
flutter pub get
React Native
npm install appambit
TypeScript definitions ship with the package, so there is no separate types install.
.NET MAUI
dotnet add package com.AppAmbit.Maui
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>")
// iOS: App init
AppAmbit.start(appKey: "<YOUR-APPKEY>")
// Flutter: main(), before runApp
AppAmbitSdk.start("<YOUR-APPKEY>");
// React Native: App entry
AppAmbit.start("<YOUR_APPKEY>");
// .NET MAUI: MauiProgram
builder.UseMauiApp<App>().UseAppAmbit("<YOUR-APPKEY>");
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.
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()
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)
// Swift
Analytics.trackEvent(eventTitle: "Audio started", data: [
"Category": "Music",
"FileName": "favorite.mp3"
])
// Dart
await AppAmbitSdk.trackEvent('Audio started', <String, String>{
'Category': 'Music',
'FileName': 'favorite.mp3'
});
// TypeScript
trackEvent("Audio started", {
"Category": "Music",
"FileName": "favorite.mp3"
});
// C#
Analytics.TrackEvent("Audio started", new Dictionary<string, string> {
{ "Category", "Music" },
{ "FileName", "favorite.mp3" }
});
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()
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)
}
// 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"])
}
// Dart
try {
throw Exception('Test with Properties');
} catch (e, st) {
await AppAmbitSdk.logError(
exception: e,
stackTrace: st,
properties: <String, String>{'user_id': '1'},
);
}
// C#
try {
// Your code
} catch (Exception exception) {
var properties = new Dictionary<string, string> {
{ "user_data_role", "admin" },
{ "battery_level", "90" }
};
Crashes.LogError(exception, properties);
}
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.
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.
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)}
>
// Flutter
MaterialApp(
navigatorObservers: [AppAmbitSdk()],
)
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>")
// Swift
RemoteConfig.enable()
AppAmbit.start(appKey: "<YOUR-APPKEY>")
// Dart
AppAmbitSdk.enableConfig();
AppAmbitSdk.start(appKey: '<YOUR-APPKEY>');
// TypeScript
AppAmbit.enableConfig();
AppAmbit.start("<YOUR-APPKEY>");
// C#
RemoteConfig.Enable();
AppAmbitSdk.Start("<YOUR-APPKEY>");
Then read values wherever you need them:
val banner = RemoteConfig.getBoolean("banner")
val message = RemoteConfig.getString("data")
val maxUpload = RemoteConfig.getDouble("max_upload")
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")
}
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'
pod install
Flutter.
flutter pub add appambit_sdk_flutter
flutter pub add appambit_sdk_push_notifications
cd ios && pod install
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
.NET MAUI.
dotnet add package com.AppAmbit.Maui
dotnet add package com.AppAmbit.PushNotifications
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)
// Swift: SwiftUI
@UIApplicationDelegateAdaptor(AppAmbitAppDelegate.self) var appDelegate
init() {
PushNotifications.start()
AppAmbit.start(appKey: "<YOUR-APPKEY>")
}
// Dart
void main() {
AppAmbitSdk.start(appKey: '<YOUR-APPKEY>');
PushNotificationsSdk.start();
PushNotificationsSdk.requestNotificationPermission();
runApp(const MyApp());
}
// TypeScript: App.tsx
AppAmbit.start("<YOUR-APPKEY>");
PushNotifications.start();
PushNotifications.requestNotificationPermission();
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)