Permutive Developer Hub

Welcome to the Permutive developer hub. You'll find comprehensive guides and documentation to help you start working with Permutive as quickly as possible, as well as support if you get stuck. Let's jump right in!

The Permutive iOS SDK (beta) has full support for Permutive's core functionality: event tracking, segment retrieval and identity management.

The SDK is accessible as a framework, downloadable from our website. If you prefer the traditional CocoaPods and static libraries delivery mechanism, this is fully supported as well.

In future, we plan to release kit libraries in addition to our core SDK, for enabling feature-rich integrations with third-party SDKs. This will enable you to more easily plug-in to other products, with minimal effort for developers.

Installing from CocoaPods

To install the SDK using CocoaPods, just add this line to your Podfile:
pod 'PermutiveSDK', '~> 0.17'

Initialise The SDK

The SDK requires that you provide the PermutiveOptions object. The options object is created with the Project ID and API Key - which are available from the Permutive dashboard. No other functionality of the SDK is available until these are passed in.

This call should only be made once in the entire code. We suggest putting this alongside other initialisations - in the application:didFinishLaunchingWithOptions:.

Here is an example of the configuration call:

import Permutive

if let projectId = UUID(uuidString: "becfaab8-d61e-4b64-99fd-d519baf368b3"),
    let apiKey = UUID(uuidString: "fff4fade-30ec-ad70-1686-6f7a27fa9cd9"),
    let options = PermutiveOptions.init(projectId: projectId, apiKey: apiKey) {
    
    // optional user identity
    options.userIdentity = "mark@facebook.com"
    
    Permutive.configure(with: options)
} else {
    print("configuration is incorrect")
}
// If modules are turned on:
@import Permutive;

// OR:
#import <Permutive/Permutive.h>

NSUUID *projectId = [[NSUUID alloc] initWithUUIDString:@"8e4deed5-bead-4a78-9063-73148668cde6"];
NSUUID *apiKey = [[NSUUID alloc] initWithUUIDString:@"45a03370-b962-4bab-cdef-067446d524ba"];
PermutiveOptions *options = [PermutiveOptions optionsWithProjectId:projectId apiKey:apiKey];

// optional: set identity at this point
options.userIdentity = @"mark@facebook.com";

if (options != nil) {
    [Permutive configureWithOptions:options];
} else {
    NSLog(@"configuration went wrong");
}

Identity Management

You can set a custom identity for the user, whenever it's available. This may be appropriate when users log in to your app, and a custom identity such as an internal identifier or email address may be available. Identifying a user with a custom identity is useful if you want to identify and synchronise users across platforms, for example across iOS and web.

You do not need to set a custom identity in order to track the user across iOS applications. By default, the IDFA will be used to identify the user if the IDFA is enabled and available on the device.

You can obtain the user identifier and is guaranteed to never be nil. However you need to remember that the userIdentifier changes every time that the user is setting a new identity.

// User ID
let userId = Permutive.userId()
print(userId)
// "1tcf4ab8-d51d-ab64-a9fd-cc19baf365c3"

// change Identity
Permutive.setIdentity("mark@facebook.com")

// User ID
Permutive.userId()
print(userId)
// "fgt4fa11-d516-5b64-30ec-0f7a27fa9cft0"
// User ID
NSString *userId = [Permutive userId];
NSLog(@"%@", )
// "1tcf4ab8-d51d-ab64-a9fd-cc19baf365c3"

// change Identity
[Permutive setIdentity:@"mark@facebook.com"];

// User ID
NSString *userId = [Permutive userId];
NSLog(@"%@", )
// "fgt4fa11-d516-5b64-30ec-0f7a27fa9cft0"

Coding Patterns

The Permutive SDK API uses flexible and lightweight objects to allow for flexible event tracking, querying and update subscription. Once the SDK has been initialised with the Project and API Keys, the Permutive.permutive() services provider interface will provide a number of different objects. Feel free to request as many of these instances as necessary. These are not singletons and so they are thread safe.

Objects whose methods take a block will retain the blocks for their lifespan. Our SDK does not use Delegation or Singleton patterns - aside from the initial configuration. We leave the choice of calling our API from any object or thread in your app or just embedding it in one abstraction layer to the developer.

The [Permutive permutive], or Permutive.permutive() call will return NULL/nil if no API Key or Project ID has been provided.

Event Tracking

Events represent any activity in your application worth tracking. Examples include entering and leaving a view, engagement changes (e.g. scrolling inside an article) and any other user or environment driven inputs.

To track an event, grab an instance of the eventTracker object and call the track method. Event tracking can include properties, passed in as a dictionary. Please note, that event names and properties should only contain characters in [a-zA-Z0-9_]. Any character outside of the allowed character set will be removed before the event is tracked.

As with other Permutive SDK APIs, the event tracker object is lightweight and multiple instances of it can be created, as needed. Event tracking is thread-safe, so feel free to track event from any thread. It is also asynchronous, and will not block.

