TouchPointKit 1.0.6

TouchPointKit 1.0.6

DharminderPD Mobile App 维护。



  • Alida

Touchpoint 移动 SDK

Touchpoint 移动 SDK 的目的是在移动应用和 Alida Touchpoint 之间创建集成。这种集成使得能够发布和管理在移动应用中显示的 Touchpoint 活动,而无需发布新版本的移动应用或进行任何代码更改。

有关 Touchpoint 的一些重要概念可以在这里找到。以下简要描述了其中一些概念,但上面的链接提供了更详细的描述。

最低要求

  • iOS 版本 10.0

示例应用

https://github.com/vcilabs/touchpointkit-sample-ios

使用Pods进行安装

请将以下内容添加到您的Podfile中。请查阅此处以确定最新标签。

pod 'TouchPointKit', :git => 'https://github.com/vcilabs/touchpoint-kit-ios.git', :tag => '1.0.3'

然后运行pod install

使用SPM进行安装

在XCode包管理器内,添加以下URL:https://github.com/vcilabs/touchpoint-kit-ios。请查阅此处以确定最新标签。

概念

SDK的核心设计目标是只需让移动应用开发者提供一次 efforts起始 efforts来使移动应用的各个屏幕“启用Touchpoint”,并消除与切换显示特定活动相关的任何持续 efforts。Touchpoint管理员可以在无需开发工作的情况下切换Touchpoint活动。

由于这种解耦,本SDK中永远不会有对特定活动的引用。所有活动的配置和分发都由Touchpoint管理员在Touchpoint用户界面内完成。

  • 触发器:触发器是激活并显示给用户Touchpoint活动的方式。SDK支持三种不同类型的触发器。
    • 横幅:当用户导航到特定屏幕后显示横幅。在屏幕底部显示了一个带有调用行动的UI组件。点击横幅将显示活动。
    • 弹窗:在用户导航到特定屏幕后显示弹窗(可选延迟后)。弹窗没有UI组件,Touchpoint活动直接显示。
    • 自定义组件:自定义组件是在任意生命周期事件期间触发Touchpoint活动的方式,并且完全在开发者的控制之下。活动可以由按钮点击、用户第二次访问屏幕或其他任何自定义逻辑触发。
  • 屏幕:屏幕是您移动应用中的一个页面或视图,例如主屏、商品列表屏幕、商品详情屏幕、设置屏幕等。您可以将移动应用中的任何屏幕指定为可以显示触点活动。
  • 组件:组件是您移动应用屏幕上的某些UI元素。这可能像按钮这样的东西,也可能像用户访问特定屏幕的次数这样的更抽象的东西。
  • 定位:Touchpoint管理员可以根据当前应用程序用户的某些用户属性对特定的Touchpoint活动进行定位。当前用户的用户属性可以在移动应用程序中定义,然后由Touchpoint用于定位。

横幅和弹出式触发器的类型是触发活动的开箱即用的方法。这样做是为了使集成更加容易,但牺牲了灵活性。自定义组件需要一些自定义逻辑来触发,但它提供了灵活性,因为逻辑完全受应用程序开发者的控制。

用户完成活动后,活动将被关闭,用户将保持在同一屏幕上,状态不变。

整合一切

作为移动应用程序的开发者,您可以指定哪些屏幕以及哪些屏幕组件能够触发Touchpoint活动。您不需要担心哪些特定的Touchpoint活动被分配给特定的屏幕,因为这由Touchpoint管理员控制。

如果需要在特定屏幕上有横幅或弹出式活动,您只需定义和提供屏幕名称。如果您使用自定义组件,您需要提供屏幕名称和组件名称。例如

let screenComponents = [
        [ "screenName": "Home" ],
        [ "screenName": "Settings", "componentName": "Lightbulb" ],
        [ "screenName": "ProductList" ],
    ]

在这里,我们定义了Home屏幕和ProductList屏幕能够显示横幅和弹出式触发器的类型。然后在我们的Settings屏幕上,我们有一个命名为Lightbulb的按钮,现在能够显示自定义组件触发器。屏幕可以支持多个组件。

实现

初始设置

在AppDelegate类中,使用import TouchPointKit导入SDK。然后,在didFinishLaunchingWithOptions函数中添加以下初始化代码。

// API key and secret are provided by the Touchpoint UI
let apiKey = API_KEY 
let apiSecret = API_SECRET

// The pod is the geographical region hosting your instance of Touchpoint.
// Easiest determined from your URL while logged in, e.g. eu2.alida.com
// Valid values are: na1, na2, eu1, eu2, ap2, ap3
let podName = TouchPointPods.eu2 

// These are the Screens and Screen Components in your mobile app that you 
// designate as being able to render Touchpoint activities.
let screenComponents = [
        [ "screenName": "Home" ],
        [ "screenName": "Settings", "componentName": "Lightbulb" ],
        [ "screenName": "ProductList" ],
    ]

