Difference between revisions of "Czech Digital Measurement iOS Simplified SDK"

From Engineering Client Portal

Line 1: Line 1:
 
{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|International}}  {{CurrentBreadcrumb}}
 
{{Breadcrumb|}} {{Breadcrumb|Digital}} {{Breadcrumb|International}}  {{CurrentBreadcrumb}}
 
[[Category:Digital]]
 
[[Category:Digital]]
 +
 
== Overview ==
 
== Overview ==
 
The Nielsen SDK is one of multiple framework SDKs that Nielsen provides to enable measuring linear (live) and on-demand TV viewing using TVs, mobile devices, etc.
 
The Nielsen SDK is one of multiple framework SDKs that Nielsen provides to enable measuring linear (live) and on-demand TV viewing using TVs, mobile devices, etc.
Line 6: Line 7:
 
*Application launch events and how long app was running
 
*Application launch events and how long app was running
 
*Time of viewing a sub section / page in the application.
 
*Time of viewing a sub section / page in the application.
 +
 
== Prerequisites ==
 
== Prerequisites ==
To start using the Simplified SDK for Browser, you will need an '''Appid.''' This is a Unique ID assigned to the player/site and configured by product.
+
To start using the App SDK, the following details are required:
 +
* '''App ID (appid):''' Unique ID assigned to the player/site and configured by product.
 
