watchOS Swift Documentation

Prerequisites for Integration

  1. Register with Finotes using the 'Get Started' button in and login to dashboard.
  2. Use "Add App" to link watchOS application to Finotes.
  3. Integrate Finotes framework to your application.
  4. Test your integration.

Current Version 3.0.0

Change log

How to Integrate

Step One

You need to add cocoa pods to your project. You can find more information here

Step Two

Integrating FinotesWatchosCore alone
WatchKit Extension Target in Podfile:
pod 'FinotesWatchosCore', '3.0.0'

Integrating both FinotesWatchosCore and FinotesWatchosDebug

To leverage the capabilities of both FinotesWatchosCore and FinotesWatchosDebug,

WatchKit Extension Target in Podfile:
pod 'FinotesWatchosCore', '3.0.0', :configuration => ['Release']
pod 'FinotesWatchosDebug', '3.0.0', :configuration => ['Debug']

FinotesDebug framework contains all features in FinotesWatchosCore and:

1. Tracking framerate issues.

The –repo-update option should be used the first time pod install is run from terminal.

Then install the same by executing the following command from terminal where your Podfile resides.
Here the —repo-update is added incase your local cocoa pods repository is not up to date.

pod install --repo-update

Step Three

You can import the FinotesWatchosCore to your code using the import statement below.

Anywhere in Code:
import FinotesWatch

Initializing Finotes

You need to call the Fn.initialize(self) function in your ExtensionDelegate applicationDidFinishLaunching

import FinotesWatch