Permutive.permutive()?.eventTracker.track("AppLaunched",
    properties: ["firstTime": true]
)

// Or one with no Properties
Permutive.permutive()?.eventTracker.track("AppStarted")
[[[Permutive permutive] eventTracker] track:@"AppLaunched" properties:@{
  @"firstTime": @(YES)
 }];

// Or one with no Properties
[[[Permutive permutive] eventTracker] track:@"AppStarted"];
 

Adding Context

Events can contain contextual information, similarly to web events. Event context can be set on the Permutive object. The context object passed in is mutable and you can set values for url, title and referrer. The domain is automatically inferred from url, if it has been set. Subsequent calls to track will contain the extra context as part of the event properties, within a client object.

Here's an example:

let eventAContext = PermutiveEventActionContext()

eventAContext.url = URL(string: "https://developer.permutive.com/page/the-permutive-ios-sdk")
eventAContext.title = "Main"
eventAcontext.referrer = URL(string: "http://www.permutive.com/android/tutorials?referrer=johnDoe")
let p = Permutive.permutive()
p?.contex = eventAContext

let eventTracker = p?.eventTracker()

//contains url/title/referrer & domain context implicitly
eventTracker?.track("sdk link")
PermutiveEventActionContext *eventAContext = [[PermutiveEventActionContext alloc] init];
eventAContext.url = [NSURL URLWithString: @"https://developer.permutive.com/page/the-permutive-ios-sdk"];
eventAContext.title = @"Main";
eventAContext.referrer = [NSURL URLWithString: @"http://www.permutive.com/android/tutorials?referrer=johnDoe"];
[Permutive permutive].context = eventAContext;

id<PermutiveEventActionInterface> eventAction = [[Permutive permutive] eventTracker];

//contains url/title/referrer & domain context implicitly
[eventAction track:@"event"];

Event Enrichment

You are able to use event enrichment in iOS in the same way as Web. Enrichment implementations are in place for picking up information about the user's geographical location and ISP information. Currently supported property values for enrichment are PermutiveEventPropertyValueConsts.geo_info and PermutiveEventPropertyValueConsts.isp_info. For example:

if let p = Permitive.permutive() {
   p.eventTracker.track("ScreenView",
       properties: ["geo_info": PermutiveEventPropertyValueConsts.geo_info, 
                    "isp_info": PermutiveEventPropertyValueConsts.isp_info]
   )
}
id<PermutiveEventActionInterface> eventTracker = [[Permutive permutive] eventTracker];
if (eventTracker != nil) {
    [[[Permutive permutive] eventTracker] track:@"ScreenView" properties:@{
      @"geo_info": PermutiveEventPropertyValueConsts.geo_info, 
      @"isp_info": PermutiveEventPropertyValueConsts.isp_info
     }];
}

If Permutive is able to detect geographical and ISP information for the user, the geo_info and isp_info properties will be replaced with a JSON object, resulting in an event with the following structure:

{
  "geo_info": {
    "city": "Thundersley",
    "country": "United Kingdom",
    "continent": "Europe",
    "province": "Essex",
    "postal_code": "SS7"
  },
  "isp_info": {
    "isp": "Sky Broadband",
    "organization": "Sky Broadband",
    "autonomous_system_number": 5607,
    "autonomous_system_organization": "Sky UK Limited"
  }
}

If enrichment wasn't possible for any property, the corresponding properties will be excluded from the event.

Segment Query

The developer can query the list of all segments current user belongs to, using the API calls below. The segment list is returned as NSNumber array. These correspond to the segment IDs shown in the Permutive dashboard.

let segments: [Number]? = Permutive.permutive()?.triggersProvider.querySegments
 
// or to avoid optional

let segments: [Number] = Permutive.permutive()?.triggersProvider.querySegments ?? []
NSArray<NSNumber *> *segments = [[[Permutive permutive] triggersProvider] querySegments];

To track what segments the user belongs to, you can use the API call below. The callback will be called straight away with the current segments the user is in, and then called each time the segment list changes. Segments are returned as a list of Integers. These correspond to segment IDs setup in the Permutive Dashboard. The returned list is never nil.

let allSegmentsAction = Permutive.permutive()?.triggerActionForAllSegments { (segment, result) in
            	 	        	print("segment \(segment) current value \(result)") }
              
              
// retain triggerAction for as long as it is needed
// ...
allSegmentsAction = nil
PermutiveTriggerAction *triggerAction = [[Permutive permutive] triggerActionForAllSegmentsWithCallback:^(NSNumber *segmentID, BOOL value) {
  									NSLog(@"segment %d changed: %@", segment, value); }];
       
// retain triggerAction for as long as it is needed
// ...
triggerAction = nil;

Tracking Segment Changes

