iOS, watchOS, tvOS & macOS

Follow

This document includes necessary information for integrating the Countly iOS / watchOS / tvOS / macOS SDKs in your application.

Supported System Versions

The Countly iOS SDK supports the minimum Deployment Target iOS 8.0 (watchOS 2.0, tvOS 9.0, macOS 10.10) , and it requires Xcode 9.0+ with Base SDK iOS 10.0+.

Integration

To integrate the Countly iOS SDK into your application, please follow these steps:

1. Download the Countly iOS SDK (or clone it in your project as a Git submodule, or use CocoaPods or Carthage).

2. Add all files in countly-ios-sdk to your project on Xcode. 

You may delete README.md and CHANGELOG.md from your project or remove them from Target > Build Phases > Compile Sources to avoid compiler warnings.

3. In your application delegate, import Countly.h, add the following lines at the beginning insideapplication:didFinishLaunchingWithOptions:

Objective-C Swift
#import "Countly.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    CountlyConfig* config = CountlyConfig.new;
    config.appKey = @"YOUR_APP_KEY";
    config.host = @"https://YOUR_COUNTLY_SERVER";
    [Countly.sharedInstance startWithConfig:config];

    // your code
  
    return YES;
}

Note: Make sure you start Countly iOS SDK on the main thread. 

4. Set your app key and host on the CountlyConfig object. Make sure to use the App Key (under top right cog > Management > Applications), not the API Key or App ID.

If you are using the Countly Enterprise Edition trial servers, the host should be https://try.count.ly, https://us-try.count.ly or https://asia-try.count.ly. Stated simply, it should be the domain from which you are accessing your trial dashboard.

5. You may run your project and see the first session data immediately displayed on your Countly Server dashboard.

Advanced Configuration

Countly Code Generator

The Countly Code Generator may be used to generate Countly iOS SDK code snippets easily and fast. You may provide values for your custom events, user profiles, or just start with basic integration. It will generate the necessary code for you.

Debug Mode

If you would like to enable the Countly iOS SDK to debug mode, which logs internal info, errors, and warnings into your console, you may set the enableDebug flag on the CountlyConfig object before starting Countly.

Objective-C Swift
config.enableDebug = YES;

Additional Features

If you would like to use additional features, such as PushNotifications, CrashReporting, and AutoViewTracking,  you may specify them in the features array on the CountlyConfig object before you start:

Objective-C Swift
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    CountlyConfig* config = CountlyConfig.new;
    config.appKey = @"YOUR_APP_KEY";
    config.host = @"https://YOUR_COUNTLY_SERVER";
  
    //You can specify additional features you want here
    config.features = @[CLYPushNotifications, CLYCrashReporting, CLYAutoViewTracking];
 
    [Countly.sharedInstance startWithConfig:config];

    // your code
  
    return YES;
}

Available additional features per platform:

iOS
CLYPushNotifications
CLYCrashReporting
CLYAutoViewTracking

watchOS
CLYCrashReporting

tvOS
CLYCrashReporting
CLYAutoViewTracking

macOS
CLYPushNotifications
CLYCrashReporting

Device ID

You may configure your device ID using the CountlyConfig object and helper methods:

Using a Custom Device ID

If you would like to use a custom device ID, you may set the deviceID property on the CountlyConfig object. If the deviceID property is not set explicitly, a default device ID will be used depending on the platform.

Objective-C Swift
config.deviceID = @"customDeviceID";  //Optional custom device ID

Note: Once set, your device ID will be persistently stored on the device after the first app launch, and the deviceID property will be ignored on the following app launches, until the app is deleted and re-installed or a resetStoredDeviceID flag is set. For further details, please see the Resetting Stored Device ID section.

Temporary Device ID

You may use temporary device ID mode for keeping all requests on hold until the real device ID is set later. It may be enabled by setting deviceID on initial configuration asCLYTemporaryDeviceID:

Objective-C Swift
config.deviceID = CLYTemporaryDeviceID;

Or by passing as an argument for deviceID parameter on setNewDeviceID:onServermethod any time:

Objective-C Swift
[Countly.sharedInstance setNewDeviceID:CLYTemporaryDeviceID onServer:NO];

 Note: When passing CLYTemporaryDeviceID for deviceID parameter, argument for onServerparameter does not matter.

As long as the device ID value is CLYTemporaryDeviceID, the SDK will be in temporary device ID mode and all requests will be on hold, but they will be persistently stored.

When in temporary device ID mode, method calls for presenting feedback widgets and updating remote config will be ignored.

Later, when the real device ID is set using setNewDeviceID:onServer method, all requests which have been kept on hold until that point will start with the real device ID:

Objective-C Swift
[Countly.sharedInstance setNewDeviceID:@"real_device_id" onServer:NO];

Note: When setting real device ID while the current device ID is CLYTemporaryDeviceID, argument for the onServer parameter does not matter.

Changing Device ID

You may use the setNewDeviceID:onServer method to change your device ID on runtime after you start Countly. You may either allow the device to be counted as a new device or merge existing data on the server.

If theonServer bool is set, the old device ID on the server will be replaced with the new one, and data associated with the old device ID will be merged automatically.

Objective-C Swift
[Countly.sharedInstance setNewDeviceID:@"new_device_id" onServer:YES];
//replace and merge on server

Otherwise, if onServer bool is not set, the device will be counted as a new device on the server.

Objective-C Swift
[Countly.sharedInstance setNewDeviceID:@"new_device_id" onServer:NO];
//no replace and merge on server, device will be counted as new

Note: To switch back to the default device ID, you may pass CLYDefaultDeviceID.

Handling User Login and Logout

If your app allows users to login, logged-in users may be tracked with a custom user ID (such as accountID, username, memberID, email, etc.) instead of a device ID. For these cases, you may use the userLoggedIn and userLoggedOut convenience methods for changing the device ID.

TheuserLoggedIn method handles switching from the device ID to a custom user ID for logged-in users. It is just a convenience method that sets passed user IDs as a new device ID and merges the existing data on the server.

Objective-C Swift
[Countly.sharedInstance userLoggedIn:@"UserID"];

TheuserLoggedOut method handles switching from the custom user ID to a default device ID for logged-out users. It is just a convenience method that handles setting the device ID to a default one and starting a new session. Afterwards, logged-out user's data will be tracked using the default device ID.

Objective-C Swift
[Countly.sharedInstance userLoggedOut];

Resetting Stored Device ID

In order to handle device ID changes for logged-in and logged-out users, the device ID specified in the CountlyConfig object of the deviceID property (or the default device ID, if not specified) will be persistently stored as well as the device ID passed to the setNewDeviceID:onServermethod at any time upon the first app launch. By this point, until you delete and re-install the app, the Countly iOS SDK will continue to use the stored device ID and ignore the deviceID property. So, if you set the deviceID property to something different upon future app launches during development, it will have no effect. In this case, you may set the resetStoredDeviceID flag on the CountlyConfig object in order to reset the stored device ID. This will reset the initially stored device ID and the Countly iOS SDK will work as if it is the first app launch.

Objective-C Swift
config.resetStoredDeviceID = YES;

After you start Countly once with the resetStoredDeviceID flag while developing, you may remove that line. The resetStoredDeviceID flag is not meant for production. It is only for debugging purposes while performing development and not being able to delete and re-install the app.

Security

You may specify extra security features on the CountlyConfig object:

Pinned Certificates

You may use optional pinnedCertificates on the CountlyConfig object for specifying bundled certificates to be used for public key pinning. Certificates from your Countly Server must be DER encoded and should have a .der, .cer or .crt extension. They must also be added to your project and be included in the Copy Bundles Resources.

Objective-C Swift
config.pinnedCertificates = @[@"mycertificate.cer"];

Custom Header Field

You may set the optional customHeaderFieldName to be sent with every request. This may be useful if your server requires special headers to be sent for security reasons. Every request sent to the Countly Server will have this custom HTTP header and its value will be what you specify as the customHeaderFieldValueproperty.

Objective-C Swift
config.customHeaderFieldName = @"X-My-Custom-Field";
config.customHeaderFieldValue = @"my_custom_value";

If you do not set the customHeaderFieldValue value while setting the customHeaderFieldName upon the initial Countly configuration (in case the value is not available upon app launch and has to be retrieved later on), requests will not start until you set it using the setCustomHeaderFieldValuemethod at a later time.

Objective-C Swift
[Countly.sharedInstance setCustomHeaderFieldValue:@"my_custom_value"];