If you do not have any of these prerequisites or if you have any questions, please see [https://engineeringportal.nielsen.com/docs/Czech_Contacts Contact list for Czech Republic].
 
If you do not have any of these prerequisites or if you have any questions, please see [https://engineeringportal.nielsen.com/docs/Czech_Contacts Contact list for Czech Republic].
  
Line 13: Line 16:
 
As part of making the SDK more user friendly and reduce the number of app integration touch points, Nielsen has designed a simple interface to pass metadata to the sdk while reducing the number of API calls.  The new <code> trackevent() </code> API has been implemented as a wrapper for the existing SDK and will be responsible for handling new API calls, performing validation, and translation of new API calls to the existing Nielsen App SDK API methods.  Applications which are already integrated with the existing SDK API, are unaffected by this new API.
 
As part of making the SDK more user friendly and reduce the number of app integration touch points, Nielsen has designed a simple interface to pass metadata to the sdk while reducing the number of API calls.  The new <code> trackevent() </code> API has been implemented as a wrapper for the existing SDK and will be responsible for handling new API calls, performing validation, and translation of new API calls to the existing Nielsen App SDK API methods.  Applications which are already integrated with the existing SDK API, are unaffected by this new API.
 
[[File:SimplifiedAPI_vs_StandardAPI_New.jpg|2048px|link=http://dayrhesdkp001z.enterprisenet.org/w/images/9/91/SimplifiedAPI_vs_StandardAPI_New.jpg]]
 
[[File:SimplifiedAPI_vs_StandardAPI_New.jpg|2048px|link=http://dayrhesdkp001z.enterprisenet.org/w/images/9/91/SimplifiedAPI_vs_StandardAPI_New.jpg]]
[http://dayrhesdkp001z.enterprisenet.org/w/images/9/91/SimplifiedAPI_vs_StandardAPI_New.jpg Click here to zoom in on image]
 
== Manages the order of metadata (ad vs content and playheads) ==
 
 
Existing API has a number of methods used for reporting player and application state changes to the SDK. Order of calls is important for the SDK in the existing API. In the new enhanced API all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the existing API in separate calls will be provided in one single call. SDK will analyse the data received in the dictionary object, compare it with the data received previously and generate a sequence of calls for the existing API.
 
Existing API has a number of methods used for reporting player and application state changes to the SDK. Order of calls is important for the SDK in the existing API. In the new enhanced API all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the existing API in separate calls will be provided in one single call. SDK will analyse the data received in the dictionary object, compare it with the data received previously and generate a sequence of calls for the existing API.
== Implementation==
+
 
This guide covers implementation steps in JavaScript.
+
[[File:Co-Existance.jpg|center|700px]]
 +
For iOS SDK framework package will contain 2 public header files. One header file will contain old SDK interface and will be used by existing clients (NielsenAppApi.h). New API will be defined in a new public header file (NielsenEventTracker.h).
 +
 
 +
== Implementation ==
 +
This guide covers implementation steps for iOS using Xcode.
 +
<br />
  
 
__TOC__
 
__TOC__
  
== Step 1 : Configure SDK ==
+
== Step 1 : Get SDK ==
Add the following script tag (to Static Queue Snippet) the website:
+
<br>
<syntaxhighlight lang="javascript">
+
For AppSDK versions, release dates and release notes - refer to [https://engineeringportal.nielsen.com/docs/iOS_SDK_Release_Notes iOS AppSDK release notes]. <br>
<script type = "text/javascript" >
+
Always keep latest and greatest AppSDK version inside your app.<br>
  /***************** Static Queue Snippet *********************/
+
 
  ! function(t, n) {
+
The Nielsen AppSDK can either be
    t[n] = t[n] || {
+
* integrated directly within an application through the use of a CocoaPod (iOS) or Gradle (Android) - select to read [https://engineeringportal.nielsen.com/docs/Digital_Measurement_iOS_Artifactory_Guide CocoaPod implementation guide] - '''Recommended solution''' that will keep your SDK copy latest by each build. Once finished continue to Step 3.
      nlsQ: function(e, c, o, r, s, i) {
+
* or downloaded as ZIP package at [https://engineeringportal.nielsen.com/docs/Special:Downloads Nielsen Downloads]. Package contains NielsenAppApi.framework (Static and Dynamic versions) as well as sample applications. You will have to update SDK manually always when new version of AppSDK is released. Once finished continue to Step 2.
        return s = t.document,
+
 
          r = s.createElement("script"),
+
== Step 2 : Setting up your xCode Development Environment  ==
          r.async = 1,
+
<br>
          r.src = ("http:" === t.location.protocol ? "http:" : "https:") + "//cdn-gl.imrworldwide.com/conf/" + e + ".js#name=" + c + "&ns=" + n, i = s.getElementsByTagName("script")[0], i.parentNode.insertBefore(r, i),
+
Applicable only if using downloaded Framework package
          t[n][c] = t[n][c] || {
+
<br><br>
            g: o || {},
+
'''From AppSDK version 6.2 (5.2.2018) - Dynamic Framework'''<br>
            ggPM: function(e, o, r, s, i) {
+
Prior to SDK Version 6.2.0.0 the iOS framework has been distributed as a static library packaged into framework bundle format. Apple recommends to use dynamic framework, it has some benefits over static libraries like less executable file size of an app, faster startup time and native support in xCode IDE. Nielsen AppSDK has been transformed into dynamic framework in this release.<br>
              (t[n][c].q = t[n][c].q || []).push([e, o, r, s, i])
+
If migrating from the static library to this new dynamic framework, once implemented, unless your specific application requires, you can remove the following Frameworks that were once required: [AdSupport, JavascriptCore, SystemConfiguration, Security, AVFoundation, libc++].
            },
+
<br><br>
            trackEvent: function(e) {
+
Proceed : <br>
              (t[n][c].te = t[n][c].te || []).push(e)
+
1) From Nielsen SDK package - inside DynamicFramework dir - extract “NielsenAppApi.Framework” and copy it to Frameworks folder of the Xcode project.<br>
            }
+
2) Add Framework - In the General tab for app configuration add NielsenAppApi.framework in the list of Embedded Binaries.<br>
          }, t[n][c]
+
3) Add Path - Add path to the NielsenAppApi.framework in the Framework Search Paths build setting.
      }
+
<br><br>
    }
+
 
  }(window, "NOLBUNDLE");
+
'''Before AppSDK version 6.2 (5.2.2018) - Static Framework - Depreciated'''<br>
</script>
+
From Nielsen SDK package - inside StaticFramework dir - extract “NielsenAppApi.Framework”. For futher steps refer to Step 1: Setting up your Development Environment inside [https://engineeringportal.nielsen.com/docs/DCR_Czech_Video_App_SDK DCR Czech Video App SDK (standard API)].
</syntaxhighlight>
 
  
The static queue snippet allows the SDK APIs to be called while the actual SDK and configuration file are still being downloaded. As the queue can capture all API calls before the download completes, there is no wait time. Once the SDK is available, the API calls will transition from directing to the queue to the SDK seamlessly.
+
Note:
 +
* Nielsen App SDK is compatible with Apple iOS versions 9.0 and above.
 +
* The SDK uses the NSURLSession instead of the deprecated NSURLConnection.
 +
* All communications between the SDK and the Census (Collection Facility) use HTTPS
  
==Step 2 : Create SDK Instance==
+
== Step 3: Import SDK ==
To initialize the SDK, create an SDK instance by making the initialization call:
+
<br>
 +
Add SDK import
  
<syntaxhighlight lang="javascript">
+
a) Using Objective-C :<br>
NOLBUNDLE.nlsQ("<apid>", "<instanceName>",{nol_sdkDebug: "debug"})
+
Add the code to the View Controller’s header file
 +
<syntaxhighlight lang="java">
 +
#import <NielsenAppApi/NielsenAppApi.h>
 +
</syntaxhighlight> 
 +
<br>
 +
b) Using Swift :<br>
 +
Add the code to the ViewController.swift
 +
<syntaxhighlight lang="java">
 +
import NielsenAppApi
 +
</syntaxhighlight> 
 +
<br>
 +
Alternatively using Objective-C bridging header : to import a set of Objective-C files in the same app target as your Swift code, you rely on an Objective-C bridging header to expose those files to Swift. Xcode offers to create this header file when you add a Swift file to an existing Objective-C app, or an Objective-C file to an existing Swift app.<br>
 +
Select File/New File/Objective-C File, Xcode will prompt you to create a bridging header.<br>
 +
Once this bridging header file has been created, you need to add the following:
 +
<syntaxhighlight lang="java">
 +
#import <NielsenAppApi/NielsenAppApi.h>
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
== Step 4 : SDK Initialization ==
 
<br>
 
<br>
When creating an instance, pass the following values: (<code>nol_sdkDebug</code> and <code>optout</code> are optional)
+
The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object, which can be used simultaneously without any issue. A maximum of four SDK instances per appid are supported. When a fifth SDK instance is launched, the SDK will return “nil” from [[initWithAppInfo:delegate:]]
 +
 
 +
The following table contains the list of arguments that should be passed during initialization.
 +
 
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
! Parameter !! Description !! Values
+
! Parameter / Argument !! Description !! Source !! Required? !! Example
 
|-
 
|-
| apid || Unique ID assigned to player/site || 'PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
+
| appid || Unique id for the application assigned by Nielsen. It is GUID data type
 +
|| Nielsen-specified || Yes || PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 
|-
 
|-
|instanceName || Name of SDK instance || "any string value"
+
| appname || Name of the application || Client-defined || No|| "Nielsen Sample App"
 
|-
 
|-
| nol_sdkDebug || Enables Nielsen console logging. Only required for testing || "{nol_sdkDebug: "debug"}"
+
| appversion || Current version of the app used || Client-defined || No || "1.0.2"
 +
|-
 +
| sfcode || Nielsen collection facility to which the SDK should connect.
 +
|| Nielsen-specified || Yes || "cz"
 +
|-
 +
|nol_devDebug || Enables Nielsen console logging. Only required for testing ||Nielsen-specified || Optional || "DEBUG"
 +
|}
 +
<br />
  
|}
+
=== Sample SDK Initialization Code  - Swift ===
 +
<br>
 +
<code>NielsenInit.swift</code>
 +
<syntaxhighlight lang="swift">
 +
 
 +
import Foundation
 +
import NielsenAppApi
 +
 
 +
class NielsenInit : NSObject {
 +
    class func createEventTracker(delegate: NielsenEventTrackerDelegate) -> NielsenEventTracker?{
 +
   
 +
        //Initialising the NielsenEventTracker class by passing app information which returns the instance of NielsenEventTracker.
 +
       
 +
        var nielsenEventTracker: NielsenEventTracker?
 +
       
 +
        let appInformation = [ 
 +
            "appid": "PDA7D5EE6-B1B8-4123-9277-2A788XXXXXXX",
 +
            "appversion": "1.0",
 +
            "appname": "Amazing app",
 +
            "sfcode": "cz",
 +
            "nol_devDebug": "DEBUG"
 +
            ]
 +
       
 +
        nielsenEventTracker = NielsenEventTracker(appInfo:appInformation1, delegate:delegate)
 +
        return nielsenEventTracker
 +
    }
 +
}
 +
</syntaxhighlight>
 +
 
 +
<br />
 +
<code>ViewController.swift</code>
 +
 
 +
<syntaxhighlight lang="swift">
 +
 
 +
    override func viewDidLoad() {
 +
        super.viewDidLoad()
 +
       
 +
        //Getting the instance of NielsenEventTracker
 +
        self.nielsenEventTracker = NielsenInit.createEventTracker(delegate: self)
 +
}
 +
 
 +
</syntaxhighlight>
 +
 
 +
<br>
 +
=== Sample SDK Initialization Code  - Objective C ===
 +
<br>
 +
Initialize the Nielsen App object within the viewDidLoad view controller delegate method using initWithAppInfo:delegate:
 +
<blockquote>If App SDK is initialized using init or new methods, it will ignore the API calls resulting in no measurement. The SDK will not return any errors.</blockquote>
 +
 
 +
NielsenInit.m
 +
<syntaxhighlight lang="objective-c">#import "NielsenInit.h"
 +
#import <NielsenAppApi/NielsenEventTracker.h>
 +
 
 +
@implementation NielsenInit
 +
 
 +
+ (NielsenEventTracker *)createNielsenEventTrackerWithDelegate:(id<NielsenEventTrackerDelegate>)delegate
 +
{
 +
    //Initialising the NielsenEventTracker class by passing app information which returns the instance of NielsenEventTracker.
 +
   
 +
    NSDictionary *appInformation = @{ @"appid": @"PDA7D5EE6-B1B8-4123-9277-2A788XXXXXXX",
 +
                            @"appversion": @"1.0",
 +
                            @"appname": @"Objc Test app",
 +
                            @"sfcode": @"cz",
 +
                            @"nol_devDebug": @"DEBUG"
 +
}; 
 +
   
 +
    return [[NielsenEventTracker alloc] initWithAppInfo:appInformation delegate:delegate];
 +
}
 +
</syntaxhighlight>
 +
 
 +
 
 +
NielsenInit.h
 +
<syntaxhighlight lang="objective-c">
 +
#import <Foundation/Foundation.h>
 +
 
 +
@class NielsenEventTracker;
 +
@protocol NielsenEventTrackerDelegate;
 +
 
 +
@interface NielsenInit : NSObject
 +
 
 +
+ (NielsenEventTracker *)createNielsenEventTrackerWithDelegate:(id<NielsenEventTrackerDelegate>)delegate;
  
When the initialization call is made, a unique static configuration file, <apid>.js, will be downloaded based on the apid and will be cached on the user’s browser.
+
@end
 +
</syntaxhighlight>
 +
<br>
  
Once the configuration is downloaded, the SDK itself will be downloaded and initialized. All SDK modules are included in one file: “nlsSDK600.bundle.min.js”.
+
Init SDK inside ViewController.m
 +
<syntaxhighlight lang="objective-c">#import "ViewController.h"
 +
#import "NielsenInit.h"
  
 +
@interface ViewController ()
 +
    @property (nonatomic, strong) NielsenAppApi *nielsenAppApi;
 +
@end
  
=== Example SDK Configuration ===
+
@implementation ViewController
 +
- (void)viewDidLoad {
 +
    [super viewDidLoad];
 +
    NSLog(@"viewDidLoad - app started");
  
The configuration should include the Static Queue Snippet and an SDK Instance for an unique App ID as shown in the example:
+
    //Getting the instance of NielsenEventTracker
<syntaxhighlight lang="javascript">
+
     nielsenEventTracker = [NielsenInit createNielsenEventTrackerWithDelegate:nil];
<script type = "text/javascript" >
+
}
  /***************** Static Queue Snippet *********************/
+
...
  ! function(t, n) {
+
}
     t[n] = t[n] || {
+
</syntaxhighlight>
      nlsQ: function(e, c, o, r, s, i) {
 
        return s = t.document,
 
          r = s.createElement("script"),
 
          r.async = 1,
 
          r.src = ("http:" === t.location.protocol ? "http:" : "https:") + "//cdn-gl.imrworldwide.com/conf/" + e + ".js#name=" + c + "&ns=" + n, i = s.getElementsByTagName("script")[0], i.parentNode.insertBefore(r, i),
 
          t[n][c] = t[n][c] || {
 
            g: o || {},
 
            ggPM: function(e, o, r, s, i) {
 
              (t[n][c].q = t[n][c].q || []).push([e, o, r, s, i])
 
            },
 
            trackEvent: function(e) {
 
              (t[n][c].te = t[n][c].te || []).push(e)
 
            }
 
          }, t[n][c]
 
      }
 
    }
 
  }(window, "NOLBUNDLE");
 
  
  // Created SDK Instance
+
=== APP SDK Error & Event Codes ===
var nSdkInstance = NOLBUNDLE.nlsQ("XXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", "nlsnInstance", {nol_sdkDebug: "debug"});
+
To view the Error and Event codes for iOS and Android, please review the [[APP SDK Event Codes|App SDK Event Code]] Reference page.
</script></syntaxhighlight>
 
  
== Step 3 : Simplified API Syntax ==
+
== Step 5 : Simplified API Syntax ==
 
The existing API has a number of methods used for reporting player and application state changes to the SDK. The order of calls is important for the SDK in the existing API. In the new simplified API, all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the older API in separate calls will be provided in one single call.  
 
The existing API has a number of methods used for reporting player and application state changes to the SDK. The order of calls is important for the SDK in the existing API. In the new simplified API, all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the older API in separate calls will be provided in one single call.  
 
   
 
   
 
Main API call for the new NielsenEventTracker API:
 
Main API call for the new NielsenEventTracker API:
<br/>
+
 
<syntaxhighlight lang="java"> nSdkInstance.trackEvent({metadata})
+
<syntaxhighlight lang="objective-c">
 +
self.nielsenEventTracker?.trackEvent(TrackEventData)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
<br>
 
=== TrackEvent JSON Metadata ===
 
=== TrackEvent JSON Metadata ===
 
Parameter “data” is a JSON object with many key-value pairs that holds all information required by SDK.
 
Parameter “data” is a JSON object with many key-value pairs that holds all information required by SDK.
Line 127: Line 238:
 
  },
 
  },
 
  "playheadPosition":<playhead value>
 
  "playheadPosition":<playhead value>
}
+
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
<br>
 
