Skip to content

Integration Flow ​

An iOS integration is split between Infillion and the publisher. Infillion provides the VAST tag and renders the interactive ad. The publisher's app finds the Infillion ad in its ad break, hands it to TruexAdRenderer, and moves the playback timeline forward when the renderer is done.

  1. Infillion provides a test VAST tag

    Your Infillion contact provides a test VAST tag that fits your ad setup. The generic tag is the preferred option. The companion tag is used when it works better with your ad stack. Both formats are described in VAST Tag Types.

  2. The publisher traffics the tag

    Your ad ops team schedules the tag in your ad server. A TrueX tag must be the first ad in the pod. An IDVx tag can be placed in any slot.

  3. The publisher integrates the SDK

    Your app resolves adParameters from the ad, starts the renderer when the ad plays, and handles the terminal events.

Resolving adParameters ​

The renderer is initialized with an adParameters dictionary. Where that JSON comes from depends on the VAST tag format and the ad framework your app uses:

Tag formatWhere adParameters is in the VASTGoogle IMA (CSAI and DAI)Custom VAST parsing
Generic<Linear><AdParameters> as a JSON stringad.traffickingParametersText content of <AdParameters>
Companion<Companion apiFramework="truex"><StaticResource creativeType="application/json"> as a base64 JSON data: URLUse the generic tag with Google IMAText content of <StaticResource>, base64-decoded after the comma

First, identify the ad by its <AdSystem> value (ad.adSystem in Google IMA), compared case-insensitively. trueX is a TrueX ad and IDVx is an IDVx ad; any other value is a regular linear ad. A TrueX ad that is not the first ad in the pod should play as a regular linear ad.

To support both formats, resolve the payload in this order:

  1. Look for a companion with apiFramework="truex" and decode its JSON.
  2. If there is none, parse the <AdParameters> JSON.
  3. If neither one gives a JSON object, do not start the renderer. Treat the ad as a normal linear ad and continue the pod.

The companion decoding, adapted from the examples (ManualVastPayload):

swift
/// Decodes a `data:application/json;base64,...` URL. Any other resource is read as plain JSON.
func companionAdParameters(_ resource: String) -> [String: Any]? {
    let trimmed = resource.trimmingCharacters(in: .whitespacesAndNewlines)
    guard trimmed.lowercased().hasPrefix("data:") else {
        return jsonObject(trimmed)
    }
    // The ad server wraps the base64 payload across lines, so whitespace is removed before decoding.
    let compact = trimmed.filter { !$0.isWhitespace }
    guard let comma = compact.firstIndex(of: ","),
        let decoded = Data(base64Encoded: String(compact[compact.index(after: comma)...]))
    else {
        return nil
    }
    return (try? JSONSerialization.jsonObject(with: decoded)) as? [String: Any]
}

/// Parses `<AdParameters>` (or IMA's `traffickingParameters`). Returns nil unless it is a JSON object.
func jsonObject(_ text: String) -> [String: Any]? {
    guard let json = text.trimmingCharacters(in: .whitespacesAndNewlines).data(using: .utf8) else {
        return nil
    }
    return (try? JSONSerialization.jsonObject(with: json)) as? [String: Any]
}
objective-c
// Parses `<AdParameters>` (or IMA's `traffickingParameters`). Returns nil unless it is a JSON object.
static NSDictionary *JsonObject(NSString *text) {
    NSData *json = [[text stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]
        dataUsingEncoding:NSUTF8StringEncoding];
    if (json.length == 0) {
        return nil;
    }
    id object = [NSJSONSerialization JSONObjectWithData:json options:0 error:nil];
    return [object isKindOfClass:[NSDictionary class]] ? object : nil;
}

// Decodes a `data:application/json;base64,...` URL. Any other resource is read as plain JSON.
+ (NSDictionary *)companionAdParameters:(NSString *)resource {
    NSString *trimmed = [resource stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
    if (![trimmed.lowercaseString hasPrefix:@"data:"]) {
        return JsonObject(trimmed);
    }
    // The ad server wraps the base64 payload across lines, so whitespace is removed before decoding.
    NSString *compact = [[trimmed componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]
        componentsJoinedByString:@""];
    NSRange comma = [compact rangeOfString:@","];
    if (comma.location == NSNotFound) {
        return nil;
    }
    NSData *decoded = [[NSData alloc] initWithBase64EncodedString:[compact substringFromIndex:NSMaxRange(comma)]
                                                          options:0];
    if (!decoded) {
        return nil;
    }
    id object = [NSJSONSerialization JSONObjectWithData:decoded options:0 error:nil];
    return [object isKindOfClass:[NSDictionary class]] ? object : nil;
}

With Google IMA, pass ad.traffickingParameters to jsonObject (Objective-C: JsonObject). The examples contain the complete parsers, including the VAST XML parsing for a custom ad layer.

Starting the Renderer ​

When the Infillion ad starts playing:

  1. Pause the content player and the ad playback (for Google IMA CSAI, also pause the IMAAdsManager).
  2. Prepare to move past the placeholder ad video:
    • CSAI and custom ad layers: seek the placeholder to just before its end, so it finishes right away when the ad playback resumes.
    • SSAI: the placeholder is stitched into the stream, so remember where it ends (the current stream time plus ad.duration) to seek there later.
  3. Create a TruexAdRenderer with the adParameters dictionary and your delegate. If the renderer can't be created, there will be no delegate events: continue the pod.
  4. Call start with the full-screen view above your player.
  5. Wait for a terminal event. While you wait, keep track of whether onAdFreePod has been called.

The calls are described in SDK Integration.

Handling Terminal Events ​

A terminal event means the renderer is done, and your app must move the playback timeline forward. What "forward" means depends on whether the user earned the ad-free pod reward and on how your ads are inserted:

Renderer resultWhat to doGoogle IMA CSAIGoogle IMA DAI (SSAI)Custom ad layer
onAdCompleted after onAdFreePodSkip all remaining ads in the pod and continue the content[adsManager discardAdBreak], then play the contentSeek past the end of the current ad break's cue pointEnd the pod and restore the content
onAdCompleted without onAdFreePodSkip the Infillion placeholder ad and play the next ad in the pod[adsManager resume] - the placeholder finishes and IMA plays the next adSeek to just before the placeholder's end, so the next stitched ad playsPlay the next ad in the pod
onAdError or onNoAdsAvailableSame as onAdCompleted without onAdFreePodSameSameSame
onUserCancelStreamThe user closed the Infillion ad because they want to leave - exit the playback screenClose the playerClose the playerClose the player

onAdCompleted without onAdFreePod covers a TrueX user who opted out or let the choice card time out, and it is the normal outcome of every IDVx ad, because IDVx never calls onAdFreePod.

IMPORTANT

Do not seek or resume playback when onAdFreePod is called. Remember that it was called, and act only on the terminal event that follows.

After any terminal event, call stop and drop all references to the renderer instance. The full list of terminal events is on the Events page.

Integration documentation for Infillion Ads