Parameter Tampering Protection

You may set the optional secretSalt to be used for calculating the checksum of the request data which will be sent with each request using the &checksum256 field. You will need to set the exact same secretSalton the Countly Server. If the secretSalton the Countly Server is set, all requests would be checked for validity of the &checksum256field before being processed.

Objective-C Swift
config.secretSalt = @"mysecretsalt";

Other Settings

You may further specify your optional settings on the CountlyConfig:

Update Session Period

You may specify the updateSessionPeriod on the CountlyConfig object before starting Countly. It is used for session updating and periodically sending queued events to the server. If the updateSessionPeriod is not explicitly set, the default setting will be at 60 seconds for iOS, tvOS & macOS, and 20 seconds for watchOS.

Objective-C Swift
config.updateSessionPeriod = 300;

Event Send Threshold

You may specify the eventSendThreshold on the CountlyConfig object before starting Countly. It is used to send events requests to the server when the number of recorded custom events reaches the threshold without waiting for the next update session request. If the eventSendThreshold is not explicitly set, the default setting will be at 10 for iOS, tvOS & macOS, and 3 for watchOS.

Objective-C Swift
config.eventSendThreshold = 5;

Stored Requests Limit

You may specify the storedRequestsLimit on the CountlyConfig object before starting Countly. It is used to limit the number of requests stored when there is a Countly Server connection problem. If your Countly Server is down, queued requests may reach excessive numbers, causing delivery problems to the server and requests stored on the device. To prevent this from happening, the Countly iOS SDK will only store requests up to the storedRequestsLimit. If the number of stored requests reaches the storedRequestsLimit, the Countly iOS SDK will start to drop the oldest requests, storing the newest ones in their place. If the storedRequestsLimit is not explicitly set, the default setting will be at 1,000.

Objective-C Swift
config.storedRequestsLimit = 5000;

Always use the POST method

You may set the alwaysUsePOST flag on theCountlyConfig object before starting Countly. This flag is used for sending all requests using the HTTP POST method, regardless of their data size. If set, all requests will be sent using the HTTP POST method. Otherwise, only the requests with a file upload or data size of more than 2,048 bytes will be sent using the HTTP POST method.

Objective-C Swift
config.alwaysUsePOST = YES;

Custom URLSessionConfiguration

For additional networking settings, you can optionally set a custom URLSessionConfiguration on the CountlyConfig object, to be used with all requests sent to Countly Server.

If URLSessionConfiguration is not set, NSURLSessionConfiguration's defaultSessionConfiguration will be used by default.

Objective-C Swift
config.URLSessionConfiguration = NSURLSessionConfiguration.ephemeralSessionConfiguration;

Manual Session Handling

You may set the manualSessionHandling flag on the CountlyConfig object before starting Countly to handle sessions manually.

Objective-C Swift
config.manualSessionHandling = YES;

By default, the Countly iOS SDK tracks sessions automatically and sends the begin_sessionrequest upon initialization, the end_session request when the app goes to the background, and the begin_session request again when the app comes back to the foreground. In addition, the Countly iOS SDK automatically sends a periodical (60 sec by default) update session request while the app is in the foreground.

If the manualSessionHandling flag is set, the Countly iOS SDK does not send these requests automatically, meaning you will need to manually call the beginSession, updateSession and endSession  methods after you start Countly, depending on your own definition of a session.

Objective-C Swift
[Countly.sharedInstance beginSession];
[Countly.sharedInstance updateSession];
[Countly.sharedInstance endSession];

Custom Metrics

For overriding default metrics or adding extra ones that are sent with begin_session requests, you can use customMetricsdictionary on the CountlyConfig object.

Custom metrics should be an NSDictionary, with keys and values are both NSString 's only.

Objective-C Swift
config.customMetrics = @{@"key": @"value"};

For overriding default metrics, keys should be one of the CLYMetricKey 's:

CLYMetricKeyDevice
CLYMetricKeyOS
CLYMetricKeyOSVersion
CLYMetricKeyAppVersion
CLYMetricKeyCarrier
CLYMetricKeyResolution
CLYMetricKeyDensity
CLYMetricKeyLocale
CLYMetricKeyHasWatch
CLYMetricKeyInstalledWatchApp
Objective-C Swift
config.customMetrics = @{CLYMetricKeyAppVersion: @"1.2.3"};

Attribution

You may set the enableAttribution flag on CountlyConfig object to enable a campaign attribution. If set, the IDFA (Identifier For Advertising) will be sent with every begin_session request, unless the user has limited ad tracking in their iOS Settings.

Objective-C Swift
config.enableAttribution = YES;

Notes

Default Device ID

On iOS, iPadOS, and tvOS, the default device ID is the Identifier For Vendor (IDFV). On watchOS and macOS, it is a persistently stored random NSUUID string.

Automatic Reference Counting (ARC)

The Countly iOS SDK uses Automatic Reference Counting (ARC). If you are integrating the Countly iOS SDK into a non-ARC project, you should add the-fobjc-arc compiler flag to all Countly iOS SDK implementation (*.m) files found under Target > Build Phases > Compile Sources.

App Transport Security (ATS)

With App Transport Security introduced in iOS 9, connections not following some security requirements will fail. You may view these requirements here. If your Countly Server instance does not meet these requirements, you may need to add the NSAppTransportSecurity key into your targets' Info.plist files, with NSAllowsArbitraryLoads or NSExceptionDomains as the value, to communicate with your Countly Server.

Swift Projects

For using Countly on Swift based projects, please ensure your Bridging Header File is configured properly for each target. Then import the Countly.h file into the Bridging Header file, after which you may seamlessly use the Countly methods in your Swift projects.

For Notification Service Extension targets, import CountlyNotificationService.h into the Bridging Header file.

You may view more details on how to create a Bridging Header file here.

Updating the Countly iOS SDK

Before upgrading to a new version of the Countly iOS SDK, do not forget to remove the existing files from your project first. Then, while adding the new Countly iOS SDK files again, please ensure you have chosen the targets correctly and select the "Copy items if needed" checkbox.

CocoaPods

CocoaPods Support

While the Countly iOS SDK supports integration via CocoaPods, we may not be able to help you with issues stemming from the CocoaPods themselves, especially for some advanced use-cases.

You may integrate the Countly iOS SDK using CocoaPods. For more information, please see the Countly CocoaPods page. Please ensure you have the latest version of CocoaPods and your local spec repo is updated. For Notification Service Extension targets, please ensure your Podfile uses something similar to the following sub specs:

target 'MyMainApp' do
  platform :ios,'8.0'
  pod 'Countly'
end

target 'CountlyNSE' do
  platform :ios,'10.0'
  pod 'Countly/NotificationService'
end

For Swift projects with Notification Service Extension, please see: https://github.com/Countly/countly-sample-ios/issues/13#issuecomment-408652426

For Crash Reporting automatic dSYM uploading, please manually add the dSYM uploader script to your project.

Carthage

You may integrate the Countly iOS SDK using Carthage, just add the following to your project's Cartfile:

github "Countly/countly-sdk-ios"

App Store Connect IDFA Warning

As the Countly iOS SDK source has references to IDFA for the attribution feature, and App Store Connect checks for API usage, you may need to answer “Yes” for the "Does this app use the Advertising Identifier (IDFA)?" question on the App Store Connect app submit form. Please ensure you follow the instructions specified on the App Store Connect support page - Provide advertising identifier details section. Otherwise, your app may get rejected due to "Improper use of IDFA" or fail to proceed on app submitting. In the screenshot below you may see which checkboxes to select while sending your app to the App Store:

If you are using an advertisement system, you might also need to check the "Serve advertisements within the app" checkbox.

If you would not like any IDFA references to be part of your app, you may add the COUNTLY_EXCLUDE_IDFA=1 flag to the Build Settings > Preprocessor Macros section in Xcode. Once this flag has been added, IDFA references won't be a part of the final product, meaning you may directly answer No to the IDFA usage question on App Store Connect.

If you are adding the Countly iOS SDK source files directly to your project, ensure the flag is added to your app target. If you are adding it as a framework, ensure the flag is added to the framework target.

Rebranding

If you would like to rebrand the Countly iOS SDK or make it white-label, you may use this rebranding script:

https://gist.github.com/erkanyildiz/4ee00b4326bb666fac636ef74bbd8450

Frequently Asked Questions (FAQ) Page

For frequently asked questions about the Countly iOS SDK, you may refer to the FAQ page:

