Objective-c framework Version: 2.1.0 (Outdated)

To check Latest Version Documentation:

Pre requisites

fi.notes framework supports iOS projects with minimum deployment target version 8 or above.

Integration

You need to add cocoa pods to your project. You can find more information here.
After integrating cocoa pods, add FinotesCore to your Podfile.

pod 'FinotesCore', '2.1.0'

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
The –repo-update option should be used the first time pod install is run from terminal.

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

#import <FinotesCore/Fn.h>

Initialize

You need to call the [Fn initialize:application] function in your appDelegate didFinishLaunchingWithOptions:

#import <FinotesCore/Fn.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application];
}

DryRun

During development, you can set the dryRun mode, 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.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application withDryRun:YES withVerbose:YES];
}

VerboseLog

There are two variations of logging available in fi.notes, Verbose and Error. You can toggle them using corresponding APIs.
Activating verbose will print all logs in LogCat including error and warning logs.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application withDryRun:YES withVerbose:YES];
}
ErrorLog

If only error and warning logs needs to be printed,

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application withDryRun:NO withVerbose:NO];
    [Fn logError:YES];
}

Test

Now that the basic integration of fi.notes framework is complete, Lets make sure that the dashboard and framework are in sync.

#import <FinotesCore/Fn.h>
#import <FinotesCore/Severity.h>


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application withDryRun:NO withVerbose:YES];
    //[Fn reportIssueAt:] allows you to raise custom issues.
    //Refer Custom Issue section by the end of this documentation for more details.
    [Fn reportIssueAt:self withDescription:@"iOS TEST Issue" withSeverity:MINOR];
}

Now run the application in a simulator or real iOS device (with network connection).
Once the application opens up, open fi.notes dash.
The issue that we raised should be reported.
In-case the issue is not listed, make sure the right app is selected at the top of the dashboard.
Also, do refer to the logs in console, it will list any errors/warnings that might have occured during the integration.

If the error still persists, do contact us at fi.notes contact email or you may use the chat support at the bottom right corner.

You should remove the [Fn reportIssueAt:] call, else every time the app is run, an issue will be reported.

#import <FinotesCore/Fn.h>
#import <FinotesCore/Severity.h>


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application withDryRun:NO withVerbose:YES];	
//  [Fn reportIssueAt:self withDescription:@"iOS TEST Issue" withSeverity:MINOR];
}

Listen for Issue

You can listen for and access every issue in realtime using the [Fn listenForIssues:] API.
You need to add the listener in your AppDelegate file.


    [Fn listenForIssues:@selector(issueFound:) atTarget:self];
}

(void) issueFound:(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.
As this callback will be made right after an issue occurrence, you will be able to provide a positive message to the user.

Reporting Issues

Report Network Call Failure

Issues like status code errors, timeout issues and other failures will be reported for all network calls by fi.notes, from your application.

If you are using NSURLSession with sharedSession then issues in all REST api calls will be reported automatically.

	NSURLSession *session = [NSURLSession sharedSession];
AFNetworking

Incase you are using AFNetworking, then you need to add the below code in your NSURLSessionConfiguration. The part we are interested is

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration 
    						defaultSessionConfiguration];
    configuration.protocolClasses = [Fn getProtocols:[configuration.protocolClasses mutableCopy] ];
AFHTTPRequestOperationManager

If you try to use ‘AFHTTPRequestOperationManager’, your app might crash.
‘AFHTTPRequestOperationManager’ is outdated in AFNetworking 3.x, we suggest you migrate to ‘NSURLSessionConfiguration’.
You can find out, how to migrate to ‘NSURLSessionConfiguration’ here.