<br>
Line 158: Line 269:
 
<td> <b>type</b>
 
<td> <b>type</b>
 
</td>
 
</td>
<td> Determines the metadata object that should be used for crediting.
+
<td> Determines the metadata object that should be used for crediting
 
</td>
 
</td>
 
<td>
 
<td>
Line 172: Line 283:
 
<td> <b>metadata</b>
 
<td> <b>metadata</b>
 
</td>
 
</td>
<td> Object that holds metadata values of specific types
+
<td> Object that holds metadata values of specific types<br />
 
</td>
 
</td>
 
<td> <code>Object</code>
 
<td> <code>Object</code>
Line 192: Line 303:
 
</td>
 
</td>
 
<td>
 
<td>
<p>For live stream : Position value is Unix timestamp:
+
<p>For Live Stream : Position value is UTC timestamp:
 
<code>
 
<code>
"playheadPosition":"1543437655"</code>
+
"playheadPosition":"1501225191747"</code>
</p>
+
</p><p>For VOD : Position value is playhead:
<p>For VOD : Position value is playhead:
 
 
<code>
 
<code>
 
"playheadPosition":"10"</code>
 
"playheadPosition":"10"</code>
</p>
 
<p>For static : pass only 1st position:
 
<code>
 
"playheadPosition":"1"</code>
 
 
</p>
 
</p>
 
</td></tr>
 
</td></tr>
 
</table>
 
</table>
  
=== Event identifiers ===
+
=== Event Types ===
 
The New API method supports the following event types:
 
The New API method supports the following event types:
 