func applicationDidFinishLaunching() {

Testing Integration

Now that the basic integration of Finotes framework is complete,
Let us make sure that the dashboard and SDK are in sync.

Step One

Add Fn.test() after Fn.initialize()

ExtensionDelegate Class:
import FinotesWatch

func applicationDidFinishLaunching() {

Step Two

Now run the application in a simulator or real watchOS device (with active network connection).

Step Three

Once the application opens up, open Finotes dash.
The test issue that we raised should be reported.

In-case the issue is not listed, make sure the right app and platform is selected at the top of the dashboard.
If the issue is still not synced in Finotes dashboard, Click Here.

Remember to remove the Fn.test() call, else every time the app is run, an issue will be reported.

Now, Finotes is all set to report any memory related issues or any uncaught NSException based crashes.

Things to take care in Release Build

Once you are ready to release the application, if you have activated crash reporting using Finotes, you need to back up the dSYM file to symbolicate the crashes as they are reported. Locating dSYM file.

Detect memory leaks

Finotes Framework can detect all WKInterfaceController level leaks.
You need to override your WKInterfaceController from ObservableInterfaceController provided by Finotes.

WKInterfaceController.h file:
class HealthInsightsInterfaceController: ObservableInterfaceController {

Please make sure that all WKInterfaceController extended controllers are replaced by ObservableInterfaceController.

Track Network Calls

Finotes will be able to notify of any errors in API calls with just a single line.

If you are using URLSession with sharedSession then add,

ExtensionDelegate Class:
import FinotesWatch

func applicationDidFinishLaunching() {


Incase you are using AFNetworking, then you need to add the below code in your AFHTTPSessionManager.

let configuration = URLSessionConfiguration.default

configuration.protocolClasses = Fn.getProtocols(configuration.protocolClasses as! NSMutableArray) as? [AnyClass]

let manager = AFHTTPSessionManager(baseURL: URL(string: ""), sessionConfiguration: configuration)


Incase you are using Alamofire, then you need to add the below code in your URLSessionConfiguration.

let configuration = URLSessionConfiguration.default
configuration.protocolClasses = (Fn.getProtocols(NSMutableArray(array: configuration.protocolClasses!)) as! [AnyClass])

let manager = Alamofire.SessionManager(configuration: configuration)

Incase, you need more clarity on getting notified of network errors, Please initiate a chat with our development team directly using chat widget at the bottom right corner.
We will help you overcome any roadblocks that you may have.

Privacy (Optional feature)

As Finotes reports API call issues, each issue is tagged with corresponding request response headers, request body and associated parameters.
If header fields contain any sensitive data, Finotes provides a global and easy mechanism to mask such header fields using maskHeaders in info.plist file as shown in code snippet.
You may provide 1 or more header keys in the 'maskHeaders' field.


Open info.plist as Source Code, then add the below snippet


Use case:

Let us say API calls from the app has header field 'X-Key' which contains the authentication token.
It can be masked by providing the 'X-Key' in the field as shown above, masked header fields are filtered out from the app itself as is not sent to the Finotes dashboard, if any issues are raised.
The maskHeaders field is case insensitive and is optional.

Dynamic Path Component

When API call issues are reported, different urls are created as separate ticket.
This can cause large number of tickets generated for the same API incase the url contains an id or any other dynamic path component.


Use URLPatterns key in info.plist to specify the urls that contains dynamic path component.
Wrap the corresponding dynamic path component or id inside '{}'.

Track screen loading delay

Finotes Framework is capable of detecting InterfaceController loading delay by extending from ObservableInterfaceController, a custom class provided by Finotes.
If the extended InterfaceController takes more than 4 seconds to load then it will be raised as a bug report.

WKInterfaceController.swift file:
class HealthInsightsInterfaceController: ObservableInterfaceController {

Report Custom Issues

You can report custom issues using the Fn.reportIssue() API.

Any where in the project:
//Payment gateway delegate methods.
func paymentCompleted(_ userIdentifier:String, _ type:String){


func paymentFailed(_ reason:String, _ userId:String){

    Fn.setActivityMarker(atTarget: self, forMarker: String(format: "User Id %@",userId))

    Fn.reportIssue(atTarget: self, withShortDescription: "Payment failed", withDescription: reason)

Use case:

In cases where the fail situations can be anticipated, they may be reported using Fn.reportIssue().
You may pass a string as parameter to the anticipated issue.
As in custom exceptions, you will be able to make use of activity markers to aid reproducing the issue when reported.

Track Function Calls

Finotes will report all return value issues, exceptions and execution delays that may arise during function execution using

Regular function call:

func getUserNameFromDb(_ userId:String) -> String?{
    return User.findById(userId).name
Function call via Finotes:
import FinotesWatch #selector(getUserNameFromDb(_:)), withTarget: self, 

@objc func getUserNameFromDb(_ userId:String) -> String?{
    return User.findById(userId).name

1. You replace the old method of function invocation to the Finotes based mechanism for main functions in the watchOS project.
2. Finotes will report an issue, if the said function returns a NULL value or takes more than 1000 milliseconds for its execution or throws any exceptions even if caught using try{}catch{} block over the function call.
3. The function execution is seamless like the original method of invocation.
4. Near zero overhead in using Finotes based function invocation.
5. The function parameters that were passed to it during its invocation will be shown along with the issue in Finotes dashboard if any raised.

Observe fields

You can make use of expectedExecutionTime, severity, expectNull fields in Observer parameter of , withTarget: , withObserver:observer, withParameters: )

to control the issue reports from a function.


Incase, we have a function that may take more than 1000 milliseconds(default value) for execution then use this field in Observer to provide an ideal execution time.
Supplying proper values in Observer parameter will help Finotes raise better issue reports.

Observer parameter:
import FinotesWatch

    let observer : Observer = Fn.observe()
    observer.expectedExecutionTime(1400) #selector(getUserNameFromDb(_:)), withTarget: self, 
                        withObserver:observer, withParameters:"123-sd-12")

@objc func getUserNameFromDb(_ userId:String) -> String?{
    return User.findById(userId).name


If function returns an object and return value could be NULL, then set this field to true to prevent Finotes from raising an issue when function returns NULL.
By default field is false, and issue will be raised if function returns NULL.


Sets severity level of issues reported from a function using this field.
All issues raised from a function will have the same severity level, set to it using Observer parameter.
By default severity is MAJOR.

Track Feature failures

Finotes has the ability to invoke code level functions and report any issues that may arise from it.

Feature tracking allows developers to track a particular feature in watchOS app by chaining 2 or more functions invoked by Finotes.
Let us take the example of Chat feature in a typical watchOS application.
The function at the time of clicking the send button 'sendChat' and 'onChatSent' success function after the items as been added to the cart needs to be invoked using Finotes.
The Finotes looks for 'onChatSent' function execution after the execution of 'sendChat', if the 'onChatSent' is not executed within in 10000 milliseconds then a corresponding issue will be raised.

import FinotesWatch

@objc func sendChatClicked(sender: UITapGestureRecognizer) {

    let observer : Observer = Fn.observe()
    observer.nextFunctionSignature(#selector(onChatSent(_:)), in: ViewController.classForCoder()) #selector(sendChat(_:)), withTarget: self, withObserver:observer, 
                            withParameters: chatMessage)

@objc func sendChat(_ message:String) -> Bool {
        return true
    return false

@objc func onChatSent(_ chatId:String) {

1. Here both functions 'sendChat' and 'onChatSent' are invoked using Finotes.
2. Annotate the function with @objc.
3. Using 'nextFunctionSignature' and 'inClass' fields in Observer parameter the functions are chained.
4. Here 'nextFunctionSignature' is the signature of the second function and 'inClass' is the class where the second function is defined.
5. 'expectedChainedExecutionTime' in first function 'sendChat' overrides time needed to execute second function after execution of first function.

Observer fields

You can make use of nextFunctionSignature, inClass, expectedChainedExecutionTime fields in Observer parameter to chain 2 or more functions.


Overrides time needed to execute second function after execution of first function.
By default the time between function executions is set to 2000 milliseconds.


Signature of the second function that is to be chained with the current function.


Class where second function is defined that is to be chained with the current function.

Track Frame rate issues

This feature will be activated only in debug framework.

With a single line, finotes can track frame rate issues that might occur in watchOS application

AppDelegate Class:
import FinotesWatch

func applicationDidFinishLaunching() {
    return true

Setting custom Activity Markers

Activity markers are events that occur in an application during runtime.

You can set custom activity markers in the project using Fn.setActivityMarker(). These markers will be shown along with the activity trail when an issue is reported.

Markers are displayed in their chronological order.
Only when an issue is raised, the activity markers are sent to the server.

Call anywhere is you project:
Fn.setActivityMarker(atTarget: self, forMarker: "completed login")

How activity trail will look like along with reported issue in Finotes dashboard:

ExtensionDelegate:app loaded                            11:19:24:469	45.79% FREE MEMORY 
Controller:user loaded                                  11:19:24:708	44.39%
Controller:user authenticated                           11:19:27:012	45.19%
Controller:clicked on payment_package_two               11:20:24:235	55.20%

Map user or device

Once the user login is complete, you can set any custom user identifier with finotes, This identifier will be tagged with all issue reports raised in Finotes dashboard.
If this API is invoked multiple times, the latest identifier will be used with each issue report.

Anywhere in code:

Issue callback listener (Optional)

You can listen for and access every issue in realtime using the Fn.listenForIssue() API.
You need to add the listener in your ExtensionDelegate class.


func applicationDidFinishLaunching() {

    Fn.listenForIssues(forSelector: #selector(issueReported(_:)), atTarget: self)


@objc func issueReported(_ issue:Issue){


You will be provided with an Issue object that contains all the issue properties that are being synced to the server, making the whole process transparent.
This callback is triggered right after an issue occurrence.

Prevent Crash Reporting

In-order to prevent Finotes from reporting uncaught exceptions that causes app to force close, add key PreventCrashes to your info.plist file.


Whitelisting Crash Reports

Developers can whitelist crash reporting by adding WhitelistCrashes key in info.plist file.
Once whitelisted, only exceptions that are listed in info.plist will be reported by Finotes.


Enabling DryRun mode

During development, you can call the dryRun API, so that the issues raised will not be sent to the server. Every other feature except the issue sync to server will work as same.

func applicationDidFinishLaunching() {

When preparing for production release, you need to remove the DryRun API.

Enable Finotes Logs

Activating verbose will print all logs in LogCat including error and warning logs.

ExtensionDelegate Class:
func applicationDidFinishLaunching() {