page:http://resources.count.ly/docs/ios-faq

Recording Events

Here is a quick summary on how to use the custom events recording methods:

Regular Events

We have recorded an event named purchase with different scenarios in the examples below:

  • purchase event occurred 1 time
Objective-C Swift
[Countly.sharedInstance recordEvent:@"purchase"];
  • purchase event occurred 3 times
Objective-C Swift
[Countly.sharedInstance recordEvent:@"purchase" count:3];
  • purchase event occurred 1 times with the total amount of 3.33
Objective-C Swift
[Countly.sharedInstance recordEvent:@"purchase" sum:3.33];
  • purchase event occurred 3 times with the total amount of 9.99
Objective-C Swift
[Countly.sharedInstance recordEvent:@"purchase" count:3 sum:9.99];
  • purchase event occurred 1 time from country : Germany, on app_version : 1.0
Objective-C Swift
NSDictionary* dict = @{@"country":@"Germany", @"app_version":@"1.0"};

[Countly.sharedInstance recordEvent:@"purchase" segmentation:dict];
  • purchase event occurred 2 times from country : Germany, on app_version : 1.0
Objective-C Swift
NSDictionary* dict = @{@"country":@"Germany", @"app_version":@"1.0"};

[Countly.sharedInstance recordEvent:@"purchase" segmentation:dict count:2];
  • purchase event occurred 2 times with the total amount of 6.66, from country: Germany, on app_version : 1.0
Objective-C Swift
NSDictionary* dict = @{@"country":@"Germany", @"app_version":@"1.0"};

[Countly.sharedInstance recordEvent:@"purchase" segmentation:dict count:2 sum:6.66];

Timed Events

In the examples below, we recorded a timed event called level24 to track how long it takes to complete:

  • level24 started
Objective-C Swift
[Countly.sharedInstance startEvent:@"level24"];
  • level24 ended
Objective-C Swift
[Countly.sharedInstance endEvent:@"level24"];

Additionally, you may provide more information, such as the segmentation, count, and sum while ending an event.

  • level24 ended 1 time with the total point of 34578, from country : Germany, on app_version : 1.0
Objective-C Swift
NSDictionary* dict = @{@"country":@"Germany", @"app_version":@"1.0"};

[Countly.sharedInstance endEvent:@"level24" segmentation:dict count:1 sum:34578];

The duration of the event will be calculated automatically when the endEvent method is called.

You may also cancel a started timed event using cancelEvent method:

Objective-C Swift
[Countly.sharedInstance cancelEvent:@"level24"];

Or, if you are measuring the duration of an event yourself, you may record it directly as follows:

  • level24 took 344 seconds to complete:
Objective-C Swift
[Countly.sharedInstance recordEvent:@"level24" duration:344];

Additionally, you may provide more information such as the segmentation, count, and sum.

  • level24 took 344 seconds to complete 2 times with the total point of 34578, from country : Germany, on app_version : 1.0
Objective-C Swift
NSDictionary* dict = @{@"country":@"Germany", @"app_version":@"1.0"};

[Countly.sharedInstance recordEvent:@"level24" segmentation:dict count:2 sum:34578 duration:344];

Event Names and Segmentation

Event names must be non-zero length valid NSString and segmentation must be an NSDictionary which does not contain any custom objects, as it will be converted to JSON.

Push Notifications

Setting up APNs Authentication

First, you will need to acquire Push Notification credentials from Apple using one of the following methods:

A) APNs Auth Key (preferred)
B) Universal (Sandbox + Production) Certificate

A) Getting an APNs Auth Key

APNs Auth Key is the preferred authentication method on APNs for a number of reasons, including less issues faced during configuration and the fact that it can reuse the same connection with multiple apps.

First go to the Create a New Key section on the Apple Developer website to get an APNs Auth Key.

Check the APNs option and create your key.

Then download your key and store it in a safe place, you won't be able to download it again.
You'll also need some identifiers to upload a key file to Countly:

  • Key ID (filled automatically if you kept the original Auth Key filename, otherwise visible on the key details panel)

  • Team ID (see Membership section)

  • Bundle ID (see App IDs section)

B) Getting APNs Universal (Sandbox + Production) Certificate

Please go to the Certificates section on the Certificates, Identifiers & Profiles page on the Apple Developer website. Click the plus sign and select the Apple Push Notification service SSL (Sandbox & Production) type. Follow the instructions. Once you are done, download it and double click to add it to your Keychain.

Next, you'll need to export your push certificate into p12 format. Please open the Keychain Access app, select the login keychain, and the My Certificates category. Search for your app ID and find the certificate starting with the Apple Push Services. Select both the certificate and its private key as shown in the screenshot below. Right click and choose Export 2 items... and save it. You're free to name the p12 file as you wish and to set up a passphrase or leave it empty.

Once you’ve downloaded your Auth Key or exported your certificate, you will need to upload it to your Countly Server. Please go to Management > Applications > Your App. Click on Edit and upload your Auth Key or exported certificate under the APN Credentials section.

After filling all the required fields, click the Validate button. Countly will check the validity of the credentials by initiating a test connection to the APNs. If validation succeeds, click Save changes.

Configuring iOS app

Using Countly Push Notifications on iOS apps is pretty straightforward. First, integrate the Countly iOS SDK as usual, if you still have yet to do so.

Then, under the Capabilities section of Xcode, enable Push Notifications and the Remote notifications Background Mode for your target, as shown in the screenshot below:

Now, start Countly in the application:didFinishLaunchingWithOptions:method of your app with the following configuration. Do not forget to specify CLYPushNotifications in the featuresarray of the CountlyConfigobject. Then you'll need to ask for user's permission for push notifications using the Countly askForNotificationPermission method at any point in the app. The Countly iOS SDK will automatically handle the rest. No need to call any other method for registering when a device token is generated, or a push notification is received.

Objective-C Swift
#import "Countly.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //Start Countly with CLYPushNotifications feature as follows
    CountlyConfig* config = CountlyConfig.new;
    config.appKey = @"YOUR_APP_KEY";
    config.host = @"https://YOUR_COUNTLY_SERVER";
    config.features = @[CLYPushNotifications];
//  config.pushTestMode = CLYPushTestModeDevelopment;
    [Countly.sharedInstance startWithConfig:config];


    //Ask for user's permission for Push Notifications (not necessarily here)
    //You can do this later at anypoint in the app after starting Countly
    [Countly.sharedInstance askForNotificationPermission];

    // your code

    return YES;
}

Note: Ensure you codesign your application using the explicit Provisioning Profile specific to your app's bundleID with an aps-environment key in it. You may get it from the iOS Provisioning Profiles section of the Apple Developer website. Be advised, wildcard (*) profiles or profiles aps-environment key do not work with APNs, and your device may not receive a push token.

Note: Please make sure you do not set UNUserNotificationCenter.currentNotificationCenter's delegate manually, as Countly iOS SDK will be acting as the delegate.

Note: To see how to send push notifications using the Countly Server, please check our Push Notifications documentation.

Rich Push Notifications (iOS 10+ only)

Rich push notifications allow you to send image, video, or audio attachments as well as customized action buttons on iOS10+. You will need to set up the Notification Service Extension to use it.

While the main project file is selected, please click the Editor > Add Target... menu in Xcode, and add a Notification Service Extension target.

Use the Product Name field of the Notification Service Extension target as you wish (for example: CountlyNSE) and ensure the Team is also selected.

Note: If Xcode asks a question about activating the scheme for a newly added Notification Service Extension target, you may select Cancel.

Under the Build Phases > Compile Sources section of a newly added extension target, click the+ sign.

Select CountlyNotificationService.m from the list.

Note: If you cannot see the CountlyNotificationService.m file because you are using CocoaPods or Carthage for integration, please locate it yourself (probably under the Pods folder) and add it to your project manually.

Then find the NotificationService.m file (NotificationService.swift in Swift projects) in the extension target. It is a default template file added automatically by Xcode. Import CountlyNotificationService.h inside this file.

Objective-C Swift
#import "CountlyNotificationService.h"

Then add the following line at the end of the  didReceiveNotificationRequest:withContentHandler: method as shown below:

Objective-C Swift
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler
{
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    //delete existing template code, and add this line
    [CountlyNotificationService didReceiveNotificationRequest:request withContentHandler:contentHandler];
}

Note: Please ensure you also configure the App Transport Security setting in the extension's Info.plist file just as with the main application. Otherwise, media attachments from non-https sources cannot be loaded.