{| class="wikitable"
 
{| class="wikitable"
Line 214: Line 320:
 
|-
 
|-
 
|'''playhead'''||
 
|'''playhead'''||
It is used to pass content, ad or static metadata, the current playhead value, Unix timestamp, ott information to the SDK.
+
It is used to pass content, ad or static metadata, the current playhead value, UTC timestamp or id3 payload, ott information to the SDK.
 
|-
 
|-
 
|'''pause'''||
 
|'''pause'''||
This event should be used when
+
This event should be used to in the following cases:
content playback is paused. <br > (Pause is detected by SDK automatically when the time gap between commands is more than 30 minutes.)
+
application enters background,
 +
any application interruptions,
 +
content playback is paused. (Pause is detected by SDK automatically only if time gap between commands in more than 30 minutes.)
 
|-
 
|-
 
|'''complete'''||
 
|'''complete'''||
Line 229: Line 337:
  
 
=== Content Metadata ===
 
=== Content Metadata ===
For detailed information of metadata and custom variables see [https://engineeringportal.nielsen.com/docs/Czech_SDK_Metadata specitication of Content Metadata for Czech Republic]. <br>
+
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play. For detailed information of metadata and custom variables see [https://engineeringportal.nielsen.com/docs/Czech_SDK_Metadata specitication of Content Metadata for Czech Republic]. Send content metadata for every playheadposition update.<br>When content is playing, pass only metadata for content - like
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play. Send content metadata for every playheadposition update.<br>When content is playing, pass only metadata for content - like
 
 
<syntaxhighlight lang="json">
 
<syntaxhighlight lang="json">
var metadata = {
+
let metadata:[String: String] = [
   "content": <content_metadataObject>,
+
   "content": <content metadata object>,
 
   "ad": "",
 
   "ad": "",
 
   "static": ""
 
   "static": ""
  };
+
  ]
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
=== Ad Metadata ===
 
=== Ad Metadata ===
For detailed information of metadata and custom variables see [https://engineeringportal.nielsen.com/docs/Czech_SDK_Metadata specitication of Content Metadata for Czech Republic]. <br>
+
The ad metadata (if applicable) should be passed for each individual ad, if ads are available during or before the stream begins. For detailed information of metadata and custom variables see [https://engineeringportal.nielsen.com/docs/Czech_SDK_Metadata specitication of Content Metadata for Czech Republic].<br>
The ad metadata (if applicable) should be passed for each individual ad, if ads are available during or before the stream begins.
 
 
When ad is playing, pass metadata for ad and its content as well - like
 
When ad is playing, pass metadata for ad and its content as well - like
 
<syntaxhighlight lang="json">
 
<syntaxhighlight lang="json">
var metadata = {
+
let metadata:[String: String] = [
   "content": <content_metadataObject>,
+
   "content": <content metadata object>,
   "ad": <ad_metadataObject>,
+
   "ad": <ad metadata object>,
 
   "static": ""
 
   "static": ""
};
+
]
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
=== Static Metadata ===
 
=== Static Metadata ===
For detailed information of metadata and custom variables see [https://engineeringportal.nielsen.com/docs/Czech_SDK_Metadata specitication of Content Metadata for Czech Republic]. <br>
+
Note : static is currently used in Czech Republic only for Browser implementations, not apps. Please leave metadata empty - like
As static measurement is indepentent from video measurement, keep content and ad metadata empty and pass only static metadata.
 
 
 
 
<syntaxhighlight lang="json">
 
<syntaxhighlight lang="json">
var metadata = {
+
let metadata:[String: String] = [
  "content": "",
+
  ...
   "ad": "",
+
   "static": ""
  "static": <static_metadataObject>
+
]
};
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Putting it all together - example for video (ad) ===
+
=== Putting it all together (example for ad) ===
 
+
<syntaxhighlight lang="java">
<syntaxhighlight lang="json">
 
 
// content
 
// content
var content_metadataObject = {
+
let metaDataContent:[String: String] = [
"assetid" : "assetid_example",
+
        "assetid" : "c1",
"type" : "content",
+
        "type" : "content",
"program" : "ProgramName",
+
        "program" : "myProgram",
"title" : "Bunny in woods",
+
        "title" : "Anime Movie",
"length" : "579",
+
        "length" : "52",
"mediaUrl" : "", // empty
+
        "mediaUrl" : "",
"airdate" : "20190313 20:00:00",
+
        "airdate" : "20161013 20:00:00",
"isfullepisode" : "y",
+
        "isfullepisode" : "y",
"crossId1" : "IDEC",
+
        "crossId1" : "9827411",
"nol_c1" : "p1,", // empty for VOD
+
        "nol_c1" : "p1,",
"nol_c2" : "p2,TV ident",
+
        "nol_c2" : "p2,TVident",
"segB" : "Program type",
+
        "segB" : "Programový typ",
"segC" : "", // empty
+
        "segC" : "",
"adloadtype" : "1",
+
        "adloadtype" : "2",
"hasAds" : "1"
+
        "hasAds" : "1"
}
+
    ]
  
 
// ad
 
// ad
var ad_metadataObject = {
+
let metaDataAd:[String: String] = [
"assetid" : "assetid_example_preroll",
+
        "assetid": "ad1",
"type" : "preroll", // or midroll, postroll
+
        "type": "postroll",
"length" : "30",
+
        "length": "15",
"title" : "Nielsen preroll",
+
        "title": "Ad Tostoriros",
"nol_c4" : "p4,ASMEAcode", // empty for VOD
+
        "nol_c4": "p4,ASMEA code for ad",
"nol_c5" : "p5,AtributForAd",
+
        "nol_c5": "p5,Atribut for ad",
"nol_c6" : "p6,preroll"
+
        "nol_c6": "p6,postroll",
}
+
        "nol_ac": "ad"
 +
    ]
 +
 
 +
// metaData
 +
let metadata = [
 +
        "content" : metaDataContent,
 +
        "ad" : metaDataAd,
 +
        "static" : ""
 +
            ] as [String : Any]
  
var trackObject = {
+
// trackEventObject
"event": 'playhead',
+
let trackEventObject = [
"type": 'ad',
+
        "metadata" : metadata,
"playheadPosition": '1',
+
        "event": "playhead",
"metadata": {
+
        "playheadPosition": "1",
                "content": content_metadataObject,
+
        "type": "ad",
                "ad": ad_metadataObject,
+
            ] as [String : Any]
                "static": ""
 
          }
 
    };
 
  
 
// fire TrackEvent
 
// fire TrackEvent
nSdkInstance.trackEvent(trackObject)
+
self.nielsenEventTracker.trackEvent(trackEventObject)
  
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Putting it all together - example for static ===
+
== Step 6 : API Call sequence ==
 
 
<syntaxhighlight lang="json">
 
 
 
// static
 
var static_metadataObject = {
 
"type" : "static", 
 
"section" : "static_section1", 
 
"segA" : "segA example", 
 
"segB" : "segB example", 
 
"segC" : "segC example", 
 
}
 
 
 
var trackObject = {
 
"event": 'playhead',
 
"type": 'static',
 
"playheadPosition": '1',
 
"metadata": {
 
                "content": "",
 
                "ad": "",
 
                "static": static_metadataObject
 
          }
 
    };
 
 
 
// fire TrackEvent
 
nSdkInstance.trackEvent(trackObject)
 
  
</syntaxhighlight>
+
=== Use Case 1: Content has no Advertisements ===
 
 
== Step 4 : API Call sequence ==
 
=== Use Case 1: Video - Content has no Advertisements ===
 
 
Playlist : single content, no ads.<br>
 
Playlist : single content, no ads.<br>
 
A Sample API sequence follow this flow:
 
A Sample API sequence follow this flow:
Line 357: Line 436:
 
|}
 
|}
  
=== Use Case 2: Video - Content has Advertisements ===
+
 
 +
=== Use Case 2: Content has Advertisements ===
 
Playlist : ad preroll - content - ad midroll - content continues - ad postroll.<br>
 
Playlist : ad preroll - content - ad midroll - content continues - ad postroll.<br>
 
A Sample API sequence follow this flow:
 
A Sample API sequence follow this flow:
Line 402: Line 482:
 
|}
 
|}
  
=== Use Case 3: Video - more ads in each ad pool ===
+
=== Use Case 3: Advertisement Pool ===
 +
where there are more ads in each ad pool.<br>
 
Playlist : ad preroll1 - preroll 2 - content - ad midroll 1 - ad midroll 2 - content continues - ad postroll 1 - ad postroll 2.<br>
 
Playlist : ad preroll1 - preroll 2 - content - ad midroll 1 - ad midroll 2 - content continues - ad postroll 1 - ad postroll 2.<br>
 
Use adldx parametr to assign position of ad in ad pool (starting from 0)
 
Use adldx parametr to assign position of ad in ad pool (starting from 0)
 
 
Ad in pool MetaData Example  
 
Ad in pool MetaData Example  
  
 
<syntaxhighlight lang="json">
 
<syntaxhighlight lang="json">
var ad_pool_metadataObject = {
+
JSONObject adMetadata = new JSONObject()
"assetid" : "assetid_example_preroll",
+
  .put("assetid", "assetid_example_postroll")
"type" : "preroll",  // or midroll, postroll
+
  .put("type", "postroll")
"length" : "30",
+
  .put("length", "30")
"title" : "Nielsen preroll",
+
  .put("title", "Nielsen postroll")
"nol_c4" : "p4,ASMEAcode", // empty for VOD
+
  .put("nol_c4", "p4,ASMEAcode")
"nol_c5" : "p5,AtributForAd",
+
  .put("nol_c5", "p5,AtributForAd")
"nol_c6" : "p6,preroll",
+
  .put("nol_c6", "p6,postroll")
"adldx" : "0" // position in ad pool - 0,1,2..
+
  .put("adldx" : “0” );  // position in ad pool - 0,1,2..
}
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
<br>
 
<br>
Line 425: Line 504:
 
The Simplified SDK will can automatically detect the change from ad to content, or even ad to ad if the assetID changes; however, there could be situations where the same ad is played back to back.  You can either increment/change the adldx value, and/or call adStop at the end of each Ad.
 
The Simplified SDK will can automatically detect the change from ad to content, or even ad to ad if the assetID changes; however, there could be situations where the same ad is played back to back.  You can either increment/change the adldx value, and/or call adStop at the end of each Ad.
  
=== Use Case 4: Static ===
+
Sometimes it is not possible for integrators to provide different assetId value for individual ads in a sequence of ads. Then once the next ad is started the adIdx parameter should be changed and provided - like below
A Sample API sequence follow this flow:
+
 
{| class="wikitable"
+
<syntaxhighlight lang="swift">
|-
+
self.data.updateValue("adStop", forKey: "event")
! Playlist !! API call TrackEvent !! Description
+
self.data.updateValue("223", forKey: "adldx")
|-
+
self.nielsenEventTracker.trackEvent(data)
| 1. User enters page (or HbbTV section) || event playhead, type : static, metadata : static_metaDataObject, playheadPosition : 1  || 1st call playhead
+
</syntaxhighlight>
|-
+
 
| 2. User stays within section ..  ||  n/a ||  no call needed, SDK counts seconds 2,3,4,..
+
== Step 7 : Handling Foreground and Background states ==
|-
+
<br>
| 3. User leaves section ||  n/a || no call needed, SDK automatically ends measurement
+
For iOS, background/foreground detection is handled by the app lifecylce APIs which are provided by [https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html Apple].
|}
 
  
== Step 5 : Privacy and Opt-Out ==
+
Foreground/Background state measurement is a requirement of Nielsen AppSDK implementation.
  
=== User Opt-Out ===
+
Call API call pause when app is entering Background.
The site must provide a means for the user to opt-out of, or opt back into Nielsen Measurement. A user can opt-out if they would prefer not to participate in any Nielsen online measurement research. To implement the opt-out option, include the following two items in your privacy policy.
 
*A notice that the player includes proprietary measurement software that allows users to contribute to market research (such as Nielsen TV Ratings).
 
*A link to the Nielsen Digital Measurement Privacy Policy
 
  
On the Nielsen Digital Measurement Privacy Policy page, users can click Choices to read more detailed information about the measurement software, learn about their options with regard to Nielsen measurement, and, if they do not want to participate in Nielsen online measurement, click a link to receive an opt-out cookie. Once users have opted-out, they can choose to opt back into Nielsen Measurement at anytime by selecting the opt back in link on the Nielsen Digital Privacy Policy page. When a user selects the link, their opt-out cookie will be deleted and they will be able to be measured.
+
== Step 8 : Privacy and Opt-Out ==
 +
<br>
 +
A user can opt-out if they would prefer not to participate in any Nielsen online measurement research.  
 +
Users must have access to "About Nielsen Measurement" web page - see below, or have similar text available within the native app. <br><br>
 +
Include link or text to
 +
* app settings screen
 +
* or to your Privacy Policy / EULA section
 +
* or as a button near the link to the app's Privacy Policy.
  
The following paragraph is a template for an opt-out statement (in Czech).
+
In addition, the following text must be included in your app store description.
 
<blockquote>
 
<blockquote>
Tato aplikace obsahuje proprietární měřicí software společnosti Nielsen, který uživatelům umožní přispívat k průzkumu trhu. Chcete-li se dozvědět více o informacích, které může software Nielsen shromažďovat a o Vaší možnosti měření deaktivovat, přečtěte si zásady ochrany osobních údajů Nielsen Digital Measurement na [http://priv-policy.imrworldwide.com/priv/browser/cz/cs/optout.html Nielsen Digital Measurement Privacy Policy].
+
Please note: This app features Nielsen’s proprietary measurement software which contributes to market research, like Nielsen’s TV Ratings. Please see http://priv-policy.imrworldwide.com/priv/browser/cz/cs/optout.html for more information</blockquote>
</blockquote>
+
 
 +
SDK will be sending the data pings to census even though SDK is opted out (In earlier releases all the traffic from SDK to census will be ceased). However, all the outgoing pings will have the parameter uoo=true using which backend can ignore this data.
  
=== Initialization Opt-Out ===
+
The Nielsen SDK automatically leverages the iOS's ''Limit Ad Tracking'' setting. The user is opted out of demographic measurement if the OS-level ''"Limit Ad Tracking"'' ("Limit Ad Tracking" for Android) setting is ''enabled''. As a publisher, you cannot override this setting.
To configure Opt-Out during SDK initialization. Add parametr outout into SDK init syntax - see step2. <br>
 
Use value "true" or "Yes" or "1" to OptOut, or value "false", "No" or "0" to not OptOut.
 
  
<syntaxhighlight lang="javascript">
+
=== 1. Get Url of privacy page ===
var nSdkInstance = NOLBUNDLE.nlsQ("XXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX", "nlsnInstance", {
+
URL for the Nielsen Privacy web page should be retrieved using API call optOutURL
  nol_sdkDebug: "debug",
+
<syntaxhighlight lang="java">
  outout: "true"
+
optOutUrlfromSKD = self.nielsenEventTracker!.optOutURL
});
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
If the App SDK returns NULL in the optOutURL, handle the exception gracefully and retry later.
 +
Example of returned Url is http://priv-policy.imrworldwide.com/priv/mobile/cz/cs/optout.html followed by unique hash.
  
 +
=== 2. Open URL in webView ===
 +
Recieved URL should be opened in 'WebView' / External browser. App should provide a UI control like 'close' or 'back' button to close the 'WebView' / External browser.
  
 
+
=== 3. Get current Opt-Out status (voluntarily) ===
=== Opt-out status ===
+
To retrieve the current Opt-Out status of a device, use the [[optOutStatus]] method.
Optional : to retrieve Opt-Out status call getOptOutStatus() , result is boolean value true (OptOuted = not measuring) or false (not OptOuted = measuring).
+
<syntaxhighlight lang="java">
<syntaxhighlight lang="javascript">
+
let optOutStatusDetected = nielsenEventTracker!.optOutStatus
nSdkInstance.getOptOutStatus();
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
if returns false = no OptOut, if true = OptOut is active.
  
== Step 6 : Test your player by yourself ==
+
== Step 9 : Test your player by your self ==
see how to test player at https://www.youtube.com/watch?v=t9eUsf9yh8w&feature=youtu.be
 
  
=== Test SKD API calls ===  
+
=== Guide ===
to verify SDK API calls use Chrome and install add-on called "Nielsen SDK Inspector" (https://chrome.google.com/webstore/search/%20Nielsen%20SDK%20Inspector)
+
# Connect your PC and test device (tablet or phone) via same router.
 +
# PC side: run Proxy sw (like Charles) and get local IP
 +
# Test device: modify Wifi setting to pass throug Proxy IP from add 2
 +
# Test device: run your player, launch video
 +
# PC side: filter trafic by "imr" and confirm presence of GN pings
 +
for Android see https://youtu.be/zOKOinb-zdc, for iOS see https://youtu.be/Gk0YQttiXRI
  
=== Test outgoing pings ===  
+
=== Example of GN ping ===
to verify outgoing pings use Charles Proxy or any other proxy sw capable to sniff your network traffic. Filter URLs to "imrworld". Example of such ping :
+
<syntaxhighlight lang="java">
<syntaxhighlight lang="html">
+
https://secure-cert.imrworldwide.com/cgi-bin/gn?prd=dcr&ci=cz-509218&ch=cz-509218_c04_P&asn=defChnAsset&sessionId=OasHSz6TtBLlBK7m8L63h3cEihKCL1553591192&tl=Ad%2520preroll%2520Renault&prv=1&c6=vc,c04&ca=cz-509218_c04_id_ad1&cg=myProgram&c13=asid,P9E9DAEAE-BDEE-461B-8263-54CD2DCBDA22&c32=segA,NA&c33=segB,Programov%25C3%25BD%2520typ&c34=segC,NA&c15=apn,CZ%20demo%20TrackEvent&plugv=&playerv=&sup=1&segment2=-1&segment1=cze&forward=0&ad=1&cr=4_00_99_D1_00000&c9=devid,&enc=true&c1=nuid,&at=timer&rt=video&c16=sdkv,ai.6.0.0&c27=cln,15&crs=&lat=&lon=&c29=plid,P9E9DAEAE-BDEE-461B-8263-54CD2DCBDA22&c30=bldv,ai.6.2.0.0_gada&st=dcr&c7=osgrp,IOS&c8=devgrp,TAB&c10=plt,MBL&c40=adbid,&c14=osver,iOS12_1_4&c26=dmap,1&dd=&hrd=&wkd=&c35=adrsid,&c36=cref1,9827411&c37=cref2,&c11=agg,1&c12=apv,&c51=adl,15&c52=noad,1&sd=34&devtypid=iPad5,3&pc=NA&c53=fef,n&c54=oad,&c55=cref3,&c57=adldf,2&ai=id_ad1&c3=st,a&c64=starttm,1553591199&adid=id_ad1&c58=isLive,false&c59=sesid,mmk4Mnl3tXLhVD7Conqn7sBNMtn4o1553591199&c61=createtm,1553591214&c63=pipMode,&c60=cvarall,p0%252Cdcrcz~~~~~~st%252Ca~~p4%252CASMEA%2520code%2520for%2520ad~~p5%252CAtribut%2520for%2520ad~~p6%252Cpreroll&c62=sendTime,1553591214&c68=bndlid,cz.admosphere.iOSdemo.TrackEvent.Josef&c69=cvw,&nodeTM=&logTM=&c73=phtype,Tablet&c74=dvcnm,Apple+iPad&c76=adbsnid,&df=-1&uoo=true&c44=progen,&davty=1&si=&c66=mediaurl,&vtoff=0&rnd=1553591214683
http://secure-eu-cert.imrworldwide.com/cgi-bin/gn?prd=dcr&ci=de-205177
 
&ch=de-205177_c01_static-123_P&asn=static-123&prv=1&c6=vc,c01&ca=NA
 
&c13=asid,T74896328-A13B-4985-8798-0AEBFA228D3E&c32=segA,segA%20example&c33=segB,segB%20example
 
&c34=segC,segC%20example&c15=apn,&sup=1&segment2=&segment1=&forward=0&ad=0&cr=V&c9=devid,
 
&enc=true&c1=nuid,88b7cd07-f83d-45b1-8db1-20b4e2caae55
 
&at=view&rt=text&c16=sdkv,bj.6.0.0&c27=cln,0&crs=&lat=&lon=&c29=plid,14966485169569829
 
&c30=bldv,6.0.0.1&st=dcr&c7=osgrp,&c8=devgrp,&c10=plt,&c40=adbid,&c14=osver,NA
 
&c26=dmap,1&dd=&hrd=&wkd=&c35=adrsid,&c36=cref1,&c37=cref2,&c11=agg,1&c12=apv,&c51=adl,0
 
&c52=noad,0&devtypid=&pc=NA&c53=fef,n&c54=oad,&c55=cref3,&c57=adldf,2&ai=static-123&c3=st,c&c64=starttm,1496648518
 
&adid=static-123&c58=isLive,false&c59=sesid,&c61=createtm,1496648517
 
&c63=pipMode,&c68=bndlid,&c73=phtype,&c74=dvcnm,&c76=adbsnid,&df=0&sessionId=14966485169569829
 
&c44=progen,&davty=0&si=http%3A%2F%2Fsdkdemo.admosphere.cz%2FBrowserSdk6static%2Fdemo1cz%2F
 
&c66=mediaurl,&c62=sendTime,1496648517&rnd=496564
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Step 7 : Provide your app for certification ==
+
== Step 10: Provide your app for certification ==
 
Once ready please send your application to Nielsen local staff for verification - see [http://dayrhesdkp001z.enterprisenet.org/docs/Czech_Contacts Contact list for Czech Republic].
 
Once ready please send your application to Nielsen local staff for verification - see [http://dayrhesdkp001z.enterprisenet.org/docs/Czech_Contacts Contact list for Czech Republic].
  
 
+
== Step 11: Going Live ==
== Step 8 : Going Live ==
+
After the integration has been certified (but not prior that), disable debug logging by deleting {nol_sdkDebug: 'DEBUG'} from initialization call - see Step 4.
After the integration has been certified, users will need to make a couple of updates to the initialization call to ensure that player will be measured properly.
 
Disable debug logging by deleting {nol_sdkDebug: 'DEBUG'} from initialization call - see step 2.
 

Revision as of 18:54, 28 March 2019

Engineering Portal breadcrumbArrow.png Digital breadcrumbArrow.png International breadcrumbArrow.png Czech Digital Measurement iOS Simplified SDK

Overview

The Nielsen SDK is one of multiple framework SDKs that Nielsen provides to enable measuring linear (live) and on-demand TV viewing using TVs, mobile devices, etc. The App SDK is the framework for mobile application developers to integrate Nielsen Measurement into their media player applications. It supports a variety of Nielsen Measurement Products like Digital in TV Ratings, Digital Content Ratings (DCR & DTVR), Digital Ad Ratings (DAR), Digital Audio. Nielsen SDKs are also equipped to measure static content and can track key life cycle events of an application like:

  • Application launch events and how long app was running
  • Time of viewing a sub section / page in the application.

Prerequisites

To start using the App SDK, the following details are required:

  • App ID (appid): Unique ID assigned to the player/site and configured by product.

If you do not have any of these prerequisites or if you have any questions, please see Contact list for Czech Republic.

Simplified SDK API

As part of making the SDK more user friendly and reduce the number of app integration touch points, Nielsen has designed a simple interface to pass metadata to the sdk while reducing the number of API calls. The new trackevent() API has been implemented as a wrapper for the existing SDK and will be responsible for handling new API calls, performing validation, and translation of new API calls to the existing Nielsen App SDK API methods. Applications which are already integrated with the existing SDK API, are unaffected by this new API. SimplifiedAPI vs StandardAPI New.jpg Existing API has a number of methods used for reporting player and application state changes to the SDK. Order of calls is important for the SDK in the existing API. In the new enhanced API all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the existing API in separate calls will be provided in one single call. SDK will analyse the data received in the dictionary object, compare it with the data received previously and generate a sequence of calls for the existing API.

Co-Existance.jpg

For iOS SDK framework package will contain 2 public header files. One header file will contain old SDK interface and will be used by existing clients (NielsenAppApi.h). New API will be defined in a new public header file (NielsenEventTracker.h).

Implementation

This guide covers implementation steps for iOS using Xcode.

Step 1 : Get SDK


For AppSDK versions, release dates and release notes - refer to iOS AppSDK release notes.
Always keep latest and greatest AppSDK version inside your app.

The Nielsen AppSDK can either be

  • integrated directly within an application through the use of a CocoaPod (iOS) or Gradle (Android) - select to read CocoaPod implementation guide - Recommended solution that will keep your SDK copy latest by each build. Once finished continue to Step 3.
  • or downloaded as ZIP package at Nielsen Downloads. Package contains NielsenAppApi.framework (Static and Dynamic versions) as well as sample applications. You will have to update SDK manually always when new version of AppSDK is released. Once finished continue to Step 2.

Step 2 : Setting up your xCode Development Environment


Applicable only if using downloaded Framework package

From AppSDK version 6.2 (5.2.2018) - Dynamic Framework
Prior to SDK Version 6.2.0.0 the iOS framework has been distributed as a static library packaged into framework bundle format. Apple recommends to use dynamic framework, it has some benefits over static libraries like less executable file size of an app, faster startup time and native support in xCode IDE. Nielsen AppSDK has been transformed into dynamic framework in this release.
If migrating from the static library to this new dynamic framework, once implemented, unless your specific application requires, you can remove the following Frameworks that were once required: [AdSupport, JavascriptCore, SystemConfiguration, Security, AVFoundation, libc++].

Proceed :
1) From Nielsen SDK package - inside DynamicFramework dir - extract “NielsenAppApi.Framework” and copy it to Frameworks folder of the Xcode project.
2) Add Framework - In the General tab for app configuration add NielsenAppApi.framework in the list of Embedded Binaries.
3) Add Path - Add path to the NielsenAppApi.framework in the Framework Search Paths build setting.

Before AppSDK version 6.2 (5.2.2018) - Static Framework - Depreciated
From Nielsen SDK package - inside StaticFramework dir - extract “NielsenAppApi.Framework”. For futher steps refer to Step 1: Setting up your Development Environment inside DCR Czech Video App SDK (standard API).

Note:

  • Nielsen App SDK is compatible with Apple iOS versions 9.0 and above.
  • The SDK uses the NSURLSession instead of the deprecated NSURLConnection.
  • All communications between the SDK and the Census (Collection Facility) use HTTPS

Step 3: Import SDK


Add SDK import

a) Using Objective-C :
Add the code to the View Controller’s header file

#import <NielsenAppApi/NielsenAppApi.h>


b) Using Swift :
Add the code to the ViewController.swift

import NielsenAppApi


Alternatively using Objective-C bridging header : to import a set of Objective-C files in the same app target as your Swift code, you rely on an Objective-C bridging header to expose those files to Swift. Xcode offers to create this header file when you add a Swift file to an existing Objective-C app, or an Objective-C file to an existing Swift app.
Select File/New File/Objective-C File, Xcode will prompt you to create a bridging header.
Once this bridging header file has been created, you need to add the following:

#import <NielsenAppApi/NielsenAppApi.h>

Step 4 : SDK Initialization


The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object, which can be used simultaneously without any issue. A maximum of four SDK instances per appid are supported. When a fifth SDK instance is launched, the SDK will return “nil” from initWithAppInfo:delegate:

The following table contains the list of arguments that should be passed during initialization.

Parameter / Argument Description Source Required? Example
appid Unique id for the application assigned by Nielsen. It is GUID data type Nielsen-specified Yes PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
appname Name of the application Client-defined No "Nielsen Sample App"
appversion Current version of the app used Client-defined No "1.0.2"
sfcode Nielsen collection facility to which the SDK should connect. Nielsen-specified Yes "cz"
nol_devDebug Enables Nielsen console logging. Only required for testing Nielsen-specified Optional "DEBUG"


Sample SDK Initialization Code - Swift


NielsenInit.swift

import Foundation
import NielsenAppApi

class NielsenInit : NSObject {
    class func createEventTracker(delegate: NielsenEventTrackerDelegate) -> NielsenEventTracker?{
    
        //Initialising the NielsenEventTracker class by passing app information which returns the instance of NielsenEventTracker.
        
        var nielsenEventTracker: NielsenEventTracker?
        
        let appInformation = [  
            "appid": "PDA7D5EE6-B1B8-4123-9277-2A788XXXXXXX",
            "appversion": "1.0",
            "appname": "Amazing app",
            "sfcode": "cz",
            "nol_devDebug": "DEBUG" 
            ]
        
        nielsenEventTracker = NielsenEventTracker(appInfo:appInformation1, delegate:delegate)
        return nielsenEventTracker
    }
}


ViewController.swift

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Getting the instance of NielsenEventTracker
        self.nielsenEventTracker = NielsenInit.createEventTracker(delegate: self)
}


Sample SDK Initialization Code - Objective C


Initialize the Nielsen App object within the viewDidLoad view controller delegate method using initWithAppInfo:delegate:

If App SDK is initialized using init or new methods, it will ignore the API calls resulting in no measurement. The SDK will not return any errors.

NielsenInit.m

#import "NielsenInit.h"
#import <NielsenAppApi/NielsenEventTracker.h>

@implementation NielsenInit

+ (NielsenEventTracker *)createNielsenEventTrackerWithDelegate:(id<NielsenEventTrackerDelegate>)delegate
{
    //Initialising the NielsenEventTracker class by passing app information which returns the instance of NielsenEventTracker.
    
    NSDictionary *appInformation = @{ @"appid": @"PDA7D5EE6-B1B8-4123-9277-2A788XXXXXXX",
                            @"appversion": @"1.0",
                            @"appname": @"Objc Test app",
                            @"sfcode": @"cz",
                            @"nol_devDebug": @"DEBUG"
 };  
    
    return [[NielsenEventTracker alloc] initWithAppInfo:appInformation delegate:delegate];
}


NielsenInit.h

#import <Foundation/Foundation.h>

@class NielsenEventTracker;
@protocol NielsenEventTrackerDelegate;

@interface NielsenInit : NSObject

+ (NielsenEventTracker *)createNielsenEventTrackerWithDelegate:(id<NielsenEventTrackerDelegate>)delegate;

@end


Init SDK inside ViewController.m

#import "ViewController.h"
#import "NielsenInit.h"

@interface ViewController ()
    @property (nonatomic, strong) NielsenAppApi *nielsenAppApi;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewDidLoad - app started");

    //Getting the instance of NielsenEventTracker
    nielsenEventTracker = [NielsenInit createNielsenEventTrackerWithDelegate:nil];
}
...
}

