iOS SDK API Reference: Difference between revisions

From Engineering Client Portal

(Fixed curly quotes in Opt-Out code example)
 
(48 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{Breadcrumb|}} {{Breadcrumb|Digital}} {{CurrentBreadcrumb}}
{{Breadcrumb|}} {{Breadcrumb|Digital}} {{CurrentBreadcrumb}}
[[Category:Digital]]
[[Category:Digital]]
The iOS 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. These SDKs leverage the following:
== Overview ==
*Nielsen audio watermark technologies for TV audience measurement
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 industry supported ID3 metadata tag specification
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]]), and [[Digital Ad Ratings]] (DAR). Nielsen SDKs are also equipped to measure static content and can track key life cycle events of an application like:
*Nielsen Combined Beacon technology
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
*Application launch events and how long app was running
*Application crash events
== Prerequisites ==
*Time of viewing a sub section / page in the application.
To start using the App SDK, the following items are required:
 
{| class="wikitable"
== Importing Frameworks ==
|-
'''Setting up your Xcode Development Environment'''
! style="width: 30px;" |
 
! style="width: 15%;" | Item
Nielsen App SDK is compatible with Apple iOS versions 7.0 and above.
! Description
 
! Source
SDK uses the NSURLSession available from iOS 7 instead of the deprecated NSURLConnection.
|-
 
|| ☑ || '''App ID (appid)''' || Unique ID assigned to the player/site and configured by product. || Contact Nielsen
<blockquote>'''Note''': App SDK supports devices running on iOS 9 and above, as all communications between the SDK and the Census (Collection Facility) use HTTPS.</blockquote>
|-
 
|| ☑ || '''sfcode''' || Environment that the SDK must point to || Contact Nielsen
Ensure that the following frameworks and libraries are imported into the Frameworks folder of the Xcode project before creating an instance of the Nielsen App SDK object.
|-
* UIKit.framework
|| ☑ || '''Nielsen SDK''' || Includes SDK frameworks and '''sample implementation'''; ''See [[iOS SDK Release Notes]]'' || [[Special:Downloads|Download]]
* Foundation.framework
|}
* AdSupport.framework
* SystemConfiguration.framework
* Security.framework
** Nielsen Analytics framework makes use of a number of functions in this library.
* AVFoundation.framework
** This framework is mandatory for the iOS SDK version 5.1.1 to work.
* CoreLocation.framework
* CoreMedia.framework
* NielsenAppApi.framework
* libc++.tbd (as SDK contains Objective C++ source file)
** Alternatively, include -lstdc++ in Build Settings → Other Linker Flag of the Xcode project
 
<blockquote>'''Example'''
* Extract “NielsenAppApi.Framework” from the Nielsen App SDK sample app and copy it to Frameworks folder of the Xcode project.
* Add the code <code>-#import NielsenAppApi/NielsenAppApi.h</code> to the View Controller’s header file.</blockquote>
Ensure that the following are included in the Linked Frameworks and Libraries list (located in the project’s Summary settings).
* Nielsen App SDK
* iOS security framework
 
== 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. The sharedInstance API that creates a singleton object was deprecated prior to version 5.1.1.
 
*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:]]
*When four SDK instances exist, you must destroy an old instance before creating a new one.
 
=== Stages of SDK Initialization ===
==== Step 1: App SDK Initialization ====
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>
 
==== Step 2: Setting up event and error notifications ====
Once the <code>NielsenAppApi</code> object has been initialized, the next step is to enable notifications regarding ID3 tags extracted from the playing stream. In case of standard AVFoundation player, the SDK NSNotificationCenter API is used to
* Collect HLS timed metadata events (ID3 tags) during viewing sessions.
** Set up a timed metadata event listener method for receiving ID3 tags and calling Nielsen [[sendID3]].
* SDK uses the following NielsenAppApiDelegate protocol methods to notify its delegate (set during initialization) about event / error information.
** nielsenAppApi:eventOccurred
** nielsenAppApi:errorOccurred
<blockquote>'''Note''': Ensure to add to your view controller’s <code>@interface</code>.</blockquote>
 
<syntaxhighlight lang="objective-c">(void)nielsenAppApi:(NielsenAppApi *)appApi eventOccurred:(NSDictionary *)event {NSLog(@"Sample player is Notified by a Event : %@", event);
}
(void)nielsenAppApi:(NielsenAppApi *)appApi errorOccurred:(NSDictionary *)error {NSLog(@"Sample player is Notified by an Error : %@", error);
}</syntaxhighlight>
 
===== Sample event confirmation to player application upon successful initialization of SDK =====
<syntaxhighlight lang="console">NielsenSDKSampleDebug[9028:237989] [Nls:0] -I- Analytics framework Status:
{
"Event Description" = "Nielsen App SDK Version, ai.4.0.0.4 is initialized by the Player…";
EventStatus = 2001;
TimeStamp = "2015-07-23 14:51:06 +0000";
}</syntaxhighlight>
 
==== Step 3: Nielsen App SDK Streaming Sessions ====
After setting up observers for SDK events/errors and a listener method to process incoming Nielsen ID3 tags, the next steps are to
* Call [[play]] while starting or resuming a streaming session.
* Load CMS metadata using [[loadMetadata]].
* During session play, call [[playheadPosition]] every one second until the stream is stopped or interrupted (due to ad breaks or buffering).
* Call [[stop]] when pausing, ending a viewing session, or buffering is detected.


'''Serialized JSON string from NSDictionary'''
If you do not have any of these pre-requisites or if you have any questions, please contact our SDK sales support team.
<syntaxhighlight lang="objective-c">NSDictionary* appInformation = @
Refer to [[Digital Measurement Onboarding]] guide for information on how to get a Nielsen App SDK and appid.
{
==SDK Implementation==
  @"appid": @"PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
Information on how to obtain, how to configure your development environment, and how to Initialize the Nielsen SDK is located in either the [[DCR Video iOS SDK|DCR Implementation Guide,]] or the [[DTVR iOS SDK|DTVR Implementation Guide]] depending on your requirements.
  @"appname": @"Sample App Name",
___TOC___
  @"appversion": @"2.0",
  @"sfcode": @"uat-cert",
  @"nol_devDebug": @"INFO"
}
NSData* jsonDataAppInfo = [NSJSONSerialization dataWithJSONObject:appInformation options:0 error:nil];
NSString *jsonAppInfoString = [[NSString alloc] initWithData:jsonDataAppInfo encoding:NSUTF8StringEncoding];
nlsAppApiMeter = [[NielsenAppApi alloc] initWithAppInfo:jsonAppInfoString delegate:self];</syntaxhighlight>


Nielsen App SDK object handles filtering of ID3 tags and CMS tags, queuing/buffering of data, and all communications with Nielsen collection facility.
== Nielsen iOS App SDK Application Life Cycle ==
 
===== Nielsen iOS App SDK Application Life Cycle =====


[[File:initialization_appcycle.png|center|link=]]
[[File:initialization_appcycle.png|center|link=]]


Life cycle of SDK instance includes four general states:
Life cycle of SDK instance includes four general states:
# '''Initial state''' – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the '''Initial state'''.
# '''Initial state''' – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the '''Idle state'''.
# '''Idle state''' – The SDK is initialized and is ready to process playing information. Call <code>[[initWithAppInfo:delegate:]]</code> to move into this state. The SDK instance is not processing any data, but is listening for the play event to occur.
# '''Idle state''' – The SDK is initialized and is ready to process playing information. Once Initialized, the SDK instance is not processing any data, but is listening for an event to occur.
# '''Processing state''' – The SDK instance is processing playing information. [[play]] and [[loadMetadata]] calls move the SDK instance into this state. In this state, the SDK instance will be able to process the following calls.
# '''Processing state''' – The SDK instance is processing playing information. The <code>'''play'''</code> and <code>'''loadMetadata''' </code> calls move the SDK instance into this state. In this state, the SDK instance will be able to process the following calls.
## [[playheadPosition]] – Call this API every one second when playhead position timer is fired.
## <code>'''playheadPosition'''</code> – Call this API every one second when playhead position is active. If a LIVE event, use the current UNIX timestamp (seconds since Jan-1-1970 UTC).
## [[sendID3]] – Call this API when ID3 tags are identified in the stream.
## <code>'''stop'''</code> – Call this API when the playback is paused, switches between content and ad (within the same content playback) or encounters interruptions.
## [[stop]] – Call this API when the playback is paused, switches between content and ad (within the same content playback) or encounters interruptions.
## <code>'''end'''</code> – SDK instance exits from Processing state when this API is called.
## [[end]] – SDK instance exits from Processing state when this API is called.
# '''Disabled state''' – The SDK instance is disabled and is not processing playing information.  
# '''Disabled state''' – The SDK instance is disabled and is not processing playing information. SDK instance moves into this state in one of the following scenarios.
## <code>'''appDisableApi'''</code> is set to <code>true</code>
## Initialization fails
 
## [[appDisableApi]] is called
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.


'''Note''': In case of any interruptions during playback due to alarm, calendar, call, flight mode, Wi-Fi toggle, channel change, etc., call [[stop]] to stop the measurement.
'''Note''': In case of any interruptions during playback due to alarm, calendar, call, flight mode, Wi-Fi toggle, channel change, etc., call [[stop]] to stop the measurement.
* As soon as the playback resumes, call [[play]], [[loadMetadata]] and [[sendID3]] / [[playheadPosition]].</blockquote>
* As soon as the playback resumes, call <code>'''play'''</code>, <code>'''loadMetadata''' </code> and   <code>'''playheadPosition'''</code> </blockquote>


[[File:appsdkTimeline-DCR.png|icon|link=]]
=== Finite-state machine table ===
=== Finite-state machine table ===
This table provides the possible changes of state for the SDK instance, when it is in a specific state and receives an API call.
This table provides the possible changes of state for the SDK instance, when it is in a specific state and receives an API call.
Line 176: Line 107:
** All the Variable Names like appid, appname, sfcode, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
** All the Variable Names like appid, appname, sfcode, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
* JSON string can be prepared using either raw NSString or serialized NSDictionary.
* JSON string can be prepared using either raw NSString or serialized NSDictionary.
The below is a sample - detailed information on the '''metadata requirements''' are located in either the [[DCR Video iOS SDK|DCR Implementation Guide,]] or the [[DTVR iOS SDK|DTVR Implementation Guide]]
{{ExampleCode|
|Swift =
<syntaxhighlight lang="swift">
let contentMetadata = [
    "type": "content",
    "assetid": "C77664",
    "title": "Program S2, E3",
    "isfullepisode": "Yes",
    "program": "Program Name",
    "length": "3600",
    "airdate": "20171020 10:05:00",
    "adloadtype": "2",
    "segB": "CustomSegmentValueB", //optional
    "segC": "CustomSegmentValueC", //optional
];
</syntaxhighlight>   
|Objective C = <syntaxhighlight lang="objective-c">
NSDictionary * contentMetadata = @ {
    @ "type": @ "content",
        @ "assetid": @ "C77664",
        @ "title": @ "S2,E3",
        @ "isfullepisode": @ "y", 
        @ "program": @ "Program Name",
        @ "length": @ "3600",
        @ "airdate": @ "20180120 10:00:00",
        @ "adloadtype": @ "2",
        @ "segB": @ "CustomSegmentValueB", //optional
        @ "segC": @ "CustomSegmentValueC", //optional
}
</syntaxhighlight>
}}