To track real time changes in segment states, you can use triggers. Trigger actions are lightweight objects set up to track individual changes in segments. Each action object is responsible for a callback block, which will be executed whenever a change in one of the desired segments has occurred. To stop tracking the segment, just​ release the trigger action object.

Please be aware that the block will be called from a different thread than the one setting it up.

If a segment Id does not exist, the trigger will still be created, but the callback will never be called. If, however, the segment does exist at a later point (the SDK may fetch a fresher version of the queries), then the callback will be called when a value is present, and then for each time the value changes.


let segmentA = NSNumber(value: 1048)
let segmentAction = Permutive.permutive()?.triggerAction(forSegment: segmentA) { (result) in
            	 	print("value changed: \(result)")}

// retain triggerAction for as long as it is needed
// ...
segmentAction = nil


let segmentB = NSNumber(value: 1067)
let segmentsAction = Permutive.permutive()?.triggerAction(forSegments: [segmentA, segmentB]) { (segment, result) in
            	 	print("segment \(segment) value changed: \(result)")}

// retain triggerAction for as long as it is needed
// ...
segmentsAction = nil



NSNumber *segmentA = [NSNumber numberWithInteger:1048];
PermutiveTriggerAction * segmentAction = [[Permutive permutive] triggerActionForSegment:segmentA callback:^(BOOL value) {
  NSLog(@"value: %d", value);}];
                                        
// retain triggerAction for as long as it is needed
// ...
segmentAction = nil;
                                          
NSNumber *segmentB = [NSNumber numberWithInteger:1067];
PermutiveTriggerAction * segmentsAction = [[Permutive permutive] triggerActionForSegments:@[segmentA, segmentB] callback:^(NSNumber *segment, BOOL value) {
   NSLog(@"segment: %@, new value: %d", segment, value); }];
                                        
// retain triggerAction for as long as it is needed
// ...
segmentsAction = nil;

Tracking Query Changes

To track real time changes in query states, you can use triggers.
Please be aware that the block will be called from a different thread than the one setting it up.

Queries can be of a simple type - Boolean, String, Integer, Double or a more complex type: Dictionary<String, Any> (where Any is of any of the simple types, or another Dictionary of type <String, Any>).

If a query Id does not exist, the trigger will still be created, but the callback will never be called. If, however, the query Id does exist at a later point (the SDK may fetch a fresher version of the queries), then the callback will be called when a value is present, and then for each time the value changes.

Using a wrong type for a given query Id will immediately log an error to the console. The callback will never be called.

// Query of complex type
let actionComplexQuery = Permutive.permutive()?.triggerAction(forMapQueryIDs: [NSNumber(value: 10837), NSNumber(value: 10838)]) { (segment, result) in
           	print("segment \(segment) value changed: \(result)") }
        
// retain triggerAction for as long as it is needed
// ...
actionComplexQuery = nil


// Query of integer type 
let actionIntegerQuery = Permutive.permutive()?.triggerAction(forIntegerQueryID: NSNumber(value: 10838)) { (result) in
           print("value changed: \(result)") }
// retain triggerAction for as long as it is needed
// ...
actionIntegerQuery = nil
// Query of complex type

NSNumber *queryA = [NSNumber numberWithInteger:1048];
NSNumber *queryB = [NSNumber numberWithInteger:1067];
PermutiveTriggerAction *actionComplexQuery = [[Permutive permutive] triggerActionForMapQueryIDs: @[queryA, queryB] callback:^(NSNumber *queryId, NSDictionary<NSString *, id> *value) { 
   	NSLog(@"segment: %@, new value: %d", queryId, value); }];
        
// retain triggerAction for as long as it is needed
// ...
actionComplexQuery = nil;
                   
                                              
// Query of integer type 
NSNumber *query = [NSNumber numberWithInteger:1018];
PermutiveTriggerAction *actionIntegerQuery = [[Permutive permutive] triggerActionForIntegerQueryID: query callback:^(NSInteger value) { 
   	NSLog(@"new value: %d", value); }];
        
// retain triggerAction for as long as it is needed
// ...
actionIntegerQuery = nil;

Google AdWords DFP Custom Targets

Our iOS SDK will return a dictionary of targeting data, which can be fed directly into your DFPRequest object instances. Using the triggers provider, simply get the required dictionary from the dfpRequestCustomTargeting property. This dictionary contains Permutive targeting data for the user. If you are already setting customTargeting property on your DFPRequests, you will need to merge the two dictionaries.

let request = DFPRequest()

if let segments = Permutive.permutive()?.triggersProvider.dfpRequestCustomTargeting {
    request.customTargeting = segments
}

self.bannerView.load(request)
NSDictionary<NSString *, NSArray<NSNumber *> *> *segments = [[[Permutive permutive] triggersProvider] dfpRequestCustomTargeting];

DFPRequest *request = [DFPRequest request];
request.customTargeting = segments

[self.bannerView loadRequest:request];