APP SDK Error & Event Codes

To view the Error and Event codes for iOS and Android, please review the App SDK Event Code Reference page.

Step 5 : Simplified API Syntax

The existing API has a number of methods used for reporting player and application state changes to the SDK. The order of calls is important for the SDK in the existing API. In the new simplified API, all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the older API in separate calls will be provided in one single call.

Main API call for the new NielsenEventTracker API:

self.nielsenEventTracker?.trackEvent(TrackEventData)


TrackEvent JSON Metadata

Parameter “data” is a JSON object with many key-value pairs that holds all information required by SDK.

Format of input object is the following:

{ 
 "event": <event identifier>,
 "type": <type of metadata>,
 "metadata":{ 
   "content": <content metadata object>,
   "ad": <ad metadata object>,
   "static": <static metadata object>
 },
 "playheadPosition":<playhead value>
}


Passed parametres

Parameter Description Supported values Example
event Event identifier

String: playhead,pause,complete, adStop

"event":"playhead"
type Determines the metadata object that should be used for crediting

String:
content, ad, static

"type":"content"
metadata Object that holds metadata values of specific types
Object
"metadata":{ 
   "content": <content_metadataObject>,
   "ad": <ad_metadataObject>,
   "static": <static_metadataObject>
 },
playheadPosition Playhead value String