==== JSON for NSDictionary object ====
== Retrieving ID3 Tags ==
<syntaxhighlight lang="objective-c">
'''Only required for DTVR clients'''<br>
NSDictionary* appInformation = @{
'''Not applicable for German Clients'''<br>
                                  @"appid": @"PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
                                  @"appname": @"Sample App Name",
                                  @"appversion": @"2.0",
                                  @"sfcode": @"uat-cert",
                                  @"nol_devDebug": @"INFO"
                                };
nlsAppApiMeter = [[NielsenAppApi alloc] initWithAppInfo:appInformation delegate:delegate];</syntaxhighlight>
 
<code>appid</code>, <code>appname</code>, and <code>appversion</code> are mandatory parameters while <code>sfcode</code> is optional. <code>nol_devDebug</code> is meant for creating logs in test environments only.
 
==== JSON string from raw NSString ====
<syntaxhighlight lang="swift">NSString *appInfoString = @"{\"appid\" : \"PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\", \"appname\" : \"Sample App Name\",\"appversion\" : \"2.0\",\"sfcode\" : \"uat-cert\"}";
NielsenAppAPi *appAPI = [[NielsenAppApi alloc] initWithAppInfo:appInfoString delegate:self];</syntaxhighlight>
 
==== JSON string from serialized NSDictionary ====
<syntaxhighlight lang="objective-c">NSDictionary* appInformation = @
{
@"appid": @"appid",
@"appname": @"appname",
@"appversion": @"2.0",
@"sfcode": @"sfcode",
@"nol_devDebug": @"INFO"
};
nlsAppApiMeter = [[NielsenAppApi alloc] initWithAppInfo:appInformation delegate:self];</syntaxhighlight>


While using JSON format for sending metadat, ensure enough care in including [[special characters]] in the values for arguments.
==== Example ====
<syntaxhighlight lang="objective-c">{
@"type": @"radio", // To send "radio" in metadata string
@"assetid": @"WXYZ-FM'101",
@"stationType": @"3",
@"provider": @"SampleProvider" // To send "SampleProvider" in metadata string
}</syntaxhighlight>
<blockquote>'''Note''': For mandatory parameters like appid, appname, and appversion, please refer to the parameters table in <code>[[initWithAppInfo:delegate:]]</code></blockquote>
== Retrieving ID3 Tags ==
ID3 tags have a payload of about 249 characters and start with "www.nielsen.com".
ID3 tags have a payload of about 249 characters and start with "www.nielsen.com".


Line 222: Line 149:


Both the iOS native players have the ability to extract ID3 tags, If any other player apart from iOS native players (AVPlayer, MPMoviePlayer) is used, check and ensure that the player has the capability to extract ID3 tags.
Both the iOS native players have the ability to extract ID3 tags, If any other player apart from iOS native players (AVPlayer, MPMoviePlayer) is used, check and ensure that the player has the capability to extract ID3 tags.
=== Sample ID3 tags ===
* <code>www.nielsen.com/X100zdCIGeIlgZnkYj6UvQ==/X100zdCIGeIlgZnkYj6UvQ==/AAAB2Jz2_k74GXSzx4npHuI_<wbr />JwJd3QSUpW30rDkGTcbHEzIMWleCzM-uvNOP9fzJcQMWQLJqzXMCAxParOb5sGijSV9dNM3QiBniJYGZ5GI-lL1fXTTN0IgZ4iWBmeRiPpS9AAAAAAAAAAAAAAAAAAAAAFJWFM5SVhTONNU=/00000/00000/00</code>
* <code>www.nielsen.com/X100zdCIGeIlgZnkYj6UvQ==/R8WHe7pEBeqBhu8jTeXydg==/AAICoyitYqlxT7n6aZ0oMCGhe<wbr />Fi4CXFp46AMUPZz1lMr_M9tr3_cjee1SHqxrOiVerMDLeyn9xzocZSKwi746Re8vNOtpNCAZjYABs_J0R25IHpvOc1HS8<wbr />QHGgD5TgOJeS6gX100zdCIGeIlgZnkYj6UvVJWFNhSVhTiPE0=/00000/46016/00</code>


=== Examples of extracting ID3 tags from the iOS Native Player ===
=== Examples of extracting ID3 tags from the iOS Native Player ===
==== AVPlayer ====
{{ExampleCode|
ID3 tags will be received in the Player on AVMetadataItem Callback method.
|Objective C = <syntaxhighlight lang="objective-c">
'''Create & get the AVMetadataItem callback method'''
    //Adding observer to player to track play,pause and reverse
<syntaxhighlight lang="swift">[self.player addObserver:self
    [player addObserver:self
        forKeyPath: kTimedMetadataKey
            forKeyPath:@"rate"
        options: NSKeyValueObservingOptionNew
                options:(NSKeyValueObservingOptionNew)
        Context: MyStreamingMovieViewControllerTimedMetadataObserverContext];</syntaxhighlight>
                context:nil];
 
  </syntaxhighlight>
 
------------------------------
<syntaxhighlight lang="objective-c">
  <syntaxhighlight lang="objective-c">      
(void)observeValueForKeyPath:(NSString*) path
        //Setting observer to track timedMetadata
                       ofObject: (id)object
        [player addObserver:self
                         change: (NSDictionary*)change
                forKeyPath: timedMetadataKey
                       context: (void*)context
                    options: (NSKeyValueObservingOptionNew)
                    context: &TimedMetadataObserverContext];
  </syntaxhighlight>
------------------------------
  <syntaxhighlight lang="objective-c">    
- (void)observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                       context:(void *)context
{
{
/* Set the AVPlayerLayer on the view to allow the AVPlayer object to display
     if(keyPath == timedMetadataKey){
    its content. */
        if(context == &TimedMetadataObserverContext){
     //[playerLayerView.playerLayer setPlayer:player];
           
/* AVPlayerItem "status" property value observer. */
if (context == MyStreamingMovieViewControllerTimedMetadataObserverContext)
        {
             id newMetadataArray = [change objectForKey:NSKeyValueChangeNewKey];
             id newMetadataArray = [change objectForKey:NSKeyValueChangeNewKey];
             if (newMetadataArray != [NSNull null])
             if (newMetadataArray != [NSNull null])
Line 254: Line 182:
                 array = newMetadataArray;
                 array = newMetadataArray;
                 for (AVMetadataItem *metadataItem in array)
                 for (AVMetadataItem *metadataItem in array)
                  {
                {
                    //Handling TimedMetadata
                     [self handleTimedMetadata: metadataItem];
                     [self handleTimedMetadata: metadataItem];
                  }
                }
             }
             }
}
           
        }
    }
</syntaxhighlight>
</syntaxhighlight>
 
------------------------------
 
  <syntaxhighlight lang="objective-c">    
<syntaxhighlight lang="objective-c">
- (void)handleTimedMetadata:(AVMetadataItem *)timedMetadata
(void)handleTimedMetadata: (AVMetadataItem*)timedMetadata
{
{
/* We expect the content to contain plists encoded as timed metadata. AVPlayer turns these into NSDictionaries. */
    // We expect the content to contain plists encoded as timed metadata
    // AVPlayer turns these into NSDictionaries
   
     id extraAttributeType = [timedMetadata extraAttributes];
     id extraAttributeType = [timedMetadata extraAttributes];
     NSString * extraString= nil;
     NSString *extraString = nil;
     if ([extraAttributeType isKindOfClass:[NSDictionary class]])
     if ([extraAttributeType isKindOfClass:[NSDictionary class]])
     {
     {
         extraString = [extraAttributeType valueForKey:@"info"];
         extraString = [extraAttributeType valueForKey:@"info"];
     }
     }
     else if([extraAttributeType isKindOfClass:[NSString class]])
     else if ([extraAttributeType isKindOfClass:[NSString class]])
     {
     {
         extraString = extraAttributeType;
         extraString = extraAttributeType;
     }
     }
  if ([(NSString *)[timedMetadata key] isEqualToString:@"PRIV"]&&[extraString rangeOfString:@"www.nielsen.com"].length> 0)
   
  {
    NSString *key = [NSString stringWithFormat:@"%@", [timedMetadata key]];
  if ([[timedMetadata value] isKindOfClass:[NSData class]])
   
  {
    //If tag starts with "www.nielsen.com", then only sending to SDK
            // Sending ID3 Tag
    if ([key isEqualToString:@"PRIV"] && [extraString rangeOfString:@"www.nielsen.com"].length > 0)
/* The  sendID3: sends the extracted Nielsen ID3 payload to the App SDK for analysis. */
            [[NielsenAppApi sharedInstance]sendID3: extraString];
            NSString *value=[timedMetadata stringValue];
            if (value != nil)
            {
              // NSString *strMessage=[[@"ID3 Tag Received " stringByAppendingFormat:@"%d\n",countForMetadata] stringByAppendingString:value];
                [self appendLogConsoleText:extraString];
            }
  }
  }
    else
     {
     {
         NSLog(@"Could not send ID3 tags");
          
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [nielsenApi sendID3:extraString];
        });
     }
     }
    countForMetadata++;
   
}
}
</syntaxhighlight>
</syntaxhighlight>
 
|Swift = <syntaxhighlight lang="swift">
==== Movie Player ====
      //Setting observer to track timedMetadata
ID3 tags will be received in the Player on MPTimedMetadata Callback method.
            player.addObserver(self, forKeyPath: timedMetadataKey, options: NSKeyValueObservingOptions.new, context: &TimedMetadataObserverContext)</syntaxhighlight>