Note: Please ensure you check that the Deployment Target version of the extension target is 10, not 10.3 (or whatever minor version Xcode set automatically). Otherwise, users running iOS versions lower than the Deployment Target value will not be able to get rich push notifications.

Provisional Permission for Push Notifications (iOS 12+ only)

iOS12 has a new feature called Provisional Permission for push notifications, and it is granted by default for all users. Without showing the notification permission dialog and without requiring users to accept anything, it allows you to send notifications to the users.

However, these notifications vary slightly, they do not actually notify the users. There are no alerts, no banners, no sounds, no badges. Nothing informing the users at the moment of notification delivery. Instead, these notifications go directly to the Notification Center and they silently pile up in the list. Only when the user goes to the Notification Center and checks the list does he/she become aware of them.

To utilize Provisional Permission for push notifications (while requesting for notification permission types), you pass a new type, called UNAuthorizationOptionProvisional.

Objective-C Swift
UNAuthorizationOptions authorizationOptions = UNAuthorizationOptionProvisional;

[Countly.sharedInstance askForNotificationPermissionWithOptions:authorizationOptions completionHandler:^(BOOL granted, NSError *error)
{
    NSLog(@"granted: %d", granted);
    NSLog(@"error: %@", error);
}];

If this is the only notification permission type for which you ask, there will be no permission dialog and it will be granted by default. Then, later on, the Notification Center users may swipe on these provisional notifications and cancel the provisional permission anytime they please. The notification permission level then changes to the UNAuthorizationStatusDenied from the UNAuthorizationStatusProvisional state. This functions is a kind of opt-out.

How Push Notifications Work in Countly

When a push notification is received, the Countly iOS SDK handles everything automatically.

First, it checks if the notification payload has the Countly specific dictionary (c key) and the notification ID inside it (ikey). If the Countly specific dictionary is present, it processes the notification. Otherwise, it does nothing. In both cases, the Countly iOS SDK forwards the notification to the default application delegate implementation for manual handling.

The processing of the notification payload depends on the iOS version, the application’s status (background or foreground) at the time of notification reception, and the notification payload's content.

If there is a media attachment or custom action buttons, the Notification Service Extension handles everything automatically. Users may view these notifications via their device’s 3D Touch or by swiping up on older devices.

When the app is not in the foreground, it waits for the user's interaction (e.g. tapping the actual notification or one of the custom action buttons). After the user's interaction, it automatically records a specific event indicating that that user has opened the push notification. If the user tapped one of the custom action buttons, it also records another specific event with button index segmentation and redirects them to the specified URL for that action.

When the app is in foreground, it uses UNNotificationPresentationOptionAlert mode on iOS10+ to present a default notification banner and it uses the system UIAlertController on older iOS versions and directly records push-opened events.

You may view the detailed flow in this chart (download the chart for a larger view):

Deep Linking

When you send a push notification with custom actions buttons, you may redirect users to any custom page or view in your app by specifying deep links as custom actions button URLs. To do so, you will first need to create a URL scheme (e.g. : myapp://) in your project.

To do so, select your app target in Xcode and open the Info tab. Then, open the URL Types section by clicking the horizontal arrow, and click the plus + sign there.

Enter an identifier (preferably in reverse domain format) into the Identifier field and enter your app's URL scheme (without ://part) into the URL Schemes field. Optionally, you may set an Icon. You may leave the Role field as whatever its default value is. When you are done, you may confirm that your new URL scheme has been added to your app's Info.plist file. It should look like this:

After setting up the URL scheme, you should add the application:openURL:options: method to your app delegate:

Objective-C Swift
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
    //handle URL here to navigate to custom views

    return YES;
}

If your app's deployment target is lower than iOS9, you should add the application:openURL:sourceApplication:annotation: method instead:

Objective-C Swift
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation
{
    //handle URL here to navigate to custom views

    return YES;
}

Then in this method, you may check the passed url for custom view navigation using thescheme and host properties. For example, if you set the custom action button URLs as countly://productA and countly://productB, you may use something similar to this snippet:

Objective-C Swift
if ([url.scheme isEqualToString: @"countly"])
{
    if ([url.host isEqualToString: @"productA"])
    {
        // present view controller for Product A;
    }
    else if ([url.host isEqualToString: @"productB"])
    {
        // present view controller for Product B;
    }
  
   // or you can use host property directly
}

When users tap on the custom action buttons, the Countly iOS SDK will open the specified URLs with your app's scheme. Following this, the related method you added to your app's delegate will be called.

Advanced Setup

Test Mode

For Development builds (where the project is code signed with an iOS Development Provisioning Profile), you should set pushTestModeas CLYPushTestModeDevelopmenton CountlyConfig object.

Objective-C Swift
config.pushTestMode = CLYPushTestModeDevelopment;

For TestFlight or AdHoc Distributon builds (where the project is code signed with an iOS Distribution Provisioning Profile), you should set pushTestModeas CLYPushTestModeTestFlightOrAdHocon CountlyConfig object.

Objective-C Swift
config.pushTestMode = CLYPushTestModeTestFlightOrAdHoc;

 So, you can send test push notifications to test devices on Countly Server Create Message screen.

Note: For App Store production builds, no need to set  pushTestModeon CountlyConfig object at all.

Disabling Alerts Shown by Notification

To disable messages from automatically being shown by the CLYPushNotifications feature while the app is in the foreground, you may set the doNotShowAlertForNotifications flag on the CountlyConfig object. If set, no message will be displayed by using the default system UI in the app, but push-open events will be recorded automatically.

Objective-C Swift
config.doNotShowAlertForNotifications = YES;

Manually Handling Notifications

If you would like to do additional custom work when a push notification is received, all you need to do is implement the default push-related methods in your application delegate (e.g. AppDelegate.m). After finishing its internal work, the Countly iOS SDK will push forward the related method calls to the default implementations on the application delegate.

Please ensure you do not set theUNUserNotificationCenter.currentNotificationCenter's delegate manually, as the Countly iOS SDK will be acting as the delegate. All you need to do is directly add theUNUserNotificationCenterDelegate methods to your application delegate class.

Inside the push notification userInfodictionary you may find all the necessary information under the Countly Payload dictionary specified by the c (kCountlyPNKeyCountlyPayload) key. The array of the custom action buttons is specified by theb (kCountlyPNKeyButtons) key here, and each custom action button's title and action URL is specified by thet (kCountlyPNKeyActionButtonTitle) and l (kCountlyPNKeyActionButtonURL) keys, respectively. Here is an example of the Countly Push Notification Payload:

{
  "aps": 
  {
    "alert": "this is notification text",
    //or     {"title": "title string", "body": "message string"}, //if title is set separately
        "sound": "sound_name", //if sound is set 
        "badge": 123, //if badge is set 
        "content-available": 1, //if data only flag is set
        "mutable-content": 1, //if buttons or media attachment is set      
  },

  "c": 
  {
    "i": "100001", //notification ID
    "l": "https://example.com/defaultlink", //if default link is set
        "a": "https://rich.media.url.jpg", //if media attachment is  set
    "b": //if buttons is set
    [
      {
        "t": "Custom Action Button Title 1", //button title
        "l": "https://example.com/test1", //button link
      },
      {
        "t": "Custom Action Button Title 2",
        "l": "https://example.com/test2",
      },
    ],
  },
  
  //any other custom data if set
}

You may create your own custom UI to display notification messages and custom action buttons according to your needs, along with URLs to redirect users when action is taken. Once users take action by clicking your custom buttons, you will need to manually report this event using this method:

Objective-C Swift
NSDictionary* userInfo;     // notification dictionary
NSInteger buttonIndex = 1;  // clicked button index
                            // 1 for first action button
                            // 2 for second action button
                            // 0 for default action
[Countly.sharedInstance recordActionForNotification:userInfo clickedButtonIndex:buttonIndex];

Always Sending Push Tokens

Thanks to iOS’ Remote Notification Background Mode, silent push notifications may be sent to users who have not given notification permission. However, the Countly iOS SDK does not send push tokens to the server by default from users who have not given permission for notifications. You may change this by setting the sendPushTokenAlways flag of the CountlyConfig object. If set, push tokens from all users, regardless of their notification permission status, will be sent to the Countly server and these users will be listed as possible recipients on the Create Message screen of the Countly Dashboard. Be advised; these users may not be notified by an alert, sound, or badge. This is useful only for sending data via silent notifications.