For Live Stream : Position value is UTC timestamp: "playheadPosition":"1501225191747"

For VOD : Position value is playhead:

"playheadPosition":"10"

Event Types

The New API method supports the following event types:

Key Description
playhead

It is used to pass content, ad or static metadata, the current playhead value, UTC timestamp or id3 payload, ott information to the SDK.

pause

This event should be used to in the following cases: application enters background, any application interruptions, content playback is paused. (Pause is detected by SDK automatically only if time gap between commands in more than 30 minutes.)

complete

It is called when session is completed or ends.

adStop

Should be called at the end of each ad. This event type is required to handle the case when advertisements could not be distinguished, as its assetId is the same.


Content Metadata

Content metadata should remain constant throughout the entirety of an episode/clip including when ads play. For detailed information of metadata and custom variables see specitication of Content Metadata for Czech Republic. Send content metadata for every playheadposition update.
When content is playing, pass only metadata for content - like

let metadata:[String: String] = [
   "content": <content metadata object>,
   "ad": "",
   "static": ""
 ]

Ad Metadata

The ad metadata (if applicable) should be passed for each individual ad, if ads are available during or before the stream begins. For detailed information of metadata and custom variables see specitication of Content Metadata for Czech Republic.
When ad is playing, pass metadata for ad and its content as well - like