// The visitor payload describes the current user of the app. The "id"
// is used to help determine if this particular user has already
// seen certain activities and should be a unique identifier.
// "userAttributes" are the targeting parameters. "type" is the data type
// found in the "value". The data type is required as Touchpoint has
// various operators that make sense for certain data types and not 
// others, such as "greater than" or "less than" for numbers.
// Valid values are number, boolean, string and date.
let visitor = [
        "id": "12345",
        "userAttributes": [ // These are used in targeting
            [
                "key": "age", 
                "type": "number",
                "value": "53"
            ],
            [
                "key": "isLoyaltyMember",
                "type": "boolean",
                "value": "true"
            ],
            [
                "key": "city",
                "type": "string",
                "value": "Springfield"
            ],
            [
                "key": "previousVisitDate",
                "type": "date",
                "value": "2022-04-11T21:51:34+0000"
            ]
        ]
    ] as [String : Any]

TouchPointActivity.shared.configure(
    apiKey: apiKey,
    apiSecret: apiSecret,
    podName: podName,
    screenComponents: screenComponents,
    visitor: visitor)
 
// The following are optional properties for developer convenience

// Should be "false" in production and is "false" by default.
// Touchpoint generally won't show an activity to the same user twice which
// can make it tricky to test. Setting this to "true" makes it
// possible for a user to be served the same activity more than once.
TouchPointActivity.shared.disableAPIFilter = false

// To disable wkwebview caching, set to "true". "false" by default.
TouchPointActivity.shared.disableCaching = false

// Logs at debug level are silenced by default. To enable debug logs, 
// set this to "true". "false" by default.
TouchPointActivity.shared.enableDebugLogs = true

// Prevents any logs being generated, default is "false".
TouchPointActivity.shared.disableAllLogs = false

// If status bar style is dark, set this property to "false". "true" by
// default.
TouchPointActivity.shared.isStatusBarStyleLight = false

// The height of the banner UI element in pixels. Default is "70".
TouchPointActivity.shared.defaultBannerHeight = 50

注意

请勿使用访客属性将个人信息(PII)传递到Touchpoint。为了保护访客隐私,请勿传递可能被视为个人信息(PII)的数据到Touchpoint。PII包括但不限于社会安全号码、个人家庭地址、信用卡号码、金融账户号码、街道地址等信息。

触发横幅和弹出窗口

对于横幅和弹出窗口触发器,您只需要告诉SDK当前是哪个屏幕可见。要做到这一点,请使用以下方法在ViewViewController中导入SDK:使用import TouchPointKit,并在下面设置屏幕,最好在viewDidLoad函数中

override func viewDidLoad() {
    super.viewDidLoad()
    TouchPointActivity.shared.setScreenName(screenName: SCREEN_NAME)
}

分配给指定屏幕的任何横幅或弹出窗口都将被触发并自动显示。

由于弹出窗口可以由Touchpoint管理员配置,仅在指定时间过后触发,因此用户可能会在弹出窗口显示前快速切换到其他屏幕。这时,弹出窗口将在第二个屏幕上显示,这可能是我们不希望的。为了防止这种情况,可以在删除第一个屏幕时取消弹出窗口。

TouchPointActivity.shared.cancelPopupForScreen(screenName: SCREEN_NAME)

触发自定义组件

对于自定义组件的触发,您可以将钩子钩入任何生命周期事件并直接调用Touchpoint活动。

TouchPointActivity.shared.openActivityForScreenComponent(screenName: SCREEN_NAME, componentName: COMPONENT_NAME, delegate: self)

在调用openActivityForScreenComponent函数之前,可以使用以下方法检查是否需要显示Touchpoint活动:

if TouchPointActivity.shared.shouldShowActivity(screenName: SCREEN_NAME, componentName: COMPONENT_NAME) {
    // Call openActivityForScreenComponent
}

这可以帮助您管理如何渲染屏幕,例如根据Touchpoint活动是否可用来隐藏或显示按钮。

刷新活动列表

在每次调用TouchPointActivity.shared.configure时,SDK将向Touchpoint请求获取当前用户的有效活动列表并将其缓存。通常此函数在应用加载时运行,之后不再调用,这可能会导致活动列表过时。有一个辅助函数可以为您刷新活动列表。

TouchPointActivity.shared.refreshActivities()

这可以在调用TouchPointActivity.shared.configure之后的任何时候调用,并从Touchpoint获取新的活动列表。

注意:通常在一个Touchpoint活动中,只有当在应用的启动过程中调用了一次TouchPointActivity.shared.configure时,才会向应用分发一个活动,在同一个“会话”中每组活动只会显示一个活动。使用TouchPointActivity.shared.refreshActivities可能导致在相同用户会话中显示活动的下一个活动。