Objective-C Swift
config.sendPushTokenAlways = YES;

Notification Permission with Preferred Types and Callback

As asking for users’ permission for push notifications differ by iOS versions, the Countly iOS SDK has a one-liner convenience method, askForNotificationPermission, which does this for both iOS10 and older versions. It simply asks for a user's permission for all available notification types. However, if you need to specify which notification types your app will use (alert, badge, sound) or if you need a callback to see a user's response to the permission dialog, you may use theaskForNotificationPermissionWithOptions:completionHandler: method.

Objective-C Swift
UNAuthorizationOptions authorizationOptions = UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert;

[Countly.sharedInstance askForNotificationPermissionWithOptions:authorizationOptions completionHandler:^(BOOL granted, NSError *error)
{
    NSLog(@"granted: %d", granted);
    NSLog(@"error: %@", error);
}];

GeoLocation

Enterprise Edition Feature

This feature is only available with an Enterprise Edition subscription.

Countly allows you to send GeoLocation-based push notifications to your users. By default, the Countly Server uses the GeoIP database to deduce a user's location. However, if your app has a better mean of detecting location, you may send this information to the Countly Server by using the initial configuration properties or relevant methods.

Initial configuration properties may be set on the CountlyConfig object to be sent upon SDK initialization. These include:

  • location: a CLLocationCoordinate2D struct specifying latitude and longitude
  • ISOCountryCode an NSString in ISO 3166-1 alpha-2 format country code
  • city an NSString specifying city name
  • IP an NSString specifying an IP address in IPv4 or IPv6 format
Objective-C Swift
config.location = (CLLocationCoordinate2D){35.6895,139.6917};

config.city = @"Tokyo";

config.ISOCountryCode = @"JP";

config.IP = @"255.255.255.255"

GeoLocation info recording methods may also be called at any time after the Countly iOS SDK has started. Values recorded using these methods will override the values specified upon initial configuration.

Objective-C Swift
[Countly.sharedInstance recordLocation:(CLLocationCoordinate2D){35.6895,139.6917}];

[Countly.sharedInstance recordCity:@"Tokyo" andISOCountryCode:@"JP"];

[Countly.sharedInstance recordIP:@"255.255.255.255"];

GeoLocation info may also be disabled:

Objective-C Swift
[Countly.sharedInstance disableLocationInfo];

Once disabled, you may re-enable GeoLocation info by calling the recordLocation: or recordCity:andISOCountryCode: or recordIP: method.

Custom Alert Sounds

The playing of notification sounds is handled by iOS, so all limitations would also be dictated by it. At the time of writing, audio data formats supported by iOS are: Linear PCM, MA4 (IMA/ADPCM), µLaw, aLaw. And that data needs to be packaged in aiff, wav, or caf file formats.

More information can be found here under "Preparing Custom Alert Sounds" section.

You have to add that audio file to your application target. If you have added an audio file called "exampleSound.wav" to your project, when sending a push notification, you should enter "exampleSound.wav" inside the "Send sound" field on Countly Server push notifications dashboard.

macOS launchNotification

launchNotification property on initial configuration needs to be set in applicationDidFinishLaunching: method of macOS apps that use CLYPushNotifications feature, in order to handle app launches by push notification click.

Objective-C Swift
config.launchNotification = notification;

Crash Reporting

Automatic Crash Reporting

For Countly Crash Reporting, you'll need to specify CLYCrashReporting in features array on the CountlyConfig object before starting Countly.

Objective-C Swift
config.features = @[CLYCrashReporting];

With this feature, the Countly iOS SDK will generate a crash report if your application crashes due to an exception and send it to the Countly Server for further inspection. If a crash report cannot be delivered to the server (e.g. no internet connection, unavailable server) at the time of the crash, the Countly iOS SDK will then store the crash report locally in order to make another attempt at a later time.

Manually Handled Exceptions

You may manually record all handled exceptions, except for automatically reported unhandled exceptions and crashes:

Objective-C Swift
NSException* myException = [NSException exceptionWithName:@"MyException" reason:@"MyReason" userInfo:@{@"key":@"value"}];

[Countly.sharedInstance recordHandledException:myException];

You may also manually pass stack trace at the time of the handled exception:

Objective-C Swift
NSException* myException = [NSException exceptionWithName:@"MyException" reason:@"MyReason" userInfo:@{@"key":@"value"}];

[Countly.sharedInstance recordHandledException:myException withStackTrace:[NSThread callStackSymbols]];

Crash Report Contents

A crash report includes the following information:

Default Crash Report Information

- Exception Info:
  * Exception Name
  * Exception Description
  * Stack Trace
  * Binary Images

- Device Static Info:
  * Device Type
  * Device Architecture
  * Resolution
  * Total RAM
  * Total Disk

- Device Dynamic Info:
  * Used RAM
  * Used Disk
  * Battery Level 
  * Connection Type
  * Device Orientation

- OS Info:
  * OS Name
  * OS Version
  * OpenGL ES Version
  * Jailbrake State

- App Info:
  * App Version
  * App Build Number
  * Executable Name
  * Time Since App Launch
  * Background State

- Custom Info:
  * Crash logs recorded using `recordCrashLog:` method
  * Crash segmentation specified in `crashSegmentation` property

Custom Crash Logs

You may use the recordCrashLog: method to receive custom logs with the crash reports. Logs generated by the recordCrashLog:method are stored in a non-persistent structure and are delivered to the Countly Server only in the case of a crash.

Objective-C Swift
[Countly.sharedInstance recordCrashLog:@"This is a custom crash log."];

 There is limit for the number of crash logs to be stored on the device. By default it is 100. You can change it using crashLogLimit property on the CountlyConfig object.

Objective-C Swift
config.crashLogLimit = 500;

If number of stored crash logs reaches the limit, SDK will start to drop oldest crash log while appending the newest one.

Custom Crash Segmentation

If you would like to use custom crash segmentation, you may set the optional crashSegmentation dictionary on the CountlyConfig object.

Objective-C Swift
config.crashSegmentation = @{@"key":@"value"};

Crash Filtering

You can set the optional crashFilter regular expression on the CountlyConfig object for filtering crash reports and preventing them from being sent to Countly Server. If a crash's name, description or any line of stack trace matches the given regular expression, it will not be sent to Countly Server.

Objective-C Swift
config.crashFilter = [NSRegularExpression regularExpressionWithPattern:@".*keyword.*" options:NSRegularExpressionCaseInsensitive error:nil];

Symbolication

Enterprise Edition Feature

This feature is only available with an Enterprise Edition subscription.

Symbolication is the process of converting stack trace memory addresses in crash reports into human-readable, useful information, such as class/method names, file names, and line numbers.

In order to symbolicate memory addresses, the dSYM files for each build need to be uploaded to the Countly Server.

Automatic dSYM Uploading

For Automatic dSYM Uploading, you may use the countly_dsym_uploader script in the Countly iOS SDK.

To do so, go to the Build Phases section of your app target and click on the plus ( + ) icon on the top left, then choose New Run Script Phaseffrom the list.

Then, add the following snippet:

COUNTLY_DSYM_UPLOADER=$(/usr/bin/find $SRCROOT -name "countly_dsym_uploader.sh" | head -n 1)
sh "$COUNTLY_DSYM_UPLOADER" "https://YOUR_COUNTLY_SERVER" "YOUR_APP_KEY"

Next, select the checkboxRun script only when installing.

Note: Do not forget to replace your server and app key.

By default, Xcode will generate dSYM files for the Release build configuration, and the countly_dsym_uploader script will handle the uploading automatically. You may check for the results on the Report Navigator within Xcode. If the dSYM upload has completed successfully, you will see the[Countly] dSYM upload succesfully completed.message.

If there are any errors while uploading the dSYM file, you may also see these error messages in the Report Navigator. Some of the possible error reasons include: the dSYM file not being created due to build configurations, the dSYM file being created at a non-default location, wrong App ID and/or Countly Server address, or network unavailability.

Manual dSYM Uploading

In case of an error with Automatic dSYM Uploading, or if you would like to upload your dSYM files manually, you may use our guide for Manual dSYM Uploading here. You will also need to use Manual dSYM Uploading if Bitcode is enabled while uploading your app to App Store Connect.

Bitcode Enabled Apps

