Skip to content
Login Contact

Native iOS SDK instrumentation and tracking guide

The Marfeel SDK installation can be done via Swift Package Manager (SPM) or via Cocoapods.

If you’re using XCode, Swift Package Manager (SPM) is part of it since version 11. Follow the official documentation using https://github.com/Marfeel/MarfeelSDK-iOS as the package repository.

If you’re using a Package.swift manifest file, include MarfeelSDK as one of your project dependencies:

import PackageDescription
let package = Package(
name: "Sample",
dependencies: [
.Package(url: "https://github.com/Marfeel/MarfeelSDK-iOS", from: "2.18.8")
]
)

Include the following line in your Podfile:

pod 'MarfeelSDK-iOS', '~> 2.18.8'

To use the library, we offer two diffrent options for defining the configuration.

add the following properties within the Info.plist file ensuring your unique account ID is correctly added before /* AccountId */:

<key>COMPASS_ACCOUNT_ID</key>
<integer>/* AccountId */</integer>
<key>COMPASS_ENDPOINT</key>
<string>https://events.newsroom.bi/</string>
Simply update the /* AccountId */ placeholder with your Marfeel Account ID, and keep the rest of the code unchanged.

Call before any tracking the initialize method

func intialize(accountId: Int, pageTechnology: Int?, endpoint: String?)

For instance