回调事件

Touchpoint SDK提供了两种不同的回调,可以在用户与活动交互时触发自定义逻辑。

  1. 收起:当用户关闭或收起活动时。在这种情况下,活动刚刚从视图中移除,并开始与用户重逢。
  2. 完成:当用户完成活动中的最后一个问题,活动被视为完成时。在此之后,该活动被视为在Touchpoint中的“完成”,用户仍在查看活动。

为了实现这些回调,您的类应继承自 TouchPointActivityDelegate 并实现两个函数 onTouchPointActivityCollapseonTouchPointActivityComplete。请注意,onTouchPointActivityComplete 是可选的。

示例

class CustomComponentViewController: UIViewController, TouchPointActivityDelegate {
    // ...
    
    func onTouchPointActivityComplete() {
        let activityCompleteAlert: UIAlertView = UIAlertView(title: "Thanks for completing the activity!", message: "We really appreciate your feedback",
                             delegate: self, cancelButtonTitle: "OK")
        activityCompleteAlert.show()
    }
    
    func onTouchPointActivityCollapse() {
        curSelectedButton?.isEnabled = false
    }

    // ...    
}

React Native 的 iOS 集成

在您的 React Native 项目中会有一个名为 ios 的文件夹。在这个文件夹中,将包含您应用的 iOS 原生部分。要将此 SDK 安装到您的应用中,请使用上述描述的方法之一,通过 PodfileSPM 进行安装

AppDelegate.m 文件中,通过在文件顶部添加一行导入语句来导入 SDK:@import TouchPointKit; 并在 AppDelegate.mdidFinishLaunchingWithOptions 函数中添加以下代码片段。

请参见上面的 初始设置 部分,以了解如何使用下面代码片段中的各种参数及其用法。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //...

    NSString *apiKey = @"API_KEY";
    NSString *apiSecret = @"API_SECRET";

    // Possible values: TouchPointPodsNA1, TouchPointPodsNA2, TouchPointPodsEU1,
    // TouchPointPodsEU2, TouchPointPodsAP2, TouchPointPodsAP3
    TouchPointPods pod = TouchPointPodsEU2;

    // Example user attributes used for targeting, see the integration details
    // above for more details.
    NSArray *userAttributes = @[
        @{ @"key": @"age", @"type": @"number", @"value": @"53" },
        @{ @"key": @"isLoyaltyMember", @"type": @"boolean", @"value": @"true" },
        @{ @"key": @"city", @"type": @"string", @"value": @"Springfield" },
        @{ @"key": @"previousVisitDate", @"type": @"date", @"value": @"2022-04-11T21:51:34+0000" },
    ];
    NSDictionary *visitor = [[NSDictionary alloc] initWithObjectsAndKeys:@"12345", @"id", userAttributes, @"userAttributes", nil];

    // Example screens and components
    NSArray *screenComponents = @[
        @{ @"screenName": @"Banner Screen" },
        @{ @"screenName": @"Popup Screen" },
        @{ @"screenName": @"Custom Component Screen", @"componentName": @"Button 1" },
        @{ @"screenName": @"Custom Component Screen", @"componentName": @"Button 2" },
        @{ @"screenName": @"Custom Component Screen", @"componentName": @"Button 3" },
    ];

    [[TouchPointActivity shared] configureWithApiKey: apiKey apiSecret: apiSecret podName: pod screenComponents: screenComponents visitor: visitor];

    // Optional configuration elements, see integration details above for more details
    [TouchPointActivity shared].disableAPIFilter = false;
    [TouchPointActivity shared].disableCaching = false;
    [TouchPointActivity shared].enableDebugLogs = true;
    [TouchPointActivity shared].disableAllLogs = false;
    [TouchPointActivity shared].isStatusBarStyleLight = false;
    [TouchPointActivity shared].defaultBannerHeight = 50;
    
    //...
}

现在,在您的 iOS 项目中创建两个文件,分别命名为 TouchPointKitBridge.hTouchPointKitBridge.m。将以下代码添加到这些文件中。

//  TouchPointKitBridge.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@import TouchPointKit;

NS_ASSUME_NONNULL_BEGIN

@interface TouchPointKitBridge : RCTEventEmitter <RCTBridgeModule, TouchPointActivityDelegate>

@end

NS_ASSUME_NONNULL_END
//  TouchPointKitBridge.m
#import "TouchPointKitBridge.h"

@implementation TouchPointKitBridge
{
  bool hasListeners;
}

// Will be called when this module's first listener is added.
-(void)startObserving {
    hasListeners = YES;
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
    hasListeners = NO;
}

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(configure:(NSString *)apiKey apiSecret:(NSString *)apiSecret pod:(int)pod screens:(NSArray *)screens visitor:(NSDictionary *)visitor ) {
  [[TouchPointActivity shared] configureWithApiKey:apiKey apiSecret:apiSecret podName:pod screenComponents:screens visitor:visitor];
}