If Bitcode is enabled in your project while uploading your app to App Store Connect, Apple re-compiles your app to optimize it for specific devices. When Apple re-compiles your app, a new dSYM file is generated for the new build, and the dSYM file on your machine will not work for symbolication. So, you will need to receive this new dSYM file manually, then upload it to the Countly Server. In order to get the new dSYM file, you may use App Store Connect or Xcode Organizer.

Using App Store Connect: 1. Login to App Store Connect. 2. Go to the Activitytab. 3. Select your app's Version and Build 4. Under General Information click on Download dSYM. 5. If the downloaded file does not have any extension, add .zip and unarchive to see its content.

Using Xcode: 1. Open Organizer in Xcode. 2. Go to the Archives tab. 3. Select your app from the list on the left and select the archive. 4. Click on Download dSYMs.... 5. Xcode inserts the downloaded .dSYM files into the selected archive.

For more information regarding downloading dSYM files from Apple, please see Apple's documentation here.

After you receive your dSYM file from Apple, you may use our Manual dSYM Uploading guide.

How to Use Symbolication

Once your dSYM file has been uploaded to the Countly Server, you may symbolicate your crash reports coming from that build on the Crashespanel of your Countly Server.

A crash report symbolicated stack trace appears as follows:

Before symbolication:

YourAppName                               0x000000010006e174 YourAppName + 156020
YourAppName                               0x000000010006d060 YourAppName + 151648
YourAppName                               0x000000010006ad34 YourAppName + 142644

After symbolication:

-[MHViewController countlyProductionTest] (in YourAppName) (MHViewController.m:620)
-[MHViewController transitionToMahya] (in YourAppName) (MHViewController.m:443)
-[MHViewController textFieldShouldReturn:] (in YourAppName) (MHViewController.m:210)

For more information about how to use the Symbolication feature on the Countly Server, please see our Symbolication documentation here.

PLCrashReporter

As an alternative to Countly iOS SDK's own exception and signal handling mechanism based on NSSetUncaughtExceptionHandler() and signal() functions, you can optionally use more advanced PLCrashReporter as well.

For using PLCrashReporter instead of default crash handling mechanism you can set shouldUsePLCrashReporter flag on the CountlyConfig object.

If set, Countly iOS SDK will be using PLCrashReporter dependency for creating crash reports.

Objective-C Swift
config.shouldUsePLCrashReporter = YES;

And PLCrashReporter framework dependency should be added to your project manually from its GitHub releases page or using CocoaPods (Countly/PL subspec).

Existence of PLCrashReporter dependency will be checked using __has_include(<CrashReporter/CrashReporter.h>) preprocessor macro.

Note: PLCrashReporter option is available only for iOS apps.

Note: Currently tested and supported PLCrashReporter version is 1.5.1.

PLCrashReporter Signal Handler Type

PLCrashReporter has two different signal handling implementations with different traits:

1) BSD: PLCrashReporterSignalHandlerTypeBSD

2) Mach: PLCrashReporterSignalHandlerTypeMach

 For more information about PLCrashReporter please see: https://github.com/microsoft/plcrashreporter

By default, BSD type will be used. For using Mach type signal handler with PLCrashReporter you can set shouldUseMachSignalHandler flag on the CountlyConfig object.

Objective-C Swift
config.shouldUseMachSignalHandler = YES;

PLCrashReporter callback blocks

There is a crashOccuredOnPreviousSessionCallback block to be executed when the app is launched again following a crash which is detected by PLCrashReporter on the previous session. It has an NSDictionary parameter that represents crash report object. If shouldUsePLCrashReporter flag is not set on initial config, this block will never be executed. You can set it on the CountlyConfig object:

Objective-C Swift
config.crashOccuredOnPreviousSessionCallback = ^(NSDictionary * crashReport)
{
NSLog(@"crash report: %@", crashReport);
};

And there is also another shouldSendCrashReportCallback block to be executed to decide whether the crash report detected by PLCrashReporter on the previous session should be sent to Countly Server or not. If not set, crash report will be sent to Countly Server by default. If set, crash report will be sent to Countly Server only if YES is returned. It has an NSDictionary parameter that represents crash report object. If shouldUsePLCrashReporter flag is not set on initial config, this block will never be executed. You can set it on the CountlyConfig object:

Objective-C Swift
config.shouldSendCrashReportCallback = ^(NSDictionary * crashReport)
{
NSLog(@"crash report: %@", crashReport);
return YES; //NO;
};

User Profiles

Enterprise Edition Feature

This feature is only available with an Enterprise Edition subscription.

You may see detailed user information under the User Profiles section of the Countly Dashboard by recording user properties.

User Properties

You may record default and custom user detail properties by adhering to the following:

Objective-C Swift
//default properties
Countly.user.name = @"John Doe";
Countly.user.username = @"johndoe";
Countly.user.email = @"john@doe.com";
Countly.user.birthYear = @1970;
Countly.user.organization = @"United Nations";
Countly.user.gender = @"M";
Countly.user.phone = @"+0123456789";

//profile photo
Countly.user.pictureURL = @"http://s12.postimg.org/qji0724gd/988a10da33b57631caa7ee8e2b5a9036.jpg";
//or local image on the device
Countly.user.pictureLocalPath = localImagePath;

//custom properties
Countly.user.custom = @{@"testkey1":@"testvalue1",@"testkey2":@"testvalue2"};

//save
[Countly.user save];

Note: Local images specified on the pictureLocalPath property will not be persisted exclusively. If a request fails and is retried later, the local image is expected to still be present on the exact same path. Otherwise, the upload will be aborted.

Custom User Property Modifiers

Also, you may use custom user property modifiers, such as the following:

Objective-C Swift
[Countly.user set:@"key101" value:@"value101"];
[Countly.user setOnce:@"key101" value:@"value101"];
[Countly.user unSet:@"key101"];

[Countly.user increment:@"key102"];
[Countly.user incrementBy:@"key102" value:5];
[Countly.user multiply:@"key102" value:2];

[Countly.user max:@"key102" value:30];
[Countly.user min:@"key102" value:20];

[Countly.user push:@"key103" value:@"singlevalue"];
[Countly.user push:@"key103" values:@[@"a",@"b",@"c",@"d"]];
[Countly.user pull:@"key103" value:@"b"];
[Countly.user pull:@"key103" values:@[@"a",@"d"]];

[Countly.user pushUnique:@"key104" value:@"uniqueValue"];
[Countly.user pushUnique:@"key104" values:@[@"uniqueValue2",@"uniqueValue3"]];

//save
[Countly.user save];

View Tracking

Auto View Tracking

For Countly Auto View Tracking, you will need to specify CLYAutoViewTracking in features  array on the CountlyConfig object before starting Countly.

Objective-C Swift
config.features = @[CLYAutoViewTracking];

After this step, the Countly iOS SDK will automatically track appeared and disappeared views. It simply intercepts the viewDidAppear: method of the UIViewControllerclass and reports which view is displayed with the view's name and duration. If the view controller's title property is set, the reported view's name will be the value of the title property. Otherwise, it will be the view controller's class name.

You may temporarily enable or disable Auto View Tracking using the isAutoViewTrackingActive property.

Objective-C Swift
Countly.sharedInstance.isAutoViewTrackingActive = NO;

If the Auto View Tracking feature is not enabled upon initial configuration, enabling or disabling this property at a later time will have no effect. It will always be disabled.

Exception View Controllers

Following system view controllers will be excluded by default from auto tracking, as they are not visible views but rather structural controllers:

UINavigationController
UIAlertController
UIPageViewController
UITabBarController
UIReferenceLibraryViewController
UISplitViewController
UIInputViewController
UISearchController
UISearchContainerViewController
UIApplicationRotationFollowingController
MFMailComposeInternalViewController
MFMailComposeInternalViewController
MFMailComposePlaceholderViewController
UIInputWindowController
_UIFallbackPresentationViewController
UIActivityViewController
UIActivityGroupViewController
_UIActivityGroupListViewController
_UIActivityViewControllerContentController
UIKeyboardCandidateRowViewController
UIKeyboardCandidateGridCollectionViewController
UIPrintMoreOptionsTableViewController
UIPrintPanelTableViewController
UIPrintPanelViewController
UIPrintPaperViewController
UIPrintPreviewViewController
UIPrintRangeViewController
UIDocumentMenuViewController
UIDocumentPickerViewController
UIDocumentPickerExtensionViewController
UIInterfaceActionGroupViewController
UISystemInputViewController
UIRecentsInputViewController
UICompatibilityInputViewController
UIInputViewAnimationControllerViewController
UISnapshotModalViewController
UIMultiColumnViewController
UIKeyCommandDiscoverabilityHUDViewController