'''Sample Implementation of MPTimedMetadata callback'''
------------------------------------------------------
<syntaxhighlight lang="objective-c">
<syntaxhighlight lang="swift">
(void)handleTimedMetadata:(MPTimedMetadata*)timedMetadata
  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
{
       
    id extraAttributeType = [timedMetadata  allMetadata];
        if keyPath == timedMetadataKey {
    NSString * extraString= nil;
            if(context == &TimedMetadataObserverContext){
    if ([extraAttributeType isKindOfClass:[NSDictionary class]])
                if change != nil {
    {
                    let timedMetadataArray = change![.newKey]
         extraString = [extraAttributeType valueForKey:@"info"];
                    if timedMetadataArray != nil && (timedMetadataArray! as AnyObject) is Array<Any> {
    }
                        for item in timedMetadataArray as! [AVMetadataItem]  {
    else if([extraAttributeType isKindOfClass:[NSString class]])
                            //Handling TimedMetadata
    {
                            self.handleTimedMetadata(metadataItem: item)
         extraString = extraAttributeType;
                        }
                    }
                }
            }
        }
</syntaxhighlight>
------------------------------------------------------
<syntaxhighlight lang="swift">  
func handleTimedMetadata(metadataItem: AVMetadataItem) {
        guard let extraAttributeType = metadataItem.extraAttributes else {
            return
        }
        let info : AVMetadataExtraAttributeKey = AVMetadataExtraAttributeKey(rawValue: "info")
         let extraString = extraAttributeType[info] as AnyObject
        let key = metadataItem.key as! String
       
        //If tag starts with "www.nielsen.com", then only sending to SDK
        if key == "PRIV" && extraString.range(of: "www.nielsen.com").length > 0 {
           
            DispatchQueue.global(qos: .default).async { () -> Void in
                self.nielsenApi?.sendID3(extraString as! String)
            }
         }
     }
     }
      
      
  if ([(NSString *)[timedMetadata key] isEqualToString:@"PRIV"]&&[extraString rangeOfString:@"www.nielsen.com"].length> 0)
    {
    if ([[timedMetadata value] isKindOfClass:[NSData class]])
      {
            // Sending ID3 Tag
            [[NielsenAppApi sharedInstance]sendID3: extraString];
            NSString *value=[timedMetadata value];
            if (value != nil)
            {
               
                [self appendLogConsoleText:extraString];
            }
      }
    }
    else
    {
        NSLog(@"Could not send ID3 tags");
    }
    countForMetadata++;
}
</syntaxhighlight>
</syntaxhighlight>
}}
<blockquote>'''Note:''' ID3 tags are not applicable for International (Germany)</blockquote>
<blockquote>'''Note:''' ID3 tags are not applicable for International (Germany)</blockquote>