import MarfeelSDK_iOS
CompassTracker.initialize([accountId: 0](https://hub.marfeel.com/compass/dashboard?accountId=0)) // just configuring accountId
// or
CompassTracker.initialize([accountId: 0](https://hub.marfeel.com/compass/dashboard?accountId=0), pageTechnology: 12345) // use it instead if you want to use a custom page technology
We also offer a sample playground application to demonstrate how our SDKs can be used. You can access the GitHub repositories here: iOS Playground Application

The default value for the Page Technology dimension is set to iOS App represented by the value 3 . However, you can modify this default value adding the following property to Info.plist file:

<key>COMPASS_PAGE_TYPE</key>
<integer>$$COMPASS_PAGE_TYPE_VALUE_TO_BE_REPLACED$$</integer>
Custom Page Technologies have IDs above 100 and must be declared on the Organization Settings

To utilize the library, import the MarfeelSDK_iOS module.

import MarfeelSDK_iOS

All tracking features are made available through a CompassTracking class. To access CompassTracker, use the shared variable from any part of the application

let tracker = CompassTracker.shared

CompassTracker automatically monitors user time on a page. When a user begins reading a new page, you should provide the page’s canonical URL.

tracker.trackNewPage(url: {URL})
tracker.trackNewPage(url: {URL}, rs: {String?})

In cases where you can’t provide the canonical URL you can use the Editorial Metadata API.

CompassTracker will continue tracking reading time until the trackNewPage method is called again with a different URL or until the developer explicitly invokes the stopTracking() method.

tracker.stopTracking()
tracker.startPageView() has been deprecated in favor of tracker.trackNewPage()

If you want to track a screen instead of a new page, particularly when your view doesn’t have a real URI, you can utilize the trackScreen method. This method accepts a raw name instead of an actual URI for tracking purposes.

tracker.trackScreen(name: {String})
tracker.trackScreen(name: {String}, rs: {String?})

If you want to inform about the landing page of the session, tracker provides for you the following method

tracker.setLandingPage(landingPage: {String})
tracker.setLandingPage(landingPage: {URL})

If you want to include UTMs, you’ll need to add them to the landing page.

To track scroll depth, you can provide the UIScrollView to both the trackNewPage and trackScreen methods.

tracker.trackNewPage(url: {URL}, scrollView: {UIScrollView}, rs: {String?})
tracker.trackScreen(name: {String}, scrollView: {UIScrollView}, rs: {String?})

To associate the app user to the records generated by the library, use the method serUserId, indicating the user ID in your platform.

tracker.setSiteUserId({USER_ID})
tracker.setUserId() has been deprecated in favor of tracker.setSiteUserId()

On the other hand, if you need to retrieve marfeel user id for forwarding it to one of our apis, the method you need to use is getUserId()

tracker.getUserId()

You can specify the type of user with the options below. By default, if not specified, the user will be tracked as Anonymous.

tracker.setUserType(.logged)
tracker.setUserType(.paid)
tracker.setUserType(.unknown)
tracker.setUserType(.anonymous)
tracker.setUserType(.custom(8))
TIP:
It's recommended to first provide the user ID and the user journey information before initiating the tracking of the first pageview.

If you want to access the RFV of the user within a session, you can retrieve it using the getRFV method.

tracker.getRFV { rfv in
// Rfv` struct containing:
// - `rfv` (`Float`): The composite RFV score representing overall user engagement.
// - `r` (`Int`): Recency — how recently the user has visited.
// - `f` (`Int`): Frequency — how often the user visits.
// - `v` (`Int`): Volume — how much content the user consumes.
}

You can track conversions calling the trackConversion() method:

tracker.trackConversion(conversion: "{CONVERSION}")
tracker.track(conversion: "{CONVERSION}") has been deprecated in favor of tracker.trackConversion(conversion: "{CONVERSION}")

Just like in web, Conversion parameters are available for Native

tracker.trackConversion(
conversion: "{CONVERSION}",
options: ConversionOptions( // optional
id: "{ID}", // each pair {CONVERSION} and { ID } will only be tracked once
value: "{VALUE}",
meta: ["key_1": "val_1", "key_2": "val_2"],
scope: ConversionScope.page // page | session | user
)
)

Keep in mind that trackConversion with the same conversion and options.id will only be tracked once

In the following example, if you send the first call:

tracker.trackConversion(
conversion: "conv_1",
options: ConversionOptions(
id: "id_1",
value: "3",
)
)

A second call won’t be tracked because conv_1:id_1 has already been tracked:

tracker.trackConversion(
conversion: "conv_1",
options: ConversionOptions(
id: "id_1",
value: "4",
)
)

In order to mark if the current visit has consent approved or not, sdk offers the following method to use:

tracker.setConsent(true)

User data will be deleted each time the user closes the app if no consent is provided.

tracker.setPageVar(name: "name", value: "value")
tracker.setSessionVar(name: "name", value: "value")
tracker.setUserVar(name: "name", value: "value")

Adds a new persistent user segment

tracker.addUserSegment("segment")

Replaces existing user segments

tracker.setUserSegments(["segment1", "segment2"])

Removes existing user segment

tracker.removeUserSegment("segment")

Clears all user segments

tracker.clearUserSegments()
tracker.setPageMetric(name: "name", value: 123)
Only integer values are accepted

All multimedia tracking features are made available through a MultimediaTracking instance. To access MultimediaTracker, use the shared variable in any part of the application

let multimediaTracker = CompassTrackerMultimedia.shared
public protocol MultimediaTracking {
func initializeItem(id: String, provider: String, providerId: String, type: Type, metadata: MultimediaMetadata)
func registerEvent(id: String, event: Event, eventTime: Int)
}
public enum Event: String, Codable {
case PLAY = "play"
case PAUSE = "pause"
case END = "end"
case UPDATE_CURRENT_TIME = "updateCurrentTime"
case AD_PLAY = "adPlay"
case MUTE = "mute"
case UNMUTE = "unmute"
case FULL_SCREEN = "fullscreen"
case BACK_SCREEN = "backscreen"
case ENTER_VIEWPORT = "enterViewport"
case LEAVE_VIEWPORT = "leaveViewport"
}
public enum Type: String, Codable {
case VIDEO = "video"
case AUDIO = "audio"
}
public struct MultimediaMetadata: Codable {
var isLive = false
var title: String?
var description: String?
var url: String?
var thumbnail: String?
var authors: String?
var publishTime: Int64?
var duration: Int?
}
Note:
For the full list of available events (Event) and media types (Type), refer to the Multimedia tracking documentation.
CompassTrackerMultimedia.shared.initializeItem(
id:"videoId",
provider: "youtube",
providerId: "youtube-video-id",
type: .VIDEO,
metadata: MultimediaMetadata.init(
title: "title",
description: "description",
url: URL(string: "https://youtube.com/watch?v=youtube-vide-id"),
authors: "authors",
duration: 420
)
)
CompassTrackerMultimedia.shared.registerEvent(id: "videoId", event: .UPDATE_CURRENT_TIME, eventTime: 150))

Track which elements in your app are shown, visible, and clicked. Works for any element you need to measure: recommendation modules, banners, CTAs, or anything else. The SDK reports eligibility, visibility, and clicks; your app decides what those elements are.

let recirculation = Recirculation.shared
Working example: see AllPostsView.swift in the Playground app for a complete recirculation tracking flow.

Represents a single trackable element within a module:

PropertyTypeDescription
urlStringCanonical URL of the element’s destination
positionIntZero-based position of the element within the module
public struct RecirculationLink {
public let url: String
public let position: Int
public init(url: String, position: Int)
}

Each module must be tracked through its full lifecycle:

MethodWhen to call
trackEligibleThe module has been selected and will appear in the current page view
trackImpressionOne or more elements become visible on screen
trackClickThe user taps an element within the module
let recirculation = Recirculation.shared
let moduleName = "example-module-name"
let links = [
RecirculationLink(url: "https://example.com/article-1", position: 0),
RecirculationLink(url: "https://example.com/article-2", position: 1),
RecirculationLink(url: "https://example.com/article-3", position: 2)
]
recirculation.trackEligible(name: moduleName, links: links)
recirculation.trackImpression(name: moduleName, links: [links[0], links[1]])
recirculation.trackImpression(name: moduleName, link: links[2])
recirculation.trackClick(name: moduleName, link: links[1])
All recirculation methods are fire-and-forget. The SDK dispatches on a background queue and silently ignores failures.

The Experiences API lets you fetch experiences configured in Experience Manager for the current page so your app can render them. You can filter by type or family, resolve remote content on demand, and track user interactions. The SDK returns the configuration; rendering is the app’s responsibility.

All experiences features are made available through an Experiences instance:

let experiences = Experiences.shared
Working example: see BlogPostView.swift in the Playground app for a complete experiences fetch and tracking flow.

Fetch experiences for the current targeting using fetchExperiences. The SDK automatically uses the URL from the active page being tracked. The result is delivered via a completion handler invoked on a background queue.

let experiences = Experiences.shared
experiences.fetchExperiences { result in
// result is [Experience]
}
fetchExperiences requires the SDK to be initialized via CompassTracker.initialize(accountId:) and a page to be actively tracked via trackNewPage(url:rs:) or trackScreen(_:rs:). If no page is being tracked, an empty list is returned.

Each Experience contains:

PropertyTypeDescription
idStringUnique experience identifier
nameStringExperience name from the server configuration
typeExperienceTypeExperience type (.inline, .flowcards, .compass, .adManager, etc.)
familyExperienceFamily?Optional experience family (.recommender, .paywall, .widget, etc.). nil when not specified by the server
placementString?Placement identifier
contentUrlString?URL to fetch the experience content from
contentTypeExperienceContentTypeContent type (.textHTML, .json, .amp, etc.)
features[String: Any]?Arbitrary feature flags and configuration
strategyString?Placement strategy (e.g., replace, append)
resolvedContentString?The fetched content body, populated after calling resolve(_:) or when resolve = true
rawJson[String: Any]The full original JSON for accessing fields not explicitly modeled

You can filter experiences by type, family, or both:

let experiences = Experiences.shared
// Filter by type
experiences.fetchExperiences(filterByType: .inline) { inlineOnly in
// ...
}
// Filter by family
experiences.fetchExperiences(filterByFamily: .recommender) { recommenders in
// ...
}
// Combine both filters (AND logic)
experiences.fetchExperiences(
filterByType: .inline,
filterByFamily: .recommender
) { inlineRecommenders in
// ...
}

Available experience families:

Enum ValueServer Key
.twittertwitterexperience
.facebookfacebookexperience
.youtubeyoutubeexperience
.recommenderrecommenderexperience
.telegramtelegramexperience
.gatheringgatheringexperience
.affiliateaffiliateexperience
.podcastpodcastexperience
.experimentationexperimentsexperience
.widgetwidgetexperience
.marfeelPasspassexperience
.scriptscriptexperience
.paywallpaywallexperience
.marfeelSocialmarfeelsocial
The Server Key is the raw string value returned by the server in the family field. Use it when accessing rawJson directly or when debugging API responses. When the server returns an unrecognized value, it maps to ExperienceFamily.unknown. When the field is absent, it is nil.

By default, fetchExperiences does not resolve content. You can enable automatic resolution for all experiences that have a contentUrl, or resolve individually on demand:

let experiences = Experiences.shared
// No content resolution (default)
experiences.fetchExperiences { result in
result.first?.resolvedContent // nil
}
// Resolve all content automatically
experiences.fetchExperiences(resolve: true) { result in
result.first?.resolvedContent // contains the fetched content
}
// Resolve individually on demand
experiences.fetchExperiences { result in
result.first?.resolve { content in
// content is String?
}
}

Add custom targeting key-value pairs that will be sent with every experiences request:

experiences.addTargeting(key: "key", value: "value")

Each experience that your app consumes must be tracked through its full lifecycle. Call the appropriate method as each stage occurs. Tracking link interactions uses RecirculationLink, defined in the Recirculation Tracking section above.

MethodWhen to call
trackEligibleThe experience has been selected and is intended to be shown on the current page view
trackImpressionThe experience content has been rendered and is visible to the user
trackClickThe user tapped on a specific link within the experience
trackCloseThe user dismissed or closed the experience
let experiences = Experiences.shared
experiences.fetchExperiences(filterByFamily: .recommender) { result in
guard let recommender = result.first else { return }
let links = [
RecirculationLink(url: "https://example.com/article-1", position: 0),
RecirculationLink(url: "https://example.com/article-2", position: 1),
RecirculationLink(url: "https://example.com/article-3", position: 2)
]
// The experience will be used in this page view
experiences.trackEligible(experience: recommender, links: links)
// First two links are visible on screen
experiences.trackImpression(experience: recommender, links: [links[0], links[1]])
// After scrolling, the third link becomes visible
experiences.trackImpression(experience: recommender, link: links[2])
// The user tapped a link inside the experience
experiences.trackClick(experience: recommender, link: links[1])
// The user dismissed the experience
experiences.trackClose(experience: recommender)
}

Marfeel sdk is compliant with new Apple privacy policies, listing in the PrivacyInfo.xcprivacy how the tracker interacts with user data. Starting on May 1st 2024, all new apps or updates are required to list how the app interacts with user data, including third party sdks.

image|690x69