In addition to these default exceptions, you may manually add your own exception view controllers using the addExceptionForAutoViewTracking:method by passing the view controller class name or title:

Objective-C Swift
[Countly.sharedInstance addExceptionForAutoViewTracking:NSStringFromClass(MyViewController.class)];
//or
[Countly.sharedInstance addExceptionForAutoViewTracking:@"MyViewControllerTitle"];

Added view controller class name or titles will be ignored by Auto View Tracking and their appearances and disappearances will not be reported. Adding an already added view controller class name or title a second time will have no effect.

Furthermore, you may manually remove added exception view controllers using the removeExceptionForAutoViewTracking method by passing the view controller class name or title:

Objective-C Swift
[Countly.sharedInstance removeExceptionForAutoViewTracking:NSStringFromClass(MyViewController.class)];
//or
[Countly.sharedInstance removeExceptionForAutoViewTracking:@"MyViewControllerTitle"];

Removing an already removed (or not yet added) view controller class name or title again will have no effect.

Manual View Tracking

In addition to Auto View Tracking, you may manually record the appearance of a view using the recordView:method with the view's name:

Objective-C Swift
[Countly.sharedInstance recordView:@"MyView"];

When you record another view at a later time, the duration of the previous view will be calculated, and the view tracking event will be recorded automatically.

You may also specify the custom segmentation key-value pairs while recording views:

Objective-C Swift
[Countly.sharedInstance recordView:@"MyView" segmentation:@{@"key": @"value"}];

 

Feedback

Star Rating

Optionally, you may set the Countly iOS SDK to automatically ask users for a 1 to 5-star rating, depending on the app launch count for each version. To do so, you will need to set the starRatingSessionCount property on the CountlyConfig object. When the total number of sessions reaches the starRatingSessionCount, an alert view asking for a 1 to 5-star rating will be displayed automatically, once for each new version of the app.

Objective-C Swift
config.starRatingSessionCount = 10;

If you would like the star-rating dialog to only be displayed once per app lifetime, instead of for each new version, you may set the starRatingDisableAskingForEachAppVersion flag on the CountlyConfig object.

Objective-C Swift
config.starRatingDisableAskingForEachAppVersion = YES;

Additionally, you may customize the star-rating dialog message using the starRatingMessage property on the CountlyConfigobject. If you do not explicitly specify this property, the message will read, "How would you rate the app?" or a corresponding localized version depending on the device language. Currently supported localizations: English, Turkish, Japanese, Chinese, Russian, Czech, Latvian, and Bengali.

Objective-C Swift
config.starRatingMessage = @"Please rate our app?";

Additionally, you may set the starRatingCompletionblock property on the CountlyConfigobject to be executed after the star-rating dialog has been automatically shown. The completion block has a single NSInteger parameter that indicates the 1 to 5-star rating given by the user. If the user dismissed the dialog without giving a rating, the value for this rating will be 0, and it will not be reported to the server.

Objective-C Swift
config.starRatingCompletion = ^(NSInteger rating)
{
    NSLog(@"rating %d",(int)rating);
};

Additionally, you may use the askForStarRating: method to ask for a star rating anytime you would like. It displays the 1 to 5-star rating dialog manually and executes the completion block after the user's action. The completion block takes a single NSInteger parameter that indicates the 1 to 5-star rating given by the user. If the user dismissed the dialog without giving a rating, the value for this rating will be 0, and it will not be reported to the server. Manually asking for a star rating does not affect the automatically requested nature of the star rating.

Objective-C Swift
[Countly.sharedInstance askForStarRating:^(NSInteger rating)
{
    NSLog(@"rating %li",(long)rating);
}];

Feedback Widgets

You may use the Countly iOS SDK to display feedback widgets configured on the Countly Server. For more information on feedback widgets, please visit the Ratings & Feedback documentation.

Here is how you may utilize feedback widgets in your iOS apps:

Once you call the presentFeedbackWidgetWithID:completionHandler: method, the feedback widget with the given ID will be displayed in a WKWebView, having been placed in the UIViewController.

First, the availability of the feedback widget will be checked asynchronously. If the feedback widget is available, it will be modally presented. Otherwise, the completionHandler will be called with an NSError. The completionHandler will also be called with nil when the feedback widget is dismissed by the user.

Objective-C Swift
[Countly.sharedInstance presentFeedbackWidgetWithID:@"FEEDBACK_WIDGET_ID" completionHandler:^(NSError* error)
{
    if (error)
        NSLog(@"Feedback widget presentation failed: \n%@\n%@", error.localizedDescription, error.userInfo);
    else
        NSLog(@"Feedback widget presented successfully");
}];

Remote Config

The Remote Config feature allows you to change the behavior and appearance of your applications at any time, without sending an update to the App Store by creating or updating custom key-value pairs on your Countly Server. You may also create conditions to get different values depending on user criteria. For more details, please see the Remote Config documentation.

Here is how you may utilize the Remote Config feature in your iOS apps:

First, you will need to enable the Remote Config feature upon initial configuration:

Objective-C Swift
config.enableRemoteConfig = YES;

Once completed, the Countly iOS SDK will automatically fetch the Remote Config keys and values from the Countly Server when launched.

You may also set a completion block on the initial configuration object to be informed about the remote config fetching process results, either with success or failure:

Objective-C Swift
config.remoteConfigCompletionHandler = ^(NSError * error)
{
    if (!error)
    {
        NSLog(@"Remote Config is ready to use!");
    }
    else
    {
        NSLog(@"There was an error while fetching Remote Config:\n%@", error);
    }
};

Once fetched, you may access the Remote Config values using the remoteConfigValueForKey method:

Objective-C Swift
id value = [Countly.sharedInstance remoteConfigValueForKey:@"foo"];

if (value) // if value exists, you can use it as you see fit
{
        NSLog(@"Value %@", [value description]);
}
else //if value does not exist, you can set your default fallback value
{
    value = @"Default Value";   
}

The Remote Config values are stored locally on your device. This means you may access the latest fetched values even while the Countly Server is not reachable. If the Remote Config was never retrieved from the Countly Server before or after the given key was not defined, this method will return as nil, meaning you may fall back to your desired default value.

You may also trigger the fetching and updating of Remote Config values anytime you would like:

Objective-C Swift
[Countly.sharedInstance updateRemoteConfigWithCompletionHandler:^(NSError * error)
{
    if (!error)
    {
        NSLog(@"Remote Config is updated and ready to use!");
    }
    else
    {
        NSLog(@"There is an error while updating Remote Config:\n%@", error);
    }
}];

You may also trigger partial updates for the Remote Config values you would like at any time. Adhere to the following in order to only update the values for the keys specified by you:

Objective-C Swift
[Countly.sharedInstance updateRemoteConfigOnlyForKeys:@[@"key1", @"key2"] completionHandler:^(NSError * error)
{
    if (!error)
    {
        NSLog(@"Remote Config is updated only for given keys and ready to use!");
    }
    else
    {
        NSLog(@"There is an error while updating Remote Config:\n%@", error);
    }
}];

To update the values, except for the keys specified by you, adhere to the following:

Objective-C Swift
[Countly.sharedInstance updateRemoteConfigExceptForKeys:@[@"key3", @"key4"] completionHandler:^(NSError * error)
{
    if (!error)
    {
        NSLog(@"Remote Config is updated except for given keys and ready to use !");
    }
    else
    {
        NSLog(@"There is an error while updating Remote Config:\n%@", error);
    }
}];

Performance Monitoring

Performance Monitoring feature allows you to analyze your application's performance on various aspects. For more details please see Performance Monitoring documentation.

Here is how you can utilize Performance Monitoring feature in your iOS apps:

First, you need to enable Performance Monitoring feature on the initial configuration:

Objective-C Swift
config.enablePerformanceMonitoring = YES;

With this, Countly iOS SDK will start measuring some performance traces automatically. Those include app start time, app foreground time, app background time. Additionally, custom traces and network traces can be manually recorded.

App Start Time

For the app start time to be recorded, you need to call appLoadingFinished method.

It calculates and records the app launch time for performance monitoring.
It should be called when the app is loaded and it successfully displayed its first user-facing view. E.g. viewDidAppear: method of the root view controller or whatever place is suitable for the app's flow. Time passed since the app has started to launch will be automatically calculated and recorded for performance monitoring. App launch time can be recorded only once per app launch. So, the second and following calls to this method will be ignored.