let metadata:[String: String] = [
   "content": <content metadata object>,
   "ad": <ad metadata object>,
   "static": ""
]

Static Metadata

Note : static is currently used in Czech Republic only for Browser implementations, not apps. Please leave metadata empty - like

let metadata:[String: String] = [
   ...
   "static": ""
]

Putting it all together (example for ad)

// content
let metaDataContent:[String: String] = [
        "assetid" : "c1",
        "type" : "content",
        "program" : "myProgram",
        "title" : "Anime Movie",
        "length" : "52",
        "mediaUrl" : "",
        "airdate" : "20161013 20:00:00",
        "isfullepisode" : "y",
        "crossId1" : "9827411",
        "nol_c1" : "p1,",
        "nol_c2" : "p2,TVident",
        "segB" : "Programový typ",
        "segC" : "",
        "adloadtype" : "2",
        "hasAds" : "1"
    ]

// ad
let metaDataAd:[String: String] = [
        "assetid": "ad1",
        "type": "postroll",
        "length": "15",
        "title": "Ad Tostoriros",
        "nol_c4": "p4,ASMEA code for ad",
        "nol_c5": "p5,Atribut for ad",
        "nol_c6": "p6,postroll",
        "nol_ac": "ad"
    ]

// metaData
let metadata = [
        "content" : metaDataContent,
        "ad" : metaDataAd,
        "static" : ""
            ] as [String : Any]

// trackEventObject
let trackEventObject = [
         "metadata" : metadata,
         "event": "playhead",
         "playheadPosition": "1",
         "type": "ad",
            ] as [String : Any]

// fire TrackEvent
self.nielsenEventTracker.trackEvent(trackEventObject)

Step 6 : API Call sequence

Use Case 1: Content has no Advertisements

Playlist : single content, no ads.
A Sample API sequence follow this flow:

Playlist API call TrackEvent Description
1. Video start event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 1 1st call playhead
2. Video playing .. event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 2,3,4.. etc playhead every second
3. Video ends event complete, type : content, metadata : content_metaDataObject, playheadPosition : lastPlayheadValue call complete


Use Case 2: Content has Advertisements

Playlist : ad preroll - content - ad midroll - content continues - ad postroll.
A Sample API sequence follow this flow:

