iOS SDK for Russmedia Engagement Engine v 1.3.1
iOS Deployment Target 9.0
, Swift 5.3.1
.
Check out Get Started tab on cocoapods.org. Than use following spec.
Or use downloaded framework as embedded binary for your project.
Framework uses native Camera for QR Code reader, so Info.plist
of your app needs to provide static messages to display to the user when the system asks for camera or microphone permission: NSCameraUsageDescription
and NSMicrophoneUsageDescription
.
First thing is, to import the framework.
import TeeSDK
Once imported, you can access singleton instance of the framework and implement some delegates methods and properties.
Called, on every request, that is made by framework.
TEE.instance.onCall = { callName in
print("Tee is making call")
}
Called, when any message arrives. onCall
and onMessageReceived
may be a good place to paste own tracking of engine.
TEE.instance.onMessageReceived = { message in
print("Tee is getting message")
}
Method, that enques blocks, which are fired since TEE has initialised. Typically, wait with execution of relevant deeplinks, until any collector token is already received.
TEE.instance.executeAfterInitialized {
print("Anonymous or logged-in user is active")
}
Method takes block, that is executed right when TEE has known, logged-in user. And as second parameter, takes expiration of block, that can invalidate execution after some time. Typically, since user is logged in, you may want to adapt his app UI.
TEE.instance.executeAfterNotAnonymous {
print("User is finally logged in")
}
You may want to notify users, with targeting based on TEE profile. You can associate push notification token with specific user.
As additional, optional parameter, pushAlias
you can pass specific string value, associated with token.
func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) {
TEE.instance.handlePushRegistration(deviceToken: deviceToken, pushAlias: "aliasString")
}
You can catch users points progress via callbacks for any amount of points you need.
TEE.instance.addMilestone(forPointsCount: 10) {
print("Another 10 points reached")
}
Readonly Bool for getting info, if currently running session is anonymous, or with logged-in user.
TEE.instance.isAnonymous
Talky log of communication with socket server in console. Default value is false
TEE.instance.verbosePrint = true
UIViewController, that is used for presenting overlayed modal views, for engagement engine notifications. You can disable notification by keeping this property unset.
TEE.instance.presentingViewController = window?.rootViewController
Optional styling of TEE popup window.
TEE.instance.popupCornerRadius = 30
Optional width of popup window, with absolut value.
TEE.instance.popupStaticWidth = 300 /// this is default value
Optional width of popup window, relative to device screen width. Overrides popupStaticWidth
.
TEE.instance.popupPercentWidth = 80
Since all props and delegates are set, you can enable connection itself with tracking of anonymous user. Activity will be tracked and saved under new generic collector token.
TEE.instance.initConnection()
Whenever you determinate, that user is not anonymous anymore, you call init method again, with remoteId and userName as parameters. RemoteId should represent unique identificator of the user and userName, as optional parameter, as human readable string as display name.
TEE.instance.initConnection(remoteId: "[email protected]", userName: "BMan")
Logout method will cancel session with given user and will get anonymous token to starts with points from 0.
TEE.instance.logout()
To enable challenges for each part of app, you call register method for every screen, that should hold elements to trigger challenges.
Each View is registrered for own challanges under unique string detailPageId
and provides reference to root UIView of the screen.
TEE.instance.registerChallengesFor(detailPageId: "uniqueViewID", view: self.view)
Than, particular challenges can be triggered in code, using ID of challenge, with call:
TEE.instance.fireChallenge(withElementId: ".article-detail.selected .share.facebook")
You can set ID of challenge as accessibilityIdentifier
of an UIButton
or TEEUIScrollViewObservable
. Than, when you register detail page with registerChallengesFor(detailPageId:, view:)
, challenges will be attached, and fired automatically when interacting with the element.
For triggering scroll challenge, TEEUIScrollViewObservable subclass is required
@IBOutlet weak var scrollView: TEEUIScrollViewObservable!
TEE.instance.resignViewForChallenges(withKey: "uniqueViewID")
Engagement Engine measures also user activity in general. The best place to run activityPing()
call is UIApplicationDelegate methods.
func applicationDidEnterBackground(_ application: UIApplication) {
TEE.instance.activityPing()
}
func applicationDidBecomeActive(_ application: UIApplication) {
TEE.instance.activityPing()
}
You can call method updateLanguage(lang: String)
on running session, to switch language of TEE UI for currently active collector token. Either anonymous or already paired one with user. As a parameter, is expected Alpha-2 language code, ISO 639-1.
TEE.instance.updateLanguage(lang: "de")
Use method setPointsFormatter(_ formatter: NumberFormatter)
to pass number formatter, that is applied to every TEEPointsView in app while displaying current points.
let formatter = NumberFormatter()
formatter.groupingSeparator = "."
formatter.numberStyle = .decimal
TEE.instance.setPointsFormatter(formatter)
Use new public API: modalPresentationStyle: UIModalPresentationStyle?
and presentationTransitioningDelegate: UIViewControllerTransitioningDelegate?
on TEE instance, to specify your own transition to open overview page in app.
TEE.instance.modalPresentationStyle = .custom
TEE.instance.presentationTransitioningDelegate = MyTransitioningDelegate()
For opening TEE overview page on specific position, you can use public API: handleDeepLink(_ url: URL)
, where URL includes GET parameter for navigating to relevant section. Method uses regular expression to harvest entry point from ?entry=
GET parameter. So valid URL object with specified entry string as GET parameter, should be inserted. Example URLs, you can pass:
- ...ttps://yourwebsite/open-gamification
?entry=/me/rewards
- ...ttps://yourwebsite/open-gamification
?entry=/me/challenges
- ...ttps://yourwebsite/open-gamification
?entry=/me/overview
- scheme://your.app/whatever-text
?entry=/me/rewards
TEE.instance.handleDeeplink(URL(string: "https://yourwebsite/open-gamification?entry=/me/rewards")!)
For opening TEE overview page from WKWebview, with specific deeplink, you register it's WKUserContentController by TEE.instance.registerWKUserContentControllerForDeeplinks(controller: userController)
and set javascript listeners in webview, after DOM is loaded, by TEE.instance.registerHTMLForDeeplinks(webview: webView)
.
private func initWebView() {
let userController = WKUserContentController()
let config = WKWebViewConfiguration()
config.userContentController = userController
webView = WKWebView(frame: .zero, configuration: config)
TEE.instance.registerWKUserContentControllerForDeeplinks(controller: userController)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
TEE.instance.registerHTMLForDeeplinks(webview: webView)
}
In HTML, you add class tee-element
to identify TEE related element, which is automatically displayed as block, if TEE is present. Then class tee-open-btn
to open TEE overview popup on click and attribute data-open
to pass deeplink string to open.
<div class="tee-open-btn tee-element" style="display: none" data-open="/me/rewards/detail/123">
My HTML link to page with reward ID 123
</div>
Method executeAfterNotAnonymous(_ block: @escaping () -> Void, cancelAfter interval: TimeInterval = 0)
allows you to register block, that is fired, right when user is not anonymous anymore. If it won't be desired, to fire block after specific time frame, you can pass expiration time interval, timer will be invalidated and block released. In case of deep linking, you may want to check, if user is not anonymous. Anonymous user has limited content in TEE overview pages and it's beneficial to suggest login dialog first.
Code below demonstrates option, how you can implement deep linking, with user-login step in the middle.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
switch userActivity.activityType {
case NSUserActivityTypeBrowsingWeb:
let url = userActivity.webpageURL!
if userIsLoggedIn { // your getter
TEE.instance.handleDeepLink(url.absoluteString)
} else {
loginUser({ isSuccess in // your async login process
if isSuccess {
TEE.instance.executeAfterNotAnonymous({
TEE.instance.handleDeepLink(url.absoluteString)
}, cancelAfter: 5)
}
})
}
default: break
}
return false
}
Engagement Engine is looking for two mandatory strings under TEE
dictionary, that shoud be provided in Info.plist
. TEE/ApiToken and TEE/liveSocketEndpoint
- TEE (Dictionary)
- ApiToken (String)
- liveSocketEndpoint (String)
- devSocketEndpoint1 (String) // optional socket endpoint, selectable in debug console
- devSocketEndpointXY (String) // optional socket endpoint, selectable in debug console
- themeColorHex (String) // optional
- pointsFontColorHex (String) // optional
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
TEE.instance.onCall = { callName in
print("Tee is making call")
}
TEE.instance.onMessageReceived = { message in
print("Tee is getting message")
}
TEE.instance.verbosePrint = true
TEE.instance.initConnection() // Anonymous init
TEE.instance.presentingViewController = window?.rootViewController
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
TEE.instance.activityPing()
}
func applicationDidBecomeActive(_ application: UIApplication) {
TEE.instance.activityPing()
}
}
import UIKit
import TeeSDK
class DetailViewController: UIViewController {
@IBOutlet weak var scrollView: TEEUIScrollViewObservable!
let pageKey: "productDetail123"
override func viewDidLoad() {
super.viewDidLoad()
// Associating scrollview with expected challenge code
scrollView.accessibilityIdentifier = ".article-detail.selected .article.scroll"
TEE.instance.registerChallengesFor(detailPageId: pageKey, view: self.view)
}
@IBAction func FBShareAction(_ sender: Any) {
TEE.instance.fireChallenge(withElementId: ".article-detail.selected .share.facebook")
}
deinit {
TEE.instance.resignViewForChallenges(withKey: pageKey)
}
}