RCT_EXPORT_METHOD(refreshActivities) {
  [[TouchPointActivity shared] refreshActivities];
}

RCT_EXPORT_METHOD(setVisitor:(NSDictionary<NSString *, id> *)visitor)
{
  [TouchPointActivity shared].visitor = visitor;
}

RCT_EXPORT_METHOD(enableDebugLogs:(BOOL)enable)
{
  [TouchPointActivity shared].enableDebugLogs = enable;
}

RCT_EXPORT_METHOD(disableAllLogs:(BOOL)disable)
{
  [TouchPointActivity shared].disableAllLogs = disable;
}

RCT_EXPORT_METHOD(disableAPIFilter:(BOOL)disableAPIFilter)
{
  [TouchPointActivity shared].disableAPIFilter = disableAPIFilter;
}

RCT_EXPORT_METHOD(disableCaching:(BOOL)caching)
{
  [TouchPointActivity shared].disableCaching = caching;
}

RCT_EXPORT_METHOD(isStatusBarStyleLight:(BOOL)isStatusBarStyleLight)
{
  [TouchPointActivity shared].isStatusBarStyleLight = isStatusBarStyleLight;
}

RCT_EXPORT_METHOD(defaultBannerHeight:(CGFloat)defaultBannerHeight)
{
  [TouchPointActivity shared].defaultBannerHeight = defaultBannerHeight;
}


RCT_EXPORT_METHOD(setScreen:(NSString *)screenName)
{
  [[TouchPointActivity shared] setScreenNameWithScreenName:screenName delegate: self];
}

RCT_EXPORT_METHOD(openActivity:(NSString *)screenName componentName:(NSString *)componentName)
{
  if ([[TouchPointActivity shared] shouldShowActivityWithScreenName: screenName componentName: componentName]) {
    [[TouchPointActivity shared] openActivityForScreenComponentWithScreenName: screenName componentName:componentName delegate: self];
  }
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(shouldShowActivity:(NSString *)screenName) {
  BOOL val = [[TouchPointActivity shared] shouldShowActivityWithScreenName: screenName componentName: NULL];
    return [NSNumber numberWithBool:val];
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(shouldShowActivity:(NSString *)screenName componentName:(NSString *)componentName)  {
  BOOL val = [[TouchPointActivity shared] shouldShowActivityWithScreenName: screenName componentName: componentName];
    return [NSNumber numberWithBool:val];
}

RCT_EXPORT_METHOD(openActivityForUrl:(NSString *)url alwaysShow:(BOOL)alwaysShow)
{
  [[TouchPointActivity shared] openActivityForUrlWithDistUrl:url useBannerStyling:false delegate:self alwaysShow:alwaysShow];
}

RCT_EXPORT_METHOD(clearCache)
{
  NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
  NSDictionary * dict = [userDefaults dictionaryRepresentation];
  for (id key in dict) {
      [userDefaults removeObjectForKey:key];
  }
  [userDefaults synchronize];
}

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

- (void) onTouchPointActivityComplete {
  if (hasListeners) {
    [self sendEventWithName:@"onTouchPointActivityComplete" body:@"TouchPointActivityCompleted"];
  }
}

- (void) onTouchPointActivityCollapse {
  if (hasListeners) {
    [self sendEventWithName:@"onTouchPointActivityCollapse" body:@"TouchPointActivityCollapsed"];
  }
}

- (NSArray<NSString *> *)supportedEvents {
  return @[@"onTouchPointActivityComplete", @"onTouchPointActivityCollapse"];
}

@end

从您的 App.js 文件中,使用 NativeModules 调用 TouchPointKitBridge 方法。

import {
  NativeModules,
  NativeEventEmitter,
} from 'react-native';

// Register for event listening from SDK (activity complete event)
const { TouchPointKitBridge } = NativeModules;
const eventEmitter = new NativeEventEmitter(TouchPointKitBridge);
eventEmitter.addListener(
  'onTouchPointActivityComplete',
  onTouchPointActivityComplete,
);

eventEmitter.addListener(
  'onTouchPointActivityCollapse',
  onTouchPointActivityCollapse,
);

const onTouchPointActivityComplete = (event) => {
  console.log('onTouchPointActivityComplete called');
  console.log(event);
};

const onTouchPointActivityCollapse = (event) => {
  console.log('onTouchPointActivityCollapse called');
  console.log(event);
};

// To trigger a Pop-up or Banner
NativeModules.TouchPointKitBridge.setScreen('SCREEN_NAME');

// To trigger a custom component
NativeModules.TouchPointKitBridge.openActivity('SCREEN_NAME', 'COMPONENT_NAME');