Playlist API call TrackEvent Description
1. Ad Preroll start event playhead, type : ad, metadata : ad_preroll_metaDataObject + content_metaDataObject, playheadPosition : 1 1st call playhead
2. Ad Preroll playing .. event playhead, type : ad, metadata : ad_preroll_metaDataObject + content_metaDataObject, playheadPosition : 2,3,4.. etc playhead every second
3. Ad Preroll ends event adStop, type : ad, metadata : ad_preroll_metaDataObject + content_metaDataObject, playheadPosition : lastPosition call adStop at the end of add
4. Content start event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 1 1st call playhead
5. Content playing .. event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 2,3,4.. etc playhead every second
6. Content interupted by midroll event playhead, type : content, metadata : content_metaDataObject, playheadPosition : lastPlayheadValue last playhead
7. Ad Midroll start event playhead, type : ad, metadata : ad_midroll_metaDataObject + content_metaDataObject, playheadPosition : 1 1st call playhead
8. Ad Midroll playing .. event playhead, type : ad, metadata : ad_midroll_metaDataObject + content_metaDataObject, playheadPosition : 2,3,4.. etc playhead every second
9. Ad Midroll ends event adStop, type : ad, metadata : ad_midroll_metaDataObject + content_metaDataObject, playheadPosition : lastPosition call adStop at the end of add
10. Content continues after midroll event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 1 1st call playhead
11. Content playing .. event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 2,3,4.. etc playhead every second
12. Content ends event complete, type : content, metadata : content_metaDataObject, playheadPosition : lastPlayheadValue call complete for content
13. Ad Postroll start event playhead, type : ad, metadata : ad_postroll_metaDataObject + content_metaDataObject, playheadPosition : 1 1st call playhead
14. Ad Postroll playing .. event playhead, type : ad, metadata : ad_postroll_metaDataObject + content_metaDataObject, playheadPosition : 2,3,4.. etc playhead every second
15. Ad Postroll ends event adStop, type : ad, metadata : ad_postroll_metaDataObject + content_metaDataObject, playheadPosition : lastPosition call adStop at the end of add

Use Case 3: Advertisement Pool

where there are more ads in each ad pool.
Playlist : ad preroll1 - preroll 2 - content - ad midroll 1 - ad midroll 2 - content continues - ad postroll 1 - ad postroll 2.
Use adldx parametr to assign position of ad in ad pool (starting from 0) Ad in pool MetaData Example

JSONObject adMetadata = new JSONObject()
   .put("assetid", "assetid_example_postroll")
   .put("type", "postroll")
   .put("length", "30")
   .put("title", "Nielsen postroll")
   .put("nol_c4", "p4,ASMEAcode")
   .put("nol_c5", "p5,AtributForAd")
   .put("nol_c6", "p6,postroll")
   .put("adldx" : 0 );  // position in ad pool - 0,1,2..


Reset the playhead position to 0 for each ad.
Call the adStop event at the end of each ad or increment the adldx
The Simplified SDK will can automatically detect the change from ad to content, or even ad to ad if the assetID changes; however, there could be situations where the same ad is played back to back. You can either increment/change the adldx value, and/or call adStop at the end of each Ad.

Sometimes it is not possible for integrators to provide different assetId value for individual ads in a sequence of ads. Then once the next ad is started the adIdx parameter should be changed and provided - like below

self.data.updateValue("adStop", forKey: "event")
self.data.updateValue("223", forKey: "adldx")
self.nielsenEventTracker.trackEvent(data)

Step 7 : Handling Foreground and Background states


For iOS, background/foreground detection is handled by the app lifecylce APIs which are provided by Apple.

Foreground/Background state measurement is a requirement of Nielsen AppSDK implementation.

Call API call pause when app is entering Background.

Step 8 : Privacy and Opt-Out


A user can opt-out if they would prefer not to participate in any Nielsen online measurement research. Users must have access to "About Nielsen Measurement" web page - see below, or have similar text available within the native app.

Include link or text to

  • app settings screen
  • or to your Privacy Policy / EULA section
  • or as a button near the link to the app's Privacy Policy.

In addition, the following text must be included in your app store description.

Please note: This app features Nielsen’s proprietary measurement software which contributes to market research, like Nielsen’s TV Ratings. Please see http://priv-policy.imrworldwide.com/priv/browser/cz/cs/optout.html for more information

SDK will be sending the data pings to census even though SDK is opted out (In earlier releases all the traffic from SDK to census will be ceased). However, all the outgoing pings will have the parameter uoo=true using which backend can ignore this data.

The Nielsen SDK automatically leverages the iOS's Limit Ad Tracking setting. The user is opted out of demographic measurement if the OS-level "Limit Ad Tracking" ("Limit Ad Tracking" for Android) setting is enabled. As a publisher, you cannot override this setting.

1. Get Url of privacy page

URL for the Nielsen Privacy web page should be retrieved using API call optOutURL

optOutUrlfromSKD = self.nielsenEventTracker!.optOutURL

If the App SDK returns NULL in the optOutURL, handle the exception gracefully and retry later. Example of returned Url is http://priv-policy.imrworldwide.com/priv/mobile/cz/cs/optout.html followed by unique hash.

2. Open URL in webView

Recieved URL should be opened in 'WebView' / External browser. App should provide a UI control like 'close' or 'back' button to close the 'WebView' / External browser.

3. Get current Opt-Out status (voluntarily)

To retrieve the current Opt-Out status of a device, use the optOutStatus method.

let optOutStatusDetected = nielsenEventTracker!.optOutStatus

if returns false = no OptOut, if true = OptOut is active.

Step 9 : Test your player by your self

Guide

  1. Connect your PC and test device (tablet or phone) via same router.
  2. PC side: run Proxy sw (like Charles) and get local IP
  3. Test device: modify Wifi setting to pass throug Proxy IP from add 2
  4. Test device: run your player, launch video
  5. PC side: filter trafic by "imr" and confirm presence of GN pings

for Android see https://youtu.be/zOKOinb-zdc, for iOS see https://youtu.be/Gk0YQttiXRI

Example of GN ping

https://secure-cert.imrworldwide.com/cgi-bin/gn?prd=dcr&ci=cz-509218&ch=cz-509218_c04_P&asn=defChnAsset&sessionId=OasHSz6TtBLlBK7m8L63h3cEihKCL1553591192&tl=Ad%2520preroll%2520Renault&prv=1&c6=vc,c04&ca=cz-509218_c04_id_ad1&cg=myProgram&c13=asid,P9E9DAEAE-BDEE-461B-8263-54CD2DCBDA22&c32=segA,NA&c33=segB,Programov%25C3%25BD%2520typ&c34=segC,NA&c15=apn,CZ%20demo%20TrackEvent&plugv=&playerv=&sup=1&segment2=-1&segment1=cze&forward=0&ad=1&cr=4_00_99_D1_00000&c9=devid,&enc=true&c1=nuid,&at=timer&rt=video&c16=sdkv,ai.6.0.0&c27=cln,15&crs=&lat=&lon=&c29=plid,P9E9DAEAE-BDEE-461B-8263-54CD2DCBDA22&c30=bldv,ai.6.2.0.0_gada&st=dcr&c7=osgrp,IOS&c8=devgrp,TAB&c10=plt,MBL&c40=adbid,&c14=osver,iOS12_1_4&c26=dmap,1&dd=&hrd=&wkd=&c35=adrsid,&c36=cref1,9827411&c37=cref2,&c11=agg,1&c12=apv,&c51=adl,15&c52=noad,1&sd=34&devtypid=iPad5,3&pc=NA&c53=fef,n&c54=oad,&c55=cref3,&c57=adldf,2&ai=id_ad1&c3=st,a&c64=starttm,1553591199&adid=id_ad1&c58=isLive,false&c59=sesid,mmk4Mnl3tXLhVD7Conqn7sBNMtn4o1553591199&c61=createtm,1553591214&c63=pipMode,&c60=cvarall,p0%252Cdcrcz~~~~~~st%252Ca~~p4%252CASMEA%2520code%2520for%2520ad~~p5%252CAtribut%2520for%2520ad~~p6%252Cpreroll&c62=sendTime,1553591214&c68=bndlid,cz.admosphere.iOSdemo.TrackEvent.Josef&c69=cvw,&nodeTM=&logTM=&c73=phtype,Tablet&c74=dvcnm,Apple+iPad&c76=adbsnid,&df=-1&uoo=true&c44=progen,&davty=1&si=&c66=mediaurl,&vtoff=0&rnd=1553591214683

Step 10: Provide your app for certification

Once ready please send your application to Nielsen local staff for verification - see Contact list for Czech Republic.

Step 11: Going Live

After the integration has been certified (but not prior that), disable debug logging by deleting {nol_sdkDebug: 'DEBUG'} from initialization call - see Step 4.