Appearance
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.
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.
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.
The publisher integrates the SDK
Your app resolves
adParametersfrom 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 format | Where adParameters is in the VAST | Google IMA (CSAI and DAI) | Custom VAST parsing |
|---|---|---|---|
| Generic | <Linear><AdParameters> as a JSON string | ad.traffickingParameters | Text content of <AdParameters> |
| Companion | <Companion apiFramework="truex"><StaticResource creativeType="application/json"> as a base64 JSON data: URL | Use the generic tag with Google IMA | Text 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:
- Look for a companion with
apiFramework="truex"and decode its JSON. - If there is none, parse the
<AdParameters>JSON. - 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:
- Pause the content player and the ad playback (for Google IMA CSAI, also pause the
IMAAdsManager). - 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.
- Create a
TruexAdRendererwith theadParametersdictionary and your delegate. If the renderer can't be created, there will be no delegate events: continue the pod. - Call
startwith the full-screen view above your player. - Wait for a terminal event. While you wait, keep track of whether
onAdFreePodhas 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 result | What to do | Google IMA CSAI | Google IMA DAI (SSAI) | Custom ad layer |
|---|---|---|---|---|
onAdCompleted after onAdFreePod | Skip all remaining ads in the pod and continue the content | [adsManager discardAdBreak], then play the content | Seek past the end of the current ad break's cue point | End the pod and restore the content |
onAdCompleted without onAdFreePod | Skip the Infillion placeholder ad and play the next ad in the pod | [adsManager resume] - the placeholder finishes and IMA plays the next ad | Seek to just before the placeholder's end, so the next stitched ad plays | Play the next ad in the pod |
onAdError or onNoAdsAvailable | Same as onAdCompleted without onAdFreePod | Same | Same | Same |
onUserCancelStream | The user closed the Infillion ad because they want to leave - exit the playback screen | Close the player | Close the player | Close 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.