Objective-C Swift
[Countly.sharedInstance appLoadingFinished];

Manual Network Traces

You may record manual network traces using therecordNetworkTrace:requestPayloadSize:responsePayloadSize:responseStatusCode:startTime:endTime: method.

A network trace is a collection of measured information about a network request.
When a network request is completed, a network trace can be recorded manually to be analyzed in the Performance Monitoring feature later with the following parameters: 

- traceName: A non-zero length valid string
- requestPayloadSize: Size of the request's payload in bytes
- responsePayloadSize: Size of the received response's payload in bytes
- responseStatusCode: HTTP status code of the received response
- startTime: UNIX time stamp in milliseconds for the starting time of the request
- endTime: UNIX time stamp in milliseconds for the ending time of the request

Objective-C Swift
[Countly.sharedInstance recordNetworkTrace:@"/test/endpoint" requestPayloadSize:3445 responsePayloadSize:1290 responseStatusCode:200 startTime:1593418666954 endTime:1593418667384];

Custom Traces

You may also measure any operation you want and record it using custom traces. First, you need to start a trace by using the startCustomTrace method:

Objective-C Swift
[Countly.sharedInstance startCustomTrace:@"unzipping_saved_files"];

Then you may end it using the endCustomTrace:metrics:method, optionally passing any metrics as key-value pairs:

Objective-C Swift
[Countly.sharedInstance endCustomTrace:@"unzipping_saved_files" metrics:@{@"total_file_size": @1655700}];

The duration of the custom trace will be automatically calculated on ending. Trace names should be non-zero length valid strings. Trying to start a custom trace with the already started name will have no effect. Trying to end a custom trace with already ended (or not yet started) name will have no effect.

You may also cancel any custom trace you started, using cancelCustomTrace:method:

Objective-C Swift
[Countly.sharedInstance cancelCustomTrace:@"unzipping_saved_files"];

Additionally, if you need you may cancel all custom traces you started, using theclearAllCustomTracesmethod:

Objective-C Swift
[Countly.sharedInstance clearAllCustomTraces];

Note: All previously started custom traces are automatically cleaned when:
- Consent for CLYConsentPerformanceMonitoring is canceled.
- A new app key is set using the setNewAppKey: method.

watchOS Integration

Just like iPhones and iPads, collecting and analyzing usage statistics and analytics data from an Apple Watch is the key for offering a better experience. Fortunately, the Countly iOS SDK has watchOS support. Here you can find out how to use the Countly iOS SDK in your watchOS apps:

1. First, open a new or your existing Xcode project and add a new target by clicking the + icon at the bottom of the Projects and Targets List. (You may skip to the step 4 if your project already has a Watch App, or you may visit http://apple.co/1PnD1uT for more information)

2. Select the target template WatchKit App under the watchOS > Application section. Do not choose the WatchKit App for watchOS 1. In order to keep things simple, do not include the Notification scene, Glance scene, or Complication.

3. If Xcode asks whether you would like to activate the WatchKit App scheme, click Activate.

4. Now it is time to add the Countly iOS SDK to your project. After cloning the Countly iOS SDK anywhere you would like, Drag&Drop the countly-sdk-ios folder into your Xcode project, and in the following dialog, please ensure the iPhone app target and WatchKit Extension target (not WatchKit App) have been selected as well as the Copy items if needed checkbox.

5. Import Countly.h into ExtensionDelegate.m

Objective-C Swift
#import "Countly.h"

6. Add the Countly starting code into the applicationDidFinishLaunching method ofExtensionDelegate.m

Objective-C Swift
CountlyConfig* config = CountlyConfig.new;
config.appKey = @"YOUR_APP_KEY";
config.host = @"https://YOUR_COUNTLY_SERVER";
config.enableAppleWatch = YES;
[Countly.sharedInstance startWithConfig:config];

NOTE: config.enableAppleWatch flag should not be set on independent watchOS apps.

7. Add suspend code into the applicationWillResignActive method of ExtensionDelegate.m

Objective-C Swift
[Countly.sharedInstance suspend];

8. Add resume code into the applicationDidBecomeActive method of ExtensionDelegate.m

Objective-C Swift
[Countly.sharedInstance resume];

9. After adding these three lines of code into the related extension delegate methods, try building the project. Everything should be OK now. If you run the watchOS app, you may see the session on your Countly dashboard.

Now you are ready to track your watchOS app with Countly.

By the way, the session concept on watchOS is slightly different than the one on the iOS, as watchOS apps are intended for brief user interaction. So, there are two values you might need to adjust depending on your watch apps’ use cases.

  • The first value is updateSessionPeriod. Its default value is 20 seconds for watchOS and 60 seconds for iOS. This value determines how often session updating requests will be sent to the server while the app is in use.
  • The second value is eventSendThreshold, which is 3 for watchOS and 10 for iOS by default. The Countly iOS SDK waits for the number of recorded unique events to reach this threshold to deliver them to the server until the next session updating kicks in. Considering the fact that Apple Watch is designed to be used for short sessions, these values generally seem appropriate. However, you may change them depending on your watchOS app’s scenario.
Objective-C Swift
config.updateSessionPeriod = 15;
config.eventSendThreshold = 1;

Do not forget to set the enableAppleWatch flag of the CountlyConfigobject on your watch app's iOS counterpart:

Objective-C Swift
config.enableAppleWatch = YES;

With this setting, your watchOS app iOS counterpart will also report the paired Apple Watch's information to the server.

Consents

For compatibility with data protection regulations, such as GDPR, the Countly iOS SDK allows developers to enable/disable any feature at any time depending on user consent. Currently, available features with consent control are as follows:

CLYConsentSessions
CLYConsentEvents
CLYConsentUserDetails
CLYConsentCrashReporting
CLYConsentPushNotifications
CLYConsentLocation
CLYConsentViewTracking
CLYConsentAttribution
CLYConsentStarRating
CLYConsentAppleWatch
CLYConsentPerformanceMonitoring

You should set the requiresConsent flag upon initial configuration to utilize consents.

Objective-C Swift
config.requiresConsent = YES;

With this flag set, the Countly iOS SDK will not automatically collect or send any data and will ignore all manual calls. Until explicit consent is given for a feature, it will remain inactive. After consent for a feature is given, it will launch immediately and remain active from that time onward.

To give consent for a feature, you may use the giveConsentForFeature:method by passing the feature name:

Objective-C Swift
[Countly.sharedInstance giveConsentForFeature:CLYConsentSessions];
[Countly.sharedInstance giveConsentForFeature:CLYConsentEvents];

Or, you may give consent for more than one feature at a time using the giveConsentForFeatures:method by passing the feature names as an NSArray:

Objective-C Swift
[Countly.sharedInstance giveConsentForFeatures:@[CLYConsentSessions, CLYConsentEvents];

Or, if you would like to give consent for all the features, you may use the giveConsentForAllFeaturesconvenience method:

Objective-C Swift
[Countly.sharedInstance giveConsentForAllFeatures];

The Countly iOS SDK does not persistently store the status of given consents. You are expected to handle receiving consent from end-users using proper UIs depending on your app's context. You are also expected to store them either locally or remotely. Following this step, you will need to call the ‘giving consent’ methods on each app launch, right after starting the Countly iOS SDK depending on the permissions you managed to get from the end-users.

If the end-user changes his/her mind about consents at a later time, you will need to reflect this in the Countly iOS SDK using the cancelConsentForFeature:method:

Objective-C Swift
[Countly.sharedInstance cancelConsentForFeature:CLYConsentSessions];
[Countly.sharedInstance cancelConsentForFeature:CLYConsentEvents];

Or, you may cancel consent for more than one feature at a time using the cancelConsentForFeatures:method by passing the feature names as an NSArray:

Objective-C Swift
[Countly.sharedInstance cancelConsentForFeatures:@[CLYConsentSessions, CLYConsentEvents];

Or, if you would like to cancel consent for all the features, you may use the cancelConsentForAllFeaturesconvenience method:

Objective-C Swift
[Countly.sharedInstance cancelConsentForAllFeatures];

Once consent for a feature has been cancelled, that feature is stopped immediately and kept inactive from that point onward.

The Countly iOS SDK reports consent changes to the Countly Server, so that the Countly Server can make preparations, or clean-up on the server side as well.

Was this article helpful?
0 out of 0 found this helpful

Looking for help?