== IOS SDK API Methods & Properties ==
== IOS SDK API Methods & Properties ==
{| class="wikitable sortable"
{| class="wikitable sortable"
|-
|-
! Scenario !! Method / Property !! DTVR !! DAR !! Digital Audio !! DCR !! International (Germany) !! Description
! Scenario !! Method / Property !! DTVR !! DAR !! DCR !! International (Germany) !! Description
|-
|-
| Initialize || [[initWithAppInfo:delegate:]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used to create a new instance of the SDK object
| Initialize || [[initWithAppInfo:delegate:]] || ✔ || ✔ || ✔ || ✔ || Used to create a new instance of the SDK object
|-
|-
| Measurement || [[play]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used when there is an ID3 fed product such as DTVR and the client does not want to send in all the CMS metadata that is sent in loadMetadata. This allows the client to send in at least the required “channel name” value associated to the ID3 feed. If this is not called then the “channel name” value populated will be the default value of “defaultChannelName”.
| Measurement || [[play]] || ✔ || ✔ || ✔ || ✔ || Used when there is an ID3 fed product such as DTVR and the client does not want to send in all the CMS metadata that is sent in loadMetadata. This allows the client to send in at least the required “channel name” value associated to the ID3 feed. If this is not called then the “channel name” value populated will be the default value of “defaultChannelName”.
|-
|-
| Measurement || [[loadMetadata]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used to send ad or content metadata to the SDK in the form of JSON string. Application constructs a JSON hashmap and calls this API.
| Measurement || [[loadMetadata]] || ✔ || ✔ || ✔ || ✔ || Used to send ad or content metadata to the SDK in the form of JSON string. Application constructs a JSON hashmap and calls this API.
|-
|-
| Measurement || [[sendID3]] || ✔ || || || ✘ || ✘ || Used to send the ID3 metadata.
| Measurement || [[sendID3]] || ✔ ||   ||   ||   || Used to send the ID3 metadata.
|-
|-
| Measurement || [[playheadPosition]] || || ✘ || ✔ || ✔ || ✔ || Used to send the playhead position.
| Measurement || [[playheadPosition]] ||   ||   || ✔ || ✔ || Used to send the playhead position.
|-
|-
| Measurement || [[stop]] || ✔ || ✘ || ✔ || ✔ || ✔ || Used when playback is paused and when switching between ad and content or content and ad.
| Measurement || [[stop]] || ✔ ||   || ✔ || ✔ || Used when playback is paused and when switching between ad and content or content and ad.
|-
|-
| Measurement || [[end]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used when content playback is complete. This is triggered 1) at the end of the content stream, 2) if the user switches to another piece of content
| Measurement || [[end]] || ✔ || ✔ || ✔ || ✔ || Used when content playback is complete. This is triggered 1) at the end of the content stream, 2) if the user switches to another piece of content
|-
|-
| Measurement || [[updateOTT]] || || || ✘ || ✘ || ✔ || Used to notify App SDK that the remote OTT device (like Google ChromeCast, Roku, Amazon FireTV, etc.) is connected / disconnected (change of OTT status).
| Measurement || [[staticEnd]] ||   ||   || || ✔ || Used with DCR Static duration in between loadMetadata calls for static content when section name is the same but a new static view event and duration measurement restart is desired
|-
|-
| Opt-out || [[optOutURL]] || ✔ || ✔ || || || ✔ || Used to fetch the Nielsen opt-out web page URL.
| Measurement || [[updateOTT]] ||   ||   ||   || ✔ || Used to notify App SDK that the remote OTT device (like Google ChromeCast, Roku, Amazon FireTV, etc.) is connected / disconnected (change of OTT status).
|-
|-
| Opt-out || [[userOptOut]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used to supply the response message from opt-out webpage to the SDK.
| Opt-out || [[optOutURL]] || ✔ || ✔ || ✔ || ✔ || Used to fetch the Nielsen opt-out web page URL.
|-
|-
| Opt-out || [[optOutStatus]] || ✔ || ✔ || ✔ || ✔ || ✔ || Call this API to retrieve the Opt-Out or Opt-In state.
| Opt-out || [[userOptOut]] || ✔ || ✔ || ✔ || ✔ || Used to supply the response message from opt-out webpage to the SDK.
|-
| Opt-out || [[optOutStatus]] || ✔ || ✔ || ✔ || ✔ || Call this API to retrieve the Opt-Out or Opt-In state.
|-
|-
| Opt-out
| Opt-out
| [[appDisableApi]]
| [[appDisableApi]]
(kill switch)  
(kill switch)  
|| ✔ || ✔ || ✔ || ✔ || ✔ || Used to disable the SDK.
|| ✔ || ✔ || ✔ || ✔ || Used to disable the SDK.
|-
| Viewability<br/>Audibility
| [[trackViewability]]
|| ✔ ||  || ✔ || ✔ || Used to start the viewability measurement.
|-
|-
| Log || [[lastErrorDict]] || ✔ || ✔ || ✔ || ✔ || ✔ || Returns SDK error in the form of dictionary if any error has occurred.
| Log || [[lastErrorDict]] || ✔ || ✔ || ✔ || ✔ || Returns SDK error in the form of dictionary if any error has occurred.
|-
|-
| Log || [[lastEventDict]] || ✔ || ✔ || ✔ || ✔ || ✔ || Returns SDK event in the form of dictionary if any event has occurred.
| Log || [[lastEventDict]] || ✔ || ✔ || ✔ || ✔ || Returns SDK event in the form of dictionary if any event has occurred.
|-
|-
| Log || [[meterVersion]] || ✔ || ✔ || ✔ || ✔ || ✔ || Returns the current SDK version.
| Log || [[meterVersion]] || ✔ || ✔ || ✔ || ✔ || Returns the current SDK version.
|-
|-
| Log || [[nielsenId]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used to get a string defining the Nielsen ID (NUID) number for the device.
| Log || [[nielsenId]] || ✔ || ✔ || ✔ || ✔ || Used to get a string defining the Nielsen ID (NUID) number for the device.
|-
|-
| Log || [[demographicId]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used to retrieve Demographic ID (Device ID) of the current device.
| Log || [[demographicId]] || ✔ || ✔ || ✔ || ✔ || Used to retrieve Demographic ID (Device ID) of the current device.
|-
|-
| Log || [[debug]] || ✔ || ✔ || ✔ || ✔ || ✔ || Used to enable/disable debug flags. Newly introduced in SDK version 5.0.0 for International (Germany)
| Log || [[debug]] || ✔ || ✔ || ✔ || ✔ || Used to enable/disable debug flags. Newly introduced in SDK version 5.0.0 for International (Germany)
|}
|}
== iOS Opt-Out Implementation ==
To opt out, users must have access to "About Nielsen Measurement" page. User can click this page from app settings screen.
Include '''About Nielsen Measurement and Your Choices''' link in the Privacy Policy / EULA or as a button near the link to the app's Privacy Policy.
*URL for the Nielsen Privacy web page should be retrieved from the optOutURL property of the SDK object [[optOutURL]] and opened in 'WebView' / External browser.
*If the App SDK returns NULL in the optOutURL, handle the exception gracefully and retry later.
*To retrieve the current Opt-Out status of a device, use the [[optOutStatus]] method.
<!-- === Displaying Opt-Out in a WebView ===
<syntaxhighlight lang="objective-c">NielsenAppApi nAppApiObject;
@property(strong) UIWebView *optOutView;
// create WebView and set self as delegate
self.optOutView=[[UIWebViewalloc]initWithFrame:self.view.bounds];
self.optOutView.scalesPageToFit = yes;
// get Nielsen defined web address and load the page
NSString *webAddress = nAppApiObject.optOutURL;
if(webAddress == nil)
    { // Handle it gracefully and retry later}
else
{
    [optOutView loadRequest:[NSURLRequest requestWithURL:webAddress]];
    // show the view to the user
    [self.view addSubview:optOutView];
}</syntaxhighlight>
-->
The app must provide access to "About Nielsen Measurement" page for the users. Include "About Nielsen Measurement" and Your Choices link in the Privacy Policy / EULA or as a button near the link to the app's Privacy Policy.
[[File:Privacy policy iOS.jpg|link=]]
*URL to this web page should be called from SDK and opened in 'WebView' / External browser.
*If the App SDK returns NULL as Opt-Out URL, handle this case and retry later.
*To retrieve the current Opt-Out status, use the [[optOutStatus]] property from Nielsen's SDK API.
<syntaxhighlight lang="objective-c">
@property (readonly) BOOL optOutStatus;
</syntaxhighlight>
*App should provide a UI control like 'close' or 'back' button to close the 'WebView' / External browser.
=== Sequence Diagram ===
[[File:optoutsequence-ios.jpg|link=]]
== Opt-out SDK Version '''5.1.1.17 or above''' ==
<blockquote>'''Note:''' If using SDK Versions 1.2.3, 4.0.0.8, 5.1.0, 5.1.1.12, skip ahead to: [[#Opt-out SDK Version 1.2.3, 4.0.0.8, 5.1.0, 5.1.1.12|Opt-out SDK Version 1.2.3, 4.0.0.8, 5.1.0, 5.1.1.12]]</blockquote>
Users can opt out or opt back into Nielsen Measurement. Opt-Out feature relies on iOS' system setting – "Limit Ad Tracking". The setting can be accessed in the Settings application on any iOS device: '''Settings → Privacy → Advertising → Limit Ad Tracking'''.
User is opted out of Nielsen online measurement research when the "Limit Ad Tracking" setting is enabled.
[[File:Opt-Out iOS.jpg|link=]]
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.</blockquote>
== Opt-out SDK Version '''1.2.3, 4.0.0.8, 5.1.0, 5.1.1.12''' ==
<blockquote>'''Note:''' If using SDK Version 5.1.1.17 or above, refer to documentation above ([[#Opt-out SDK Version 5.1.1.17 or above|Opt-out SDK Version 5.1.1.17 or above]])</blockquote>
When a user clicks the Opt-Out / Opt-In link, the application should invoke [[optOutURL]] to get the link to the Nielsen Privacy page from SDK.
=== Privacy Page ===
[[File:privacy-policy.jpg|link=]]
*There are two click here links – one for Opt-Out and one for Opt-In. Click the required link:
**
[[File:Opt-Out Combined.jpg|link=]]
*Click OK in the dialog that appears, to confirm the action.
*The Opt-Out occurs by opening a Nielsen-defined web page and passing the user choice from the 'WebView'. In order to do this, the application needs to
**Implement the UIWebView delegate method to open the Nielsen Privacy web page
**Capture user's selection
**Pass the selection back to the SDK via the [[userOptOut]].
=== Capture and forward user selection ===
<syntaxhighlight lang="objective-c">-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *command = [NSString stringWithFormat:@"%@",request.URL];
    if ([command isEqualToString:kNielsenWebClose]) {
        // Close the WebView
        [self performSelector:@selector(closeOptOutView) withObject:nil afterDelay:0];
        return NO;
    }
    // Retrieve next URL if it’s not opt-in/out selection
    return (![nAppApiObject userOptOut:command]);
}</syntaxhighlight>
*The app gets the user selection string via webviews shouldStartLoadWithRequest and invokes [[userOptOut]] with user selection. The delegate method handles the 'WebView' URL requests, interprets the commands, and calls the SDK accordingly.
**<code>[nAppApiObject userOptOut:command]</code> passes the user's selection on Nielsen Privacy page to the SDK to allow the SDK to perform the required functions.
<blockquote>'''Note:''' When 'WebView' is closed, pass the status returned from 'WebView' to the SDK within the app.</blockquote>
<blockquote>'''Note:''' App SDK manages the user's choice (Opt-Out / Opt-In), the app does not need to manage this status.</blockquote>
== TVOS Opt-out ==
To Opt-Out, users must have access to “About Nielsen Measurement” page.
TVOS does not support creating instances of UIWebView and display web pages. Instead it makes use of a TVML page (built on TVML template) which displays information in a specific order.
Opt-Out page is built on descriptiveAlertTemplate and appears as follows:
[[File:TVOs OptOut LimitAdTracking1.png|link=]]
[[File:TVOs OptOut LimitAdTracking2.png|link=]]
=== Opt-out wording ===
<blockquote>About Nielsen Measurement
YOUR CHOICES
Television and the way we watch it have come a long way since Nielsen began measuring TV audiences in 1950. Today, the ability to watch programs at any time and on multiple devices amplifies the need for exceptionally adept and flexible audience measurement capabilities.
Consumers are changing with the times, and the same goes for Nielsen. As technology continues to evolve and media companies try new ways to attract viewers, understanding what consumers are watching — and what they're watching on — is more important than ever. Today, viewing video is a personal and mobile experience —anytime and anywhere. Our capabilities provide relevant metrics that are necessary to inform successful marketing and programming and drive continued growth.
As a global information and measurement leader, we are committed to protecting the privacy and security of the data we collect, process and use. While our digital measurement products are not used to identify you in any way, they help us and our clients measure and analyze how consumers engage with media across online, mobile and emerging technologies, and offer insights into consumer behavior.
Nielsen believes that you should have a choice about whether to contribute to our research and insights. To opt out of Nielsen measurement on this device, you need only to activate the “Limit Ad Tracking” option in your device's settings. If you have this app on more than one mobile device, you will need to change the settings on each device.
If, after you have opted out, you change your mind and would like to opt back in, please deactivate the “Limit Ad Tracking” option in your device's settings.
To learn more about our digital measurement products and your choices in regard to them, please visit http://www.nielsen.com/digitaIprivacy.</blockquote>
Users need to toggle the “Limit Ad Tracking” option in order to Opt-In / Opt-Out of Nielsen Measurement.
To retrieve the current Opt-Out status of a device, use the [[optOutStatus]] method.
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.</blockquote>


== NielsenAppApi Class Description ==
== NielsenAppApi Class Description ==
Line 512: Line 325:
   @property (assign) BOOL appDisableApi;
   @property (assign) BOOL appDisableApi;
   @property (assign) BOOL debug;
   @property (assign) BOOL debug;
   @property (readonly) NSString *nielsenId;
   @property (readonly, nonnull) NSString *nielsenId;
   @property (readonly) NSString *demographicId;
   @property (readonly, nonnull) NSString *demographicId;
   @property (readonly) NSString *optOutURL;
   @property (readonly, nonnull) NSString *firstPartyId;
   @property (readonly) NSString *meterVersion;
  @property (readonly, nonnull) NSString *vendorId;
   @property (readonly) NSDictionary *lastEventDict;
  @property (readonly, nonnull) NSString *optOutURL;
   @property (readonly) NSDictionary *lastErrorDict;
   @property (readonly, nullable) NSString *meterVersion;
   @property (readonly, nullable) NSDictionary *lastEventDict;
   @property (readonly, nullable) NSDictionary *lastErrorDict;


   (instancetype)initWithAppInfo:(id)appInfo delegate:(id)delegate;
   - (nullable instancetype)initWithAppInfo:(nonnull id)appInfo delegate:(nullable id<NielsenAppApiDelegate>)delegate


   (void)play:(id)channelInfo;
   - (void)play:(nullable id)channelInfo;
   (void)loadMetadata:(id)metadata;
   - (void)loadMetadata:(nullable id)metadata;
   – (void)stop;
   – (void)stop;
   – (void)end;
   – (void)end;
   (void)playheadPosition:(long long)playheadPos;
   - (void)playheadPosition:(long long)playheadPos;
   (void)sendID3:(NSString *)data;
   - (void)sendID3:(nonnull NSString *)data;
   (void)updateOTT:(id)ottInfo;
   - (void)updateOTT:(nonnull id)ottInfo;
   (BOOL)userOptOut:(NSString *)optOut;
   - (BOOL)userOptOut:(nonnull NSString *)optOut;
  - (void)trackViewability:(nonnull NSDictionary *)data;


   (NSString *)getNielsenId _attribute((deprecated((“Please use nielsenId property instead.”))));
   - (nonnull NSString *)getNielsenId __attribute((deprecated(("nielsenId is not used by the SDK anymore"))));
   (NSString *)optOutURLString _attribute((deprecated((“Please use optOutURL property instead.))));
   - (nonnull NSString *)optOutURLString __attribute((deprecated(("Please use optOutURL property instead."))));
   (NSString *)getMeterVersion _attribute((deprecated((“Please use meterVersion property instead.))));
   - (nullable NSString *)getMeterVersion __attribute((deprecated(("Please use meterVersion property instead."))));
   (NSDictionary *)getLastEventDict _attribute((deprecated((“Please use lastEventDict property instead.))));
   - (nullable NSDictionary *)getLastEventDict __attribute((deprecated(("Please use lastEventDict property instead."))));
   (NSDictionary *)getLastErrorDict _attribute((deprecated((“Please use lastErrorDict property instead.))));
   - (nullable NSDictionary *)getLastErrorDict __attribute((deprecated(("Please use lastErrorDict property instead."))));  


   @protocol NielsenAppApiDelegate<NSObject>
   @protocol NielsenAppApiDelegate <NSObject>


   @optional
   @optional
   (void)nielsenAppApi:(NielsenAppApi *)appApi eventOccurred:(NSDictionary *)event;
   - (void)nielsenAppApi:(nonnull NielsenAppApi *)appApi eventOccurred:(nonnull NSDictionary *)event;
   (void)nielsenAppApi:(NielsenAppApi *)appApi errorOccurred:(NSDictionary *)error;
   - (void)nielsenAppApi:(nonnull NielsenAppApi *)appApi errorOccurred:(nonnull NSDictionary *)error;


@end
@end
</syntaxhighlight>
=== AppApiEventCode ===
An enumeration with predefined App SDK event state transition codes.
<syntaxhighlight lang="objective-c">
typedef NS_ENUM(unsigned int, AppApiEventCode)
{
    AppApiStartup = 2001,
    AppApiShutdown = 2002,
}AppApiEventCode;
</syntaxhighlight>
=== App SDK Event Codes ===
{| class="wikitable"
|-
! Event Code !! Event Name !! Event Description
|-
| 2001 || AppApiStartup || App SDK has initialized successfully. It will happen only after App SDK has received a valid config file
|-
| 2002 || AppApiShutdown || App SDK is shutting down. It will happen just before App SDK is destroyed
|}
=== AppApiErrorCode ===
iOS contains two types of error codes, 1-15 and 1001-1009.
For, 1-15, an enumeration with predefined error codes which the App SDK object can generate.
<syntaxhighlight lang="objective-c">typedef NS_ENUM(unsigned int, LogCode) {
    LogCodeFailedParseStartInfo, // 1.
    LogCodeFailedParseMetadata, // 2.
    LogCodeFailedProcessID3, // 3.
    LogCodeFailedReceiveConfig, // 4.
    LogCodeFailedParseConfig, // 5.
    LogCodeFailedStartProcessor, // 6.
    LogCodeFailedCreateUrl, // 7.
    LogCodeFailedCreateRequest, // 8.
    LogCodeFailedSendHttpRequest, // 9.
    LogCodeFailedSendPing, // 10.
    LogCodeFailedSendTSV, // 11.
    LogCodeFailedSendStationRequest, // 12.
    LogCodeFailedAccessDatabase, // 13.
    LogCodeException, // 14.
    LogCodeInvalidPlayheadPosition, // 15.
    LogCodeLongAd, // 16.
    LogCodeIncorrectSfcode, // 17.
    LogCodeHemUidExceedLimit, // 18.
    LogCodeViewabilityUnableFindView // 19.
};</syntaxhighlight>
For, 1001-1009, an enumeration with predefined error codes which the App SDK object can generate.
<syntaxhighlight lang="objective-c">
typedef NS_ENUM(unsigned int, AppApiErrorCode)
{
  AppApiNetworkConnectionFailure = 1001,
  AppApiFileWriteFailure = 1002,
  AppApiFileReadFailure = 1003,
  AppApiEmptyValue = 1004,
  AppApiEmptyAppName = 1005,
  AppApiEmptyAppVersion = 1006,
  AppApiEmptyAppId = 1007,
  AppApiAnExceptionOccured = 1008,
  AppApiUnknownExceptionOccured = 1009
};
</syntaxhighlight>
</syntaxhighlight>
=== App SDK Error Codes ===
{| class="wikitable"
|-
! Error Code !! Error Name !! Error Description
|-
| 1 || LogCodeFailedParseStartInfo || Failed to parse the play() JSON string
|-
| 2 || LogCodeFailedParseMetadata || Failed to parse the loadMetadata() JSON string
|-
| 3 || LogCodeFailedProcessID3 || Failed to process ID3 data on a data processor
|-
| 4 || LogCodeFailedReceiveConfig || Failed to receive configuration file from Census
|-
| 5 || LogCodeFailedParseConfig || Failed to parse the config file JSON string
|-
| 6 || LogCodeFailedStartProcessor || Failed to create SDK processor
|-
| 7 || LogCodeFailedCreateUrl || Failed to generate URL due to missing mandatory parameter
|-
| 8 || LogCodeFailedCreateRequest || Failed to create request in HTTP client
|-
| 9 || LogCodeFailedSendHttpRequest || Failed sending HTTP or HTTPS request
|-
| 10 || LogCodeFailedSendPing || Failed to send ping
|-
| 11 || LogCodeFailedSendTSV || Failed to send TSV request
|-
| 12 || LogCodeFailedSendStationRequest || Failed to send StationId request
|-
| 13 || LogCodeFailedAccessDatabase || Failed to read/write from/to database table
|-
| 14 || LogCodeException || Any exception handled by SDK code
|-
| 15 || LogCodeInvalidPlayheadPosition || Invalid playhead position
|-
| 16 || LogCodeLongAd || Long ad
|-
| 17 || LogCodeIncorrectSfcode || Incorrect client supplied sfcode
|-
| 18 || LogCodeHemUidExceedLimit || Exceeded limit of chars for hem uid
|-
| 19 || LogCodeViewabilityUnableFindView || Viewability unable to find a view with the specificed tag
|}
{| class="wikitable"
|-
! Error Code !! Error Name !! Error Description
|-
| 1001 || AppApiNetworkConnectionFailure || App SDK Could not connect to server
|-
| 1002 || AppApiFileWriteFailure || App SDK Could not write to file
|-
| 1003 || AppApiFileReadFailure || App SDK Could not read data from file
|-
| 1004 || AppApiEmptyValue || Empty value Found.
|-
| 1005 || AppApiEmptyAppName || Cannot initialize SDK Object without an AppName(Player Name)
|-
| 1006 || AppApiEmptyAppVersion || Cannot initialize API Object without an AppVersion
|-
| 1007 || AppApiEmptyAppId || Cannot initialize API Object without an AppId
|-
| 1008 || AppApiAnExceptionOccured || Exception occurred
|-
| 1009 || AppApiUnknownExceptionOccured || Unknown exception occurred
|}


== Nielsen Sample Applications ==
== Nielsen Sample Applications ==
Nielsen iOS sample player application consists of two sample application based on native Players integrated with SDK framework. The player demonstrates all the supported functions of the SDK.
Nielsen SDK client package contains iOS sample player applications based on native Players integrated with SDK framework. The players demonstrate all the supported functions of the SDK and show the integration details for both legacy and trackEvent API. There are players developed on Objective C and Swift. Implementation of the sample apps is based on native iOS AVPlayer.
*NielsenVideoPlayer
*NielsenRadioPlayer
Implementation of Video and Audio sample apps is based on native iOS AVPlayer.


The UI components of the iOS App SDK sample applications are common to both as shown below.
The UI components of the iOS App SDK sample applications are common to both as shown below.
Line 559: Line 500:
*The Email button 📧 can be used to email the tags and status in a text file to Nielsen.
*The Email button 📧 can be used to email the tags and status in a text file to Nielsen.
If target device supports Picture-in-Picture playing, it could be activated by PIP button in the Video player window.
If target device supports Picture-in-Picture playing, it could be activated by PIP button in the Video player window.
*Channel URLs and metadata are obtained from two locations:
*Channel URLs and metadata are obtained from the JSON file named appConfig.json and stored in the application bundle.
**Channels 1 and 2 are configured from the Settings application. Channel URLs and metadata parameters can be modified.
=== Viewability testing ===
**Channels starting from 3 are configured in specific JSON file. URLs to this JSON config file can be changed from the Settings application.
The sample applications shipped with the client package has support for viewability metrics verification. The player view can be moved using a finger-dragging gesture in the application window. This allows the tester to reduce the visible area of the player view and check the resulting data. There are additional UI buttons:
*Cover View button opens a popup with multiple UI controls which allow changing a player-view alpha and/or visibility as well to show a cover view in front of the player view.
*Alert button to open a native UI alert in the application.<br/>
[[File:PlayerView AlertView1.png|200px]]&nbsp;&nbsp;&nbsp;&nbsp;[[File:PlayerViewDragging.png|200px]]&nbsp;&nbsp;&nbsp;&nbsp;[[File:PlayerView CustomView.png|200px]]


== Nielsen Privacy Requirements ==
== Nielsen Privacy Requirements ==
There are three primary methods for implementing user Opt-out preferences:
# [[Template:iOS_Privacy_and_Opt-Out#OS-level_Opt-out|OS-level Opt-out]] - managed by ''Limit Ad Tracking'' setting on device ('''preferred approach''').
# [[Template:iOS_Privacy_and_Opt-Out#Legacy_Opt-out|Legacy Opt-out]] - Direct call to SDK; used only for older versions of Nielsen iOS SDK (< 5.1.1.18)
# [https://engineeringportal.nielsen.com/docs/iOS_SDK_App_Level_Opt_Out App Level Opt-Out] - Where [https://developer.apple.com/documentation/adsupport Ad Framework] cannot be leveraged
=== Privacy Protections ===
Privacy protections that Nielsen ensures to have with each App SDK integration are as follows.
Privacy protections that Nielsen ensures to have with each App SDK integration are as follows.
*Disclosure of viewership data collection in app store description
*Disclosure of viewership data collection in EULA / Privacy Policy
*Disclosure of viewership data collection in EULA / Privacy Policy
*A link in the EULA/Privacy policy, or in another conspicuous location within the App, to a Nielsen-hosted web page outlining what Nielsen is collecting and how it is being used
*A link in the EULA/Privacy policy, or in another conspicuous location within the App, to a Nielsen-hosted web page outlining what Nielsen is collecting and how it is being used
Line 660: Line 608:
* Cache Buster
* Cache Buster


=== Nielsen Measurement Opt-Out ===
=== TVOS Opt-out ===
In accordance with Nielsen’s SDK licensing agreement, developers are required to provide basic informational data to users about Nielsen’s Privacy Policy with a navigation to additional information on Nielsen measurement.
To Opt-Out, users must have access to “About Nielsen Measurement” page.


Nielsen SDK (5.1.1.17 and above) uses Limit Ad Tracking in the latest OS versions. For older OS versions (without LAT), the app must provide a means for the user to opt out, or opt back in to Nielsen Measurement. Users can opt out of Nielsen online measurement research through this feature.
TVOS does not support creating instances of UIWebView and display web pages. Instead it makes use of a TVML page (built on TVML template) which displays information in a specific order.
* If the user has this app running on more than one mobile device, the user will need to opt out of this measurement on each device.
<blockquote>'''Note''': Opt Out feature does NOT rely on "Limit Ad Tracking" setting since Nielsen’s systems provide measurement metrics and do not serve ads to users. The SDK will detect LAT and apply the logic without special code in your app. </blockquote>


In the Description of the app in App Store, include a short description about Nielsen measurement, as below.
Opt-Out page is built on descriptiveAlertTemplate and appears as follows:
 
[[File:TVOs OptOut LimitAdTracking1.png|link=]]
 
[[File:TVOs OptOut LimitAdTracking2.png|link=]]


<blockquote>'''Sample App Store Disclosure'''
This app features Nielsen proprietary measurement software which will allow you to contribute to market research, like Nielsen’s TV Ratings.


To learn more about our digital measurement products and your choices in regard to them, please visit http://www.nielsen.com/digitalprivacy for more information.
=== Opt-out wording ===
<blockquote>About Nielsen Measurement


[[File:measurement-samplescreen.jpg]]</blockquote>
YOUR CHOICES


Both iOS-based and TVOS-based player applications need to confirm to Nielsen Privacy Requirements. Refer to the Opt-Out implementation guidelines for iOS and TVOS platforms respectively for more details.
Television and the way we watch it have come a long way since Nielsen began measuring TV audiences in 1950. Today, the ability to watch programs at any time and on multiple devices amplifies the need for exceptionally adept and flexible audience measurement capabilities.
Consumers are changing with the times, and the same goes for Nielsen. As technology continues to evolve and media companies try new ways to attract viewers, understanding what consumers are watching — and what they're watching on — is more important than ever. Today, viewing video is a personal and mobile experience —anytime and anywhere. Our capabilities provide relevant metrics that are necessary to inform successful marketing and programming and drive continued growth.
As a global information and measurement leader, we are committed to protecting the privacy and security of the data we collect, process and use. While our digital measurement products are not used to identify you in any way, they help us and our clients measure and analyze how consumers engage with media across online, mobile and emerging technologies, and offer insights into consumer behavior.
Nielsen believes that you should have a choice about whether to contribute to our research and insights. To opt out of Nielsen measurement on this device, you need only to activate the “Limit Ad Tracking” option in your device's settings. If you have this app on more than one mobile device, you will need to change the settings on each device.
If, after you have opted out, you change your mind and would like to opt back in, please deactivate the “Limit Ad Tracking” option in your device's settings.
To learn more about our digital measurement products and your choices in regard to them, please visit https://www.nielsen.com/digitaIprivacy.</blockquote>


== AppApiEventCode ==
Users need to toggle the “Limit Ad Tracking” option in order to Opt-In / Opt-Out of Nielsen Measurement.
An enumeration with predefined App SDK event state transition codes.
<syntaxhighlight lang="objective-c">
typedef NS_ENUM(unsigned int, AppApiEventCode)
{
    AppApiStartup = 2001,
    AppApiShutdown = 2002,
}AppApiEventCode;
</syntaxhighlight>


=== App SDK Event Codes ===
To retrieve the current Opt-Out status of a device, use the [[optOutStatus]] method.
{| class="wikitable"
|-
! Event Code !! Event Name !! Event Description
|-
| 2001 || AppApiStartup || App SDK has initialized successfully. It will happen only after App SDK has received a valid config file
|-
| 2002 || AppApiShutdown || App SDK is shutting down. It will happen just before App SDK is destroyed
|}


== AppApiErrorCode ==
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.</blockquote>
iOS contains two types of error codes, 1-15 and 1001-1009.


For, 1-15, an enumeration with predefined error codes which the App SDK object can generate.
== Viewability and Audibility Implementation ==
<syntaxhighlight lang="objective-c">typedef NS_ENUM(unsigned int, LogCode) {
Viewability metrics allow AppSDK to track the visibility of the player and collect information about how much of the player container is visible to the end user during playback.  
    LogCodeFailedParseStartInfo, // 1.
    LogCodeFailedParseMetadata, // 2.
    LogCodeFailedProcessID3, // 3.
    LogCodeFailedReceiveConfig, // 4.
    LogCodeFailedParseConfig, // 5.
    LogCodeFailedStartProcessor, // 6.
    LogCodeFailedCreateUrl, // 7.
    LogCodeFailedCreateRequest, // 8.
    LogCodeFailedSendHttpRequest, // 9.
    LogCodeFailedSendPing, // 10.
    LogCodeFailedSendTSV, // 11.
    LogCodeFailedSendStationRequest, // 12.
    LogCodeFailedAccessDatabase, // 13.
    LogCodeException, // 14.
    LogCodeInvalidPlayheadPosition, // 15.
};</syntaxhighlight>


The viewability pings will be fired following the same rules as measurement pings. Viewability pings will be POST requests, not GET requests like other data pings. POST body for viewability requests will contain the key-value pairs in JSON format. The key parameters in the URL schemes are '''invs, inau, inss, invp''' and '''ines''' which will contain the collected viewability data. This data will be formatted according to the specific rules so that downstream it will be possible to match measurement and viewability data for a session.


For, 1001-1009, an enumeration with predefined error codes which the App SDK object can generate.
Audibility metrics will capture the volume level as well as mute/unmute state of the device during playback.
<syntaxhighlight lang="objective-c">
typedef NS_ENUM(unsigned int, AppApiErrorCode)
{
  AppApiNetworkConnectionFailure = 1001,
  AppApiFileWriteFailure = 1002,
  AppApiFileReadFailure = 1003,
  AppApiEmptyValue = 1004,
  AppApiEmptyAppName = 1005,
  AppApiEmptyAppVersion = 1006,
  AppApiEmptyAppId = 1007,
  AppApiAnExceptionOccured = 1008,
  AppApiUnknownExceptionOccured = 1009
};
</syntaxhighlight>


=== App SDK Error Codes ===
=== Data Collected ===
{| class="wikitable"
{| class="wikitable"
|-
|-
! Error Code !! Error Name !! Error Description
! Parameter !! Description
|-
|-
| 1 || LogCodeFailedParseStartInfo || Failed to parse the play() JSON string
| Measured Value || Value is different for different request parameters:
|-
{| class="wikitable"
| 2 || LogCodeFailedParseMetadata || Failed to parse the loadMetadata() JSON string
|-
|-
| 3 || LogCodeFailedProcessID3 || Failed to process ID3 data on a data processor
| '''invs''' || Intersection ratio for the target view in percent (from 0 to 100). Default threshold for this value is 5. Example: ['''50''',1,1528457356,10]
|-
|-
| 4 || LogCodeFailedReceiveConfig || Failed to receive configuration file from Census
| '''inau''' || Volume level on the device in percent (from 0 to 100), where 0 - mute, 100 - max volume level. Default threshold for this value is 1. Example: ['''30''',1,1528457356,10]
|-
|-
| 5 || LogCodeFailedParseConfig || Failed to parse the config file JSON string
| '''inss''' || Device screen size as "WxH", where W - is width in pixels, H - is height in pixels. Example: ['''"1024x768"''',1,1528457356,10]
|-
|-
| 6 || LogCodeFailedStartProcessor || Failed to create SDK processor
| '''invp''' || Current window size. This is different than the device screen size in a multiple scene mode or on a desktop. Format is "WxH", where W - is width in pixels, H - is height in pixels. Example: ['''"800x600"''',1,1528457356,10]
|-
|-
| 7 || LogCodeFailedCreateUrl || Failed to generate URL due to missing mandatory parameter
| '''ines''' || Player view size as "WxH", where W - is width in pixels, H - is height in pixels. Example: ['''"300x200"''',1,1528457356,10]
|-
|}
| 8 || LogCodeFailedCreateRequest || Failed to create request in HTTP client
|-
| 9 || LogCodeFailedSendHttpRequest || Failed sending HTTP or HTTPS request
|-
|-
| 10 || LogCodeFailedSendPing || Failed to send ping
| Start offset || Value contains the first playhead or the first id3 offset with non-null CID after start, flush or resume. Example playhead: [50,'''1''',1528457356,10]. Example id3 offset: [50,'''70100''',1528457356,10]
|-
|-
| 11 || LogCodeFailedSendTSV || Failed to send TSV request
| Start timestamp || Timestamp value when the time period related to this time series item was started. Example: [50,1,'''1528457356''',10]
|-
|-
| 12 || LogCodeFailedSendStationRequest || Failed to send StationId request
| Duration || Duration value is calculated as a difference between the last playhead and the first playhead for the current time series item. Example: [50,1,1528457356,'''10''']
|-
| 13 || LogCodeFailedAccessDatabase || Failed to read/write from/to database table
|-
| 14 || LogCodeException || Any exception handled by SDK code
|-
| 15 || LogCodeInvalidPlayheadPosition || Invalid playhead position
|}
|}


{| class="wikitable"
Viewability support requires additional parameters to be provided from player applications to the SDK. In order to provide these parameters and to start the viewability measurement, the following method has been added to the public SDK API (please refer to the [[trackViewability|trackViewability public API reference]] for details and usage examples):
|-
<br/><br/>
! Error Code !! Error Name !! Error Description
'''Objective C'''
|-
<syntaxhighlight lang="objective-c">
| 1001 || AppApiNetworkConnectionFailure || App SDK Could not connect to server
- (void)trackViewability:(nonnull NSDictionary *)data;
|-
</syntaxhighlight>
| 1002 || AppApiFileWriteFailure || App SDK Could not write to file
<br/>
|-
'''Swift'''
| 1003 || AppApiFileReadFailure || App SDK Could not read data from file
<syntaxhighlight lang="swift">
|-
func trackViewability(_ data: [String : Any])
| 1004 || AppApiEmptyValue || Empty value Found.
</syntaxhighlight>
|-
 
| 1005 || AppApiEmptyAppName || Cannot initialize SDK Object without an AppName(Player Name)
<br/>
|-
For Audibility measurement SDK uses iOS system API in order to get the volume level for the device:
| 1006 || AppApiEmptyAppVersion || Cannot initialize API Object without an AppVersion
<syntaxhighlight lang="swift">
|-
AVAudioSession.sharedInstance().outputVolume
| 1007 || AppApiEmptyAppId || Cannot initialize API Object without an AppId
</syntaxhighlight>
|-
| 1008 || AppApiAnExceptionOccured || Exception occurred
|-
| 1009 || AppApiUnknownExceptionOccured || Unknown exception occurred
|}

Latest revision as of 18:38, 4 April 2023

Engineering Portal / Digital / iOS SDK API Reference

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), and Digital Ad Ratings (DAR). 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

Prerequisites

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

Item Description Source
App ID (appid) Unique ID assigned to the player/site and configured by product. Contact Nielsen
sfcode Environment that the SDK must point to Contact Nielsen
Nielsen SDK Includes SDK frameworks and sample implementation; See iOS SDK Release Notes Download

If you do not have any of these pre-requisites or if you have any questions, please contact our SDK sales support team. Refer to Digital Measurement Onboarding guide for information on how to get a Nielsen App SDK and appid.

SDK Implementation

Information on how to obtain, how to configure your development environment, and how to Initialize the Nielsen SDK is located in either the DCR Implementation Guide, or the DTVR Implementation Guide depending on your requirements.

_

_

Nielsen iOS App SDK Application Life Cycle

initialization appcycle.png

Life cycle of SDK instance includes four general states:

  1. Initial state – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the Idle state.
  2. Idle state – The SDK is initialized and is ready to process playing information. Once Initialized, the SDK instance is not processing any data, but is listening for an event to occur.
  3. Processing state – The SDK instance is processing playing information. The play and loadMetadata calls move the SDK instance into this state. In this state, the SDK instance will be able to process the following calls.
    1. playheadPosition – Call this API every one second when playhead position is active. If a LIVE event, use the current UNIX timestamp (seconds since Jan-1-1970 UTC).
    2. stop – Call this API when the playback is paused, switches between content and ad (within the same content playback) or encounters interruptions.
    3. end – SDK instance exits from Processing state when this API is called.
  4. Disabled state – The SDK instance is disabled and is not processing playing information.
    1. appDisableApi is set to true

Note: For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.

Note: In case of any interruptions during playback due to alarm, calendar, call, flight mode, Wi-Fi toggle, channel change, etc., call stop to stop the measurement.

  • As soon as the playback resumes, call play, loadMetadata and playheadPosition


icon

Finite-state machine table

This table provides the possible changes of state for the SDK instance, when it is in a specific state and receives an API call.

API call received Initial State Idle State Processing State Disabled State
initWithAppInfo:delegate: IDLE STATE (OR)

DISABLED STATE

IDLE STATE - -
play & loadMetadata - PROCESSING

STATE

- -
playheadPosition - - PROCESSING

STATE

-
sendID3 - - PROCESSING

STATE

-
stop - - IDLE STATE -
end - - IDLE STATE -
appDisableApi: YES - DISABLED

STATE

- -
appDisableApi: NO - - - IDLE STATE (OR)

DISABLED STATE

userOptOut: YES - - IDLE STATE -
userOptOut: NO - PROCESSING

STATE

- -
'-' indicates that no API call is expected.

Handling JSON Metadata

All the SDK methods handles only two types of objects: NSString, NSDictionary. The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.

  • NSDictionary object
    • If an object of unexpected type is passed to the method, the error message will be logged.
    • If string has invalid JSON format, the error message will be logged.
  • JSON value must be string value.
    • This includes boolean and numeric values. For example, a value of true should be represented with "true", number value 123 should be "123".
    • All the Variable Names like appid, appname, sfcode, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
  • JSON string can be prepared using either raw NSString or serialized NSDictionary.

The below is a sample - detailed information on the metadata requirements are located in either the DCR Implementation Guide, or the DTVR Implementation Guide

Swift

let contentMetadata = [
    "type": "content",
    "assetid": "C77664",
    "title": "Program S2, E3",
    "isfullepisode": "Yes",
    "program": "Program Name",
    "length": "3600",
    "airdate": "20171020 10:05:00",
    "adloadtype": "2",
    "segB": "CustomSegmentValueB", //optional
    "segC": "CustomSegmentValueC", //optional
];

Objective C

 
NSDictionary * contentMetadata = @ {
    @ "type": @ "content",
        @ "assetid": @ "C77664",
        @ "title": @ "S2,E3",
        @ "isfullepisode": @ "y",  
        @ "program": @ "Program Name",
        @ "length": @ "3600",
        @ "airdate": @ "20180120 10:00:00",
        @ "adloadtype": @ "2",
        @ "segB": @ "CustomSegmentValueB", //optional
        @ "segC": @ "CustomSegmentValueC", //optional
}


Retrieving ID3 Tags

Only required for DTVR clients
Not applicable for German Clients

ID3 tags have a payload of about 249 characters and start with "www.nielsen.com".

ID3 tags are extracted by observing a property called timedMetadata on the iOS player item. Now this is done via a concept called KVO (Key Value Observing), where you register interest in a property, and the runtime will let you know when it has changed.

Both the iOS native players have the ability to extract ID3 tags, If any other player apart from iOS native players (AVPlayer, MPMoviePlayer) is used, check and ensure that the player has the capability to extract ID3 tags.

Examples of extracting ID3 tags from the iOS Native Player

Swift

 
      //Setting observer to track timedMetadata
            player.addObserver(self, forKeyPath: timedMetadataKey, options: NSKeyValueObservingOptions.new, context: &TimedMetadataObserverContext)

 
   override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        if keyPath == timedMetadataKey {
            if(context == &TimedMetadataObserverContext){
                if change != nil {
                    let timedMetadataArray = change![.newKey]
                    if timedMetadataArray != nil && (timedMetadataArray! as AnyObject) is Array<Any> {
                        for item in timedMetadataArray as! [AVMetadataItem]  {
                            //Handling TimedMetadata
                            self.handleTimedMetadata(metadataItem: item)
                        }
                    }
                }
            }
        }

 
 func handleTimedMetadata(metadataItem: AVMetadataItem) {
        guard let extraAttributeType = metadataItem.extraAttributes else {
            return
        }
        let info : AVMetadataExtraAttributeKey = AVMetadataExtraAttributeKey(rawValue: "info")
        let extraString = extraAttributeType[info] as AnyObject
        let key = metadataItem.key as! String
        
        //If tag starts with "www.nielsen.com", then only sending to SDK
        if key == "PRIV" && extraString.range(of: "www.nielsen.com").length > 0 {
            
            DispatchQueue.global(qos: .default).async { () -> Void in
                self.nielsenApi?.sendID3(extraString as! String)
            }
        }
    }

Objective C

    //Adding observer to player to track play,pause and reverse
    [player addObserver:self
             forKeyPath:@"rate"
                options:(NSKeyValueObservingOptionNew)
                context:nil];

       
        //Setting observer to track timedMetadata
        [player addObserver:self
                 forKeyPath: timedMetadataKey
                    options: (NSKeyValueObservingOptionNew)
                    context: &TimedMetadataObserverContext];

     
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if(keyPath == timedMetadataKey){
        if(context == &TimedMetadataObserverContext){
            
            id newMetadataArray = [change objectForKey:NSKeyValueChangeNewKey];
            if (newMetadataArray != [NSNull null])
            {
                array = newMetadataArray;
                for (AVMetadataItem *metadataItem in array)
                {
                    //Handling TimedMetadata
                    [self handleTimedMetadata: metadataItem];
                }
            }
            
        }
    }

     
- (void)handleTimedMetadata:(AVMetadataItem *)timedMetadata
{
    // We expect the content to contain plists encoded as timed metadata
    // AVPlayer turns these into NSDictionaries
    
    id extraAttributeType = [timedMetadata extraAttributes];
    NSString *extraString = nil;
    if ([extraAttributeType isKindOfClass:[NSDictionary class]])
    {
        extraString = [extraAttributeType valueForKey:@"info"];
    }
    else if ([extraAttributeType isKindOfClass:[NSString class]])
    {
        extraString = extraAttributeType;
    }
    
    NSString *key = [NSString stringWithFormat:@"%@", [timedMetadata key]];
    
    //If tag starts with "www.nielsen.com", then only sending to SDK
    if ([key isEqualToString:@"PRIV"] && [extraString rangeOfString:@"www.nielsen.com"].length > 0)
    {
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [nielsenApi sendID3:extraString];
        });
    }
}


Note: ID3 tags are not applicable for International (Germany)

IOS SDK API Methods & Properties

Scenario Method / Property DTVR DAR DCR International (Germany) Description
Initialize initWithAppInfo:delegate: Used to create a new instance of the SDK object
Measurement play Used when there is an ID3 fed product such as DTVR and the client does not want to send in all the CMS metadata that is sent in loadMetadata. This allows the client to send in at least the required “channel name” value associated to the ID3 feed. If this is not called then the “channel name” value populated will be the default value of “defaultChannelName”.
Measurement loadMetadata Used to send ad or content metadata to the SDK in the form of JSON string. Application constructs a JSON hashmap and calls this API.
Measurement sendID3 Used to send the ID3 metadata.
Measurement playheadPosition Used to send the playhead position.
Measurement stop Used when playback is paused and when switching between ad and content or content and ad.
Measurement end Used when content playback is complete. This is triggered 1) at the end of the content stream, 2) if the user switches to another piece of content
Measurement staticEnd Used with DCR Static duration in between loadMetadata calls for static content when section name is the same but a new static view event and duration measurement restart is desired
Measurement updateOTT Used to notify App SDK that the remote OTT device (like Google ChromeCast, Roku, Amazon FireTV, etc.) is connected / disconnected (change of OTT status).
Opt-out optOutURL Used to fetch the Nielsen opt-out web page URL.
Opt-out userOptOut Used to supply the response message from opt-out webpage to the SDK.
Opt-out optOutStatus Call this API to retrieve the Opt-Out or Opt-In state.
Opt-out appDisableApi

(kill switch)

Used to disable the SDK.
Viewability
Audibility
trackViewability Used to start the viewability measurement.
Log lastErrorDict Returns SDK error in the form of dictionary if any error has occurred.
Log lastEventDict Returns SDK event in the form of dictionary if any event has occurred.
Log meterVersion Returns the current SDK version.
Log nielsenId Used to get a string defining the Nielsen ID (NUID) number for the device.
Log demographicId Used to retrieve Demographic ID (Device ID) of the current device.
Log debug Used to enable/disable debug flags. Newly introduced in SDK version 5.0.0 for International (Germany)

NielsenAppApi Class Description

The NielsenAppApi class is the primary application interface to the Nielsen App SDK. For example, after an instance object of the NielsenAppApi class is created and initialized, it can be used by the calling application to collect HLS timed metadata using the SDK’s sendID3: method. These are the public methods and properties exposed by the NielsenAppApi class:

@interface NielsenAppApi: NSObject

  @property (readonly) BOOL optOutStatus;
  @property (assign) BOOL appDisableApi;
  @property (assign) BOOL debug;
  @property (readonly, nonnull) NSString *nielsenId;
  @property (readonly, nonnull) NSString *demographicId;
  @property (readonly, nonnull) NSString *firstPartyId;
  @property (readonly, nonnull) NSString *vendorId;
  @property (readonly, nonnull) NSString *optOutURL;
  @property (readonly, nullable) NSString *meterVersion;
  @property (readonly, nullable) NSDictionary *lastEventDict;
  @property (readonly, nullable) NSDictionary *lastErrorDict;

  - (nullable instancetype)initWithAppInfo:(nonnull id)appInfo delegate:(nullable id<NielsenAppApiDelegate>)delegate

  - (void)play:(nullable id)channelInfo;
  - (void)loadMetadata:(nullable id)metadata;
   (void)stop;
   (void)end;
  - (void)playheadPosition:(long long)playheadPos;
  - (void)sendID3:(nonnull NSString *)data;
  - (void)updateOTT:(nonnull id)ottInfo;
  - (BOOL)userOptOut:(nonnull NSString *)optOut;
  - (void)trackViewability:(nonnull NSDictionary *)data;

  - (nonnull NSString *)getNielsenId __attribute((deprecated(("nielsenId is not used by the SDK anymore"))));
  - (nonnull NSString *)optOutURLString __attribute((deprecated(("Please use optOutURL property instead."))));
  - (nullable NSString *)getMeterVersion __attribute((deprecated(("Please use meterVersion property instead."))));
  - (nullable NSDictionary *)getLastEventDict __attribute((deprecated(("Please use lastEventDict property instead."))));
  - (nullable NSDictionary *)getLastErrorDict __attribute((deprecated(("Please use lastErrorDict property instead.")))); 

  @protocol NielsenAppApiDelegate <NSObject>

  @optional
  - (void)nielsenAppApi:(nonnull NielsenAppApi *)appApi eventOccurred:(nonnull NSDictionary *)event;
  - (void)nielsenAppApi:(nonnull NielsenAppApi *)appApi errorOccurred:(nonnull NSDictionary *)error;

@end

AppApiEventCode

An enumeration with predefined App SDK event state transition codes.

typedef NS_ENUM(unsigned int, AppApiEventCode)
{
     AppApiStartup = 2001,
     AppApiShutdown = 2002,
}AppApiEventCode;

App SDK Event Codes

Event Code Event Name Event Description
2001 AppApiStartup App SDK has initialized successfully. It will happen only after App SDK has received a valid config file
2002 AppApiShutdown App SDK is shutting down. It will happen just before App SDK is destroyed

AppApiErrorCode

iOS contains two types of error codes, 1-15 and 1001-1009.

For, 1-15, an enumeration with predefined error codes which the App SDK object can generate.

typedef NS_ENUM(unsigned int, LogCode) {
    LogCodeFailedParseStartInfo, // 1.
    LogCodeFailedParseMetadata, // 2.
    LogCodeFailedProcessID3, // 3.
    LogCodeFailedReceiveConfig, // 4.
    LogCodeFailedParseConfig, // 5.
    LogCodeFailedStartProcessor, // 6.
    LogCodeFailedCreateUrl, // 7.
    LogCodeFailedCreateRequest, // 8.
    LogCodeFailedSendHttpRequest, // 9.
    LogCodeFailedSendPing, // 10.
    LogCodeFailedSendTSV, // 11.
    LogCodeFailedSendStationRequest, // 12.
    LogCodeFailedAccessDatabase, // 13.
    LogCodeException, // 14.
    LogCodeInvalidPlayheadPosition, // 15.
    LogCodeLongAd, // 16.
    LogCodeIncorrectSfcode, // 17.
    LogCodeHemUidExceedLimit, // 18.
    LogCodeViewabilityUnableFindView // 19.
};


For, 1001-1009, an enumeration with predefined error codes which the App SDK object can generate.

typedef NS_ENUM(unsigned int, AppApiErrorCode)
{
   AppApiNetworkConnectionFailure = 1001,
   AppApiFileWriteFailure = 1002,
   AppApiFileReadFailure = 1003,
   AppApiEmptyValue = 1004,
   AppApiEmptyAppName = 1005,
   AppApiEmptyAppVersion = 1006,
   AppApiEmptyAppId = 1007,
   AppApiAnExceptionOccured = 1008,
   AppApiUnknownExceptionOccured = 1009
};

App SDK Error Codes

Error Code Error Name Error Description
1 LogCodeFailedParseStartInfo Failed to parse the play() JSON string
2 LogCodeFailedParseMetadata Failed to parse the loadMetadata() JSON string
3 LogCodeFailedProcessID3 Failed to process ID3 data on a data processor
4 LogCodeFailedReceiveConfig Failed to receive configuration file from Census
5 LogCodeFailedParseConfig Failed to parse the config file JSON string
6 LogCodeFailedStartProcessor Failed to create SDK processor
7 LogCodeFailedCreateUrl Failed to generate URL due to missing mandatory parameter
8 LogCodeFailedCreateRequest Failed to create request in HTTP client
9 LogCodeFailedSendHttpRequest Failed sending HTTP or HTTPS request
10 LogCodeFailedSendPing Failed to send ping
11 LogCodeFailedSendTSV Failed to send TSV request
12 LogCodeFailedSendStationRequest Failed to send StationId request
13 LogCodeFailedAccessDatabase Failed to read/write from/to database table
14 LogCodeException Any exception handled by SDK code
15 LogCodeInvalidPlayheadPosition Invalid playhead position
16 LogCodeLongAd Long ad
17 LogCodeIncorrectSfcode Incorrect client supplied sfcode
18 LogCodeHemUidExceedLimit Exceeded limit of chars for hem uid
19 LogCodeViewabilityUnableFindView Viewability unable to find a view with the specificed tag
Error Code Error Name Error Description
1001 AppApiNetworkConnectionFailure App SDK Could not connect to server
1002 AppApiFileWriteFailure App SDK Could not write to file
1003 AppApiFileReadFailure App SDK Could not read data from file
1004 AppApiEmptyValue Empty value Found.
1005 AppApiEmptyAppName Cannot initialize SDK Object without an AppName(Player Name)
1006 AppApiEmptyAppVersion Cannot initialize API Object without an AppVersion
1007 AppApiEmptyAppId Cannot initialize API Object without an AppId
1008 AppApiAnExceptionOccured Exception occurred
1009 AppApiUnknownExceptionOccured Unknown exception occurred

Nielsen Sample Applications

Nielsen SDK client package contains iOS sample player applications based on native Players integrated with SDK framework. The players demonstrate all the supported functions of the SDK and show the integration details for both legacy and trackEvent API. There are players developed on Objective C and Swift. Implementation of the sample apps is based on native iOS AVPlayer.

The UI components of the iOS App SDK sample applications are common to both as shown below.

  • From the Channel Selection buttons ⬇️ & ⬆️ , the user will be able select the channels to stream.
  • The Info button ℹ️ displays information of the SDK version, current Nielsen ID used for the device, and the option for opt-out/opt-in.
  • The Play ▶️ and Pause ⏸️ buttons will control the streaming of the selected channel.
  • The area at the bottom of the window displays the current stream status.
  • The Clear button 🔃 clears out the status window.
  • The Email button 📧 can be used to email the tags and status in a text file to Nielsen.

If target device supports Picture-in-Picture playing, it could be activated by PIP button in the Video player window.

  • Channel URLs and metadata are obtained from the JSON file named appConfig.json and stored in the application bundle.

Viewability testing

The sample applications shipped with the client package has support for viewability metrics verification. The player view can be moved using a finger-dragging gesture in the application window. This allows the tester to reduce the visible area of the player view and check the resulting data. There are additional UI buttons:

  • Cover View button opens a popup with multiple UI controls which allow changing a player-view alpha and/or visibility as well to show a cover view in front of the player view.
  • Alert button to open a native UI alert in the application.

PlayerView AlertView1.png    PlayerViewDragging.png    PlayerView CustomView.png

Nielsen Privacy Requirements

There are three primary methods for implementing user Opt-out preferences:

  1. OS-level Opt-out - managed by Limit Ad Tracking setting on device (preferred approach).
  2. Legacy Opt-out - Direct call to SDK; used only for older versions of Nielsen iOS SDK (< 5.1.1.18)
  3. App Level Opt-Out - Where Ad Framework cannot be leveraged

Privacy Protections

Privacy protections that Nielsen ensures to have with each App SDK integration are as follows.

  • Disclosure of viewership data collection in EULA / Privacy Policy
  • A link in the EULA/Privacy policy, or in another conspicuous location within the App, to a Nielsen-hosted web page outlining what Nielsen is collecting and how it is being used
  • Method for users to opt-out of Nielsen measurement, any time while using the application

Ratings Data Flow

Every view of creditable and watermarked content is measured by Nielsen. RatingsDataFlow.png

Information NOT Shared

  • With Nielsen
    • User's Identity
  • With Data Provider
    • Content information
    • Whether user is viewing an ad or video content
    • Player used to play the streaming (audio / video, etc.)
    • Values being de-duped / aggregating for

Nielsen collects only what it needs for audience measurement. Every view of creditable, watermarked content will be measured by Nielsen.

Data Collected

Type of Information Parameter Transmitted to Nielsen? Sent to Provider?
Nielsen ID3 Watermark
FinalDistributor Timestamp Yes No
Program Content Timestamp Yes No
Mobile Breakout Code Yes No
Commercial Credit Code – Linear or Dynamic Yes No
Time ShiftedViewing Code Yes No
Segment Number Yes No
Segment View Pattern Yes No
Device/App Info
Device OSVersion Yes Yes
Device Model Yes No
Device Advertiser ID (Apple IDFA or Google AdID/Android ID) Yes Yes
Cache Buster Yes Yes
App Version Yes No
App Name Yes No
SDKDisabled Flag Yes No
ServerCode Yes No
Channel or URL Yes No
Nielsen Identifiers
Client ID Yes No
Campaign ID Yes Yes
Nielsen Unique Device ID Yes No
Application ID Yes No
DeviceGroup (ex. Tablet, Smartphone, Desktop) Yes Yes
OS Group (ex. Android, iOS, Windows) Yes Yes
SDKVersion Yes No
IP Address for DMA, Country Code Yes Yes

Note: Data is hashed, and encrypted using AES 128 before transmission to data provider.

Example ping sent to provider

  • https://provider.com/cgi-bin/brandlift.php?campaign_id=ff12725d724fac7934cf6003f096b4cd&placement_id=a4164b8fba9ee7c873a9c72c7091bb58&creative_id=25280139b61a947e127a52f56c8a2fdd&segment1=9000&segment2=41&segment3=iOS&OSVer=iOS6.1&c9=&devgrp=tablet&h=f5f243fe6d&rnd=1376971827360


This ping passes the following parameters to the provider:

  • Campaign ID – (campaign, placement, creative)
  • Country Code
  • DMA
  • OS Group (ex. iOS, Android)
  • DeviceOS Version
  • Device Advertiser ID
  • DeviceGroup (ex. Tablet, Smartphone, Desktop)
  • Cache Buster

TVOS Opt-out

To Opt-Out, users must have access to “About Nielsen Measurement” page.

TVOS does not support creating instances of UIWebView and display web pages. Instead it makes use of a TVML page (built on TVML template) which displays information in a specific order.

Opt-Out page is built on descriptiveAlertTemplate and appears as follows:

TVOs OptOut LimitAdTracking1.png

TVOs OptOut LimitAdTracking2.png


Opt-out wording

About Nielsen Measurement

YOUR CHOICES

Television and the way we watch it have come a long way since Nielsen began measuring TV audiences in 1950. Today, the ability to watch programs at any time and on multiple devices amplifies the need for exceptionally adept and flexible audience measurement capabilities. Consumers are changing with the times, and the same goes for Nielsen. As technology continues to evolve and media companies try new ways to attract viewers, understanding what consumers are watching — and what they're watching on — is more important than ever. Today, viewing video is a personal and mobile experience —anytime and anywhere. Our capabilities provide relevant metrics that are necessary to inform successful marketing and programming and drive continued growth. As a global information and measurement leader, we are committed to protecting the privacy and security of the data we collect, process and use. While our digital measurement products are not used to identify you in any way, they help us and our clients measure and analyze how consumers engage with media across online, mobile and emerging technologies, and offer insights into consumer behavior. Nielsen believes that you should have a choice about whether to contribute to our research and insights. To opt out of Nielsen measurement on this device, you need only to activate the “Limit Ad Tracking” option in your device's settings. If you have this app on more than one mobile device, you will need to change the settings on each device. If, after you have opted out, you change your mind and would like to opt back in, please deactivate the “Limit Ad Tracking” option in your device's settings.

To learn more about our digital measurement products and your choices in regard to them, please visit https://www.nielsen.com/digitaIprivacy.

Users need to toggle the “Limit Ad Tracking” option in order to Opt-In / Opt-Out of Nielsen Measurement.

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

Note: For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.

Viewability and Audibility Implementation

Viewability metrics allow AppSDK to track the visibility of the player and collect information about how much of the player container is visible to the end user during playback.

The viewability pings will be fired following the same rules as measurement pings. Viewability pings will be POST requests, not GET requests like other data pings. POST body for viewability requests will contain the key-value pairs in JSON format. The key parameters in the URL schemes are invs, inau, inss, invp and ines which will contain the collected viewability data. This data will be formatted according to the specific rules so that downstream it will be possible to match measurement and viewability data for a session.

Audibility metrics will capture the volume level as well as mute/unmute state of the device during playback.

Data Collected

Parameter Description
Measured Value Value is different for different request parameters:
invs Intersection ratio for the target view in percent (from 0 to 100). Default threshold for this value is 5. Example: [50,1,1528457356,10]
inau Volume level on the device in percent (from 0 to 100), where 0 - mute, 100 - max volume level. Default threshold for this value is 1. Example: [30,1,1528457356,10]
inss Device screen size as "WxH", where W - is width in pixels, H - is height in pixels. Example: ["1024x768",1,1528457356,10]
invp Current window size. This is different than the device screen size in a multiple scene mode or on a desktop. Format is "WxH", where W - is width in pixels, H - is height in pixels. Example: ["800x600",1,1528457356,10]
ines Player view size as "WxH", where W - is width in pixels, H - is height in pixels. Example: ["300x200",1,1528457356,10]
Start offset Value contains the first playhead or the first id3 offset with non-null CID after start, flush or resume. Example playhead: [50,1,1528457356,10]. Example id3 offset: [50,70100,1528457356,10]
Start timestamp Timestamp value when the time period related to this time series item was started. Example: [50,1,1528457356,10]
Duration Duration value is calculated as a difference between the last playhead and the first playhead for the current time series item. Example: [50,1,1528457356,10]

Viewability support requires additional parameters to be provided from player applications to the SDK. In order to provide these parameters and to start the viewability measurement, the following method has been added to the public SDK API (please refer to the trackViewability public API reference for details and usage examples):

Objective C

- (void)trackViewability:(nonnull NSDictionary *)data;


Swift

func trackViewability(_ data: [String : Any])


For Audibility measurement SDK uses iOS system API in order to get the volume level for the device:

AVAudioSession.sharedInstance().outputVolume