Full code can be found here,

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration 
    						defaultSessionConfiguration];
    configuration.protocolClasses = [Fn getProtocols:[configuration.protocolClasses mutableCopy] ];
    AFHTTPSessionManager * myManager = [[AFHTTPSessionManager alloc] 
    						initWithSessionConfiguration:configuration];
    NSURL *URL = [NSURL URLWithString:@"https://app.finotes.com/login"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionDataTask *dataTask = [myManager dataTaskWithRequest:request 
    			completionHandler:^(NSURLResponse *response, 
						id responseObject, NSError *error) {
        if (!error) {
        }
    }];
    [dataTask resume];

Whitelist Hosts

Add ObservableDomains array to the project info.plist with the list of domains to be observed, if needed, you may provide a timeout for the api calls along with the host names separated by a coma.

Info.plist observable domains

Incase you are directly editing the info.plist as source-code

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>ObservableDomains</key>
	<array>
		<string>host.com,5000</string>
		<string>another.host.com</string>
	</array>

Optionally, You may provide a custom timeout for hostnames. This will raise an Issue for any network call to the hosts, that takes more than the specified time for completion.
Do note that specifying timeout will not interfere in any way with your network calls.

Categorize Tickets (Optional)

You will be able to configure how API issues are categorized into tickets using a custom header “X-URLID”. You can set this header in your API calls and when they are reported to fi.notes dashboard, issues from API calls with same “X-URLID” will be categorized into a single ticket.

    [nsMutableURLRequest setValue:@"loginapi" forHTTPHeaderField:@"X-URLID"];

Any issue in API calls with same X-URLID will be shown as a single ticket in fi.notes dashboard.

Controller trail

When an issue is raised, inorder to get user screen flow for the current session, you need to extend your Controller from ObservableViewController.

@interface ViewController : UIViewController

@end

Changes to,

@interface ViewController : ObservableViewController

@end
Low Memory Reporting

Extending Controller from ObservableViewController will also allow fi.notes to report any memory related issues that may occur in your application.

You may use App functions to report different app states along with Controller trail.

#import <FinotesCore/App.h>

@interface AppDelegate ()

@end

@implementation AppDelegate

	- (void)applicationWillResignActive:(UIApplication *)application {
	    [App applicationWillResignActive:self];
	}

	- (void)applicationDidEnterBackground:(UIApplication *)application {
	    [App applicationDidEnterBackground:self];
	}

	- (void)applicationWillEnterForeground:(UIApplication *)application {
	    [App applicationWillEnterForeground:self];
	}

	- (void)applicationDidBecomeActive:(UIApplication *)application {
	    [App applicationDidBecomeActive:self];
	}

	- (void)applicationWillTerminate:(UIApplication *)application {
	    [App applicationWillTerminate:self];
	}
@end

Once Controllers are extended from ObservableViewController and App functions are implemented, if an issue is raised, on fi.notes dashboard, you will be able to view screen activity for 3 minutes prior to the issue was raised.

Activity Trail
	LoginController:viewDidLoad                  09:21:53:923
	LoginController:viewWillAppear               09:21:53:923
	LoginController:viewDidAppear                09:21:53:927
	UIApplication:applicationDidBecomeActive     09:21:54:081
	LoginController:viewDidLoad                  09:22:09:670

Function call

fi.notes will report any return value issues, exceptions and execution delays that may arise in functions using [Fn call:].
A regular function call will be,


    [self getUserNameFromDb:@"123-sd-12"]
}

-(NSString *) getUserNameFromDb:(NSString *) userId {
    NSString *userName = [[User findById:userId] name];
    return userName;
}

Function “[self getUserNameFromDb:]” call needs to be changed to,

#import <FinotesCore/Observer.h>
#import <FinotesCore/Fn.h>


    [Fn call:@selector(getUserNameFromDb:) target:self withParameters:@"123-sd-12"];
}

-(NSString *) getUserNameFromDb:(NSString *) userId {
    NSString *userName = [[User findById:userId] name];
    return userName;
}

This will allow fi.notes to raise issue incase the function take more than normal time to execute, or if the function return a nil value, or throws an exception.

You can control all the above said parameters in Observe object.

expectedExecutionTime
#import <FinotesCore/Observer.h>
#import <FinotesCore/Fn.h>



    Observer *observer =  [[Fn observe] expectedExecutionTime:1400];
    NSString *userName = [Fn call:@selector(getUserNameFromDb:) target:self 
    					observer:observer withParameters:@"123-sd-12"];
}

-(NSString *) getUserNameFromDb:(NSString *) userId {
    NSString *userName = [[User findById:userId] name];
    return userName;
}
Returns nil
Exception in function

Here the expectedExecutionTime for the function “[self getUserNameFromDb:]” has been overriden to 1400 milliseconds (default was 1000 milliseconds). If the database query takes more than 1400 milliseconds to return the value or if the returned “userName” is nil, then corresponding issues will be raised.
An issue will be raised when exception occurs inside a function that is called using [Fn call:].

Function in Separate Class file

If the function is defined in a separate file and needs to be invoked, then instead of passing “self” you can pass the corresponding object in [Fn call:]

#import <FinotesCore/Observer.h>
#import <FinotesCore/Fn.h>


    DBUtils *dbUtils = [[DBUtils alloc] init];

    Observer *observer =  [[Fn observe] expectedExecutionTime:1400];
    NSString *userName = [Fn call:@selector(getUserNameFromDb:) target:dbUtils 
    					observer:observer withParameters:@"123-sd-12"];
}





@interface DBUtils ()

@end

@implementation DBUtils

-(NSString *) getUserNameFromDb:(NSString *) userId {
    NSString *userName = [[User findById:userId] name];
    return userName;
}

@end

Static Function calls

For static functions, pass corresponding class instead of object in [Fn call:] to invoke static method.

#import <FinotesCore/Observer.h>
#import <FinotesCore/Fn.h>


    Observer *observer =  [[Fn observe] expectedExecutionTime:2000];
    long userTimestamp = [[Fn call:@selector(getUserTimestampFromJSON:) 
    					target:[DBUtils class] observer:observer 
					withParameters:httpResponse] longValue];
}





@interface DBUtils ()

@end

@implementation DBUtils

+(long) getUserTimestampFromJSON:(NSDictionary *) response {
    long userTimestamp = [self processJSONAndFindUserTimestamp:response];
    return userTimestamp;
}


-(NSString *) getUserNameFromDb:(NSString *) userId {
    NSString *userName = [[User findById:userId] name];
    return userName;
}

@end

Chained Function calls

You can connect multiple functions using ‘nextFunctionSignature’ and ‘inClass’ properties in Observe object.

#import <FinotesCore/Observer.h>
#import <FinotesCore/Fn.h>


   - (void) sendChatClicked:(UITapGestureRecognizer *)recognizer {

       // Here function 'onChatSent:' is 
       // expected to be called in under '2000' milliseconds.
       Observer *observer =  [[[Fn observe] expectedChainedExecutionTime:2000]  
				nextFunctionSignature:
					@selector(onChatSent:) 
				inClass:[self class]];
       [Fn call:@selector(sendChat:) target:self observer:observer withParameters:chatMessage];
   }

   - (Boolean) sendChat:(NSString *) message {
   	if([self isValid:message]){
	     [self syncMessage:message];
	}
	return false;
   }

   - (void) onChatSent:(NSString *)chatMessageId {
   	[self chatSyncConfirmed:chatMessageId];
   }

Here ‘onChatSent:’ should be called within 2000 milliseconds after execution of ‘sendChat:’. If the function ‘onChatSent:’ is not called or is delayed then corresponding issues will be raised and reported.

boolean returns false

Here in ‘sendChat:’ function, if it returns false, an issue will be raised with the function parameters, which will help you dig more into what could have gone wrong.

Test

In-order to check if you have implemented function chaining correctly, set break points inside the chained functions then run application in your simulator or device, and use your application, if you have implemented correctly, You will hit these breakpoints correctly.

Catch Global Exceptions.

In-order to catch uncaught exceptions, You may use [Fn catchExceptions].

#import <FinotesCore/Fn.h>


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [Fn initialize:application];
    [Fn catchExceptions];
}

Custom Exceptions

You can report custom exceptions using the [Fn reportExceptionAt:] API.

@try{
    [self fetchUserDetails];
}@catch(NSException *exception){
    [Fn reportExceptionAt:self withException:exception 
    					withSeverity:FATAL];
}

Custom Issue

You can report custom issues using the [Fn reportIssueAt:] API.


//Payment gateway delegate methods.
-(void) paymentCompleted:(NSString *) userIdentifier forType:(NSInteger) type{

}

-(void) paymentFailed:(NSString *) userIdentifier forReason:(NSString *) reason{
    [Fn reportIssueAt:self withDescription:
    			[NSString stringWithFormat:@"Payment failed for %@", reason] 
			withSeverity:FATAL];
}