iOS全栈API开发指南

SDK安装

打开云眼控制台(app.eyeofcloud.com),登录后点击“业务优化”,业务优化界面打开后再点击“SDK下载与安装”。

AB测试-SDK下载与安装

在新打开的页面中,选择“全栈API SDK安装”标签,再从下方的标签栏中选择“Objective-C”。根据下方提示完成SDK的安装。 

 

SDK初始化

所有和AB测试相关的接口都是定义在EOCClient这个类上的,通过这个类才能够版本抽签、触发目标、获取参数从而进行AB测试。同时,SDK还提供了一个EOCManager类来负责EOCClient的构造和获取、配置文件(包含AB测试信息)的下载和更新、配置文件的缓存管理等等。所以,初始化SDK的第一步就是构造一个EOCManager。建议在Appdelegate上设置一个私有属性保存全局唯一的一个EOCManager,再设计一个getEOCManager的接口将其暴露出来。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 初始化EOCManager
    _eocManager = [EOCManager init:^(EOCManagerBuilder * _Nullable builder) {
        builder.projectId = @"PROJECT_ID";
    }];
    return YES;
}
- (EOCManager *)getEOCManager {
    return _eocManager;
}

参数PROJECT_ID可以从“SDK下载与安装”界面中查到。

AB测试-全栈APISDK-PROJECTID

如果您在使用私有部署的云眼系统,则需要修改配置文件和事件发送的URL。 首先,实例化EOCDatafileManagerDefault类和EOCEventDispatcherDefault类,在各自的初始化方法里设置相应的URL。然后,调用EOCManager的初始化方法,并将以上两个类的实例作为参数传入并赋值给builder 。

EOCDatafileManagerDefault *datafileManager = [EOCDatafileManagerDefault init:^(EOCDatafileManagerBuilder * _Nullable builder) {
   builder.projectId = @"PROJECT_ID";
   builder.networkServiceCDNServerURL = @"配置文件URL";
}];
 
EOCEventDispatcherDefault *eventDispatcher = [EOCEventDispatcherDefault init:^(EOCEventDispatcherBuilder * _Nullable builder) {
   builder.eventDispatcherEventURL = @"事件发送URL";
}];
 
// 初始化EOCManager
EOCManager *eocManager = [EOCManager init:^(EOCManagerBuilder * _Nullable builder) {
   builder.projectId = @"PROJECT_ID";
   builder.datafileManager = datafileManager;
   builder.eventDispatcher = eventDispatcher;
}];

配置文件URL和事件发送URL可以在“SDK下载与安装”界面的Objective-C全栈API使用说明中查看。

 

AB测试-全栈APISDK-URL

在构造完EOCManager对象后,我们需要调用EOCManager的initializeWithCallback方法完成EOCClient的初始化。该方法会执行一个异步网络任务去下载配置文件,然后根据配置文件构造一个EOCClient对象并将其保存在eocManager中。在之后需要使用SDK的地方,通过EOCManager的getEyeofcloud方法获取缓存的EOCClient对象再来调用相关接口。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 设置根视图控制器
    self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];

    UIViewController *fakeRootViewController = [[UIViewController alloc] 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:UIScreen.mainScreen.bounds];
    [imageView setImage:[UIImage imageNamed:@"LaunchImage.png"]];
    fakeRootViewController.view = imageView;

    self.window.rootViewController = fakeRootViewController;
    [self.window makeKeyAndVisible];
    
    // 初始化EOCManager
    _eocManager = [EOCManager init:^(EOCManagerBuilder * _Nullable builder) {
        builder.projectId = @"PROJECT_ID";
    }];
    // 异步网络任务下载配置文件,请求超时时间为5s
    [_eocManager initializeWithCallback:^(NSError * _Nullable error, EOCClient * _Nullable client) {
        // 完成配置文件下载以及EOCClient的初始化,进入主界面
        dispatch_async(dispatch_get_main_queue(), ^{
            YFLoginViewController *loginViewController = [[YFLoginViewController alloc] init];
            self.window.rootViewController = loginViewController;
        });
    }];
    return YES;
}

这里需要注意的一点是,在网络不通的情况下,EOCClient初始化就会失败。例如,首次启动APP时,会触发一个弹框,要求用户设定APP的网络权限。在完成设定之前,APP的网络请求是被禁止的,从而导致EOCClient初始化失败。为了保证AB测试的顺利进行,就要把EOCClient的初始化推迟到APP联网之后。一个简单的办法就是使用AFN监听APP的网络状态,一旦APP联网就开始初始化EOCClient以及加载其它资源,然后重新渲染APP的界面。

在调用SDK的接口时,会需要一个唯一的userId来标识用户。另外,如果要使用受众功能,还会需要用户的属性值。为了方便,可以在Appdelegate中定义两个方法getUserAttributes 和getUserId 。

- (NSString *)getUserId {
    if (!_userId) {
        _userId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    }
    return _userId;
}

- (NSDictionary *)getUserAttributes {
    if (!_userAttributes) {
        _userAttributes = @{@"Device":@"iPhone SE"};
    }
    return _userAttributes;
}

 

非参数化AB测试方案

非参数化方法使用SDK的版本抽签接口得到一个版本名称,依据版本名称编写if-else的分支代码,不同版本执行不同的代码从而产生版本差异。该方案灵活性较差,每次进行新的测试都需要更新应用程序,只有在参数化方案无法满足需求时才推荐使用。

设计优化方案

根据想到的假设,设计出AB测试的方案。我们需要考虑好有哪些版本,衡量结果的唯一主目标是什么。设计优化方案时最好提出一种明确的假设,比如“橙色按钮比起黑色按钮更能引起注意从而带来更多注册”,而不是“按钮的颜色可能会影响注册”这种模糊的假设。明确的假设能让测试的成功率更高。

配置优化方案

在云眼控制台中点击“业务优化”,等到页面加载完成后点击“创建优化方案”按钮。平台选择“全栈API”,填写优化方案名称,点击“确认”创建优化方案。创建完成后,点击对应优化方案的“编辑”按钮来打开编辑器。

AB测试-全栈APISDK-优化方案创建

 

在“版本设置”中根据优化方案设计的情况来添加版本并分配流量,通常情况下均分流量即可。总流量根据实际流量调整,业务流量较大的应用选择部分流量参与测试就足够了。

AB测试-全栈APISDK-流量分配

 

根据优化方案设计的情况,将需要用到的衡量指标创建为一个目标。点击“+”号将其添加到优化方案中。

AB测试-全栈APISDK-创建目标

 

启动优化方案,完成配置工作。

AB测试-全栈APISDK-启动实验

 

修改应用程序

对于非参数化方案,给出两种常见的使用方式来产生多个版本。

第一种方式是在打开新的View前,进行版本抽签,依据抽签结果创建不同的View从而形成不同的版本。这种方式比较适合版本差异较大的情况,直接制作多个View作为不同的版本。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 设置根视图控制器
    self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];

    UIViewController *fakeRootViewController = [[UIViewController alloc] init];
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:UIScreen.mainScreen.bounds];
    [imageView setImage:[UIImage imageNamed:@"LaunchImage.png"]];
    fakeRootViewController.view = imageView;

    self.window.rootViewController = fakeRootViewController;
    [self.window makeKeyAndVisible];

    // 初始化EOCManager
    _eocManager = [EOCManager init:^(EOCManagerBuilder * _Nullable builder) {
        builder.projectId = @"PROJECT_ID";
    }];

    // 异步网络任务下载配置文件,请求超时时间为5s
    [_eocManager initializeWithCallback:^(NSError * _Nullable error, EOCClient * _Nullable client) {
        // 完成下载及版本抽签,根据抽签结果创建不同版本的View,最后更新主界面
        dispatch_async(dispatch_get_main_queue(), ^{
            EOCVariation *variation = [client activate:@"登录按钮实验" userId:[self getUserId] attributes:[self getUserAttributes]];
            YFLoginViewController *loginViewController = [[YFLoginViewController alloc] init];
            if ([variation.variationKey isEqualToString:@"优化版本#1"]) {
                loginViewController.view.backgroundColor = [UIColor orangeColor];
            }
            self.window.rootViewController = loginViewController;
        });
    }];

    return YES;
}

第二种方式是在需要体现版本差异的地方进行版本抽签,依据抽签结果进入不同的代码逻辑,从而使得应用的界面或者行为变得不同。例如,通过代码修改界面的样式。

- (void)loadView {
    self.loginView = [[YFLoginView alloc] init];
    self.view = _loginView;

    // Eyeofcloud
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    self.eocClient = [[delegate getEOCManager] getEyeofcloud];
    self.userId = [delegate getUserId];
    self.userAttributes = [delegate getUserAttributes];

    EOCVariation *variation = [self.eocClient activate:@"登录按钮实验" userId:self.userId attributes:self.userAttributes];
    UIColor *loginButtonColor = [UIColor colorWithRed:0.29 green:0.73 blue:0.97 alpha:1.00];
    if ([variation.variationKey isEqualToString:@"优化版本#1"]) {
        loginButtonColor = [UIColor orangeColor];
    }
    _loginView.loginButton.backgroundColor = loginButtonColor;
}

完成版本差异化的代码之后,添加触发目标的代码。

- (void)loginButtonAction {
    [[_loginView.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        [self.eocClient track:@"点击按钮" userId:self.userId attributes:self.userAttributes];
        [self.loginViewModel.loginCommad execute:nil];
    }];
}

调用EOCManager的getEyeofcloud方法得到的EOCClient对象是在应用启动时创建的。在这之后,本地缓存的配置文件是会自动定时更新的,如果想要从最新的缓存配置文件重新构造EOCClient对象,可以调用同步的initialize方法。该方法使用本地缓存生成新的EOCClient对象并将其更新到eocManager中。之后就和正常情况下一样通过EOCManager的getEyeofcloud方法获取EOCClient对象就可以了。

EOCClient *eocClient = [eocManager initialize];

 

参数化AB测试方案

参数化方案希望的是将应用程序中的一些关键变量动态化,使其不再写死在程序中,而是从一个可变的配置文件中读取对应的值。例如,可以参数化一个广告轮播图的播放速度,通过修改配置文件的内容达到修改应用程序行为的目的。

选择参数和目标

在将应用程序参数化前,需要考虑哪些变量是需要参数化的以及哪些目标是有可能使用到的。所有将来可能进行AB测试的关键地方,都建议将其参数化,比如关键按钮(注册、购买等)的一些属性、控制应用行为的一些关键参数、甚至是推荐算法的一些参数等。这样,当将来要进行AB测试的时候就不再需要发布新本应用了。目标的选择往往是和业务指标相关的一些行为,比如一些按钮的点击,把可能用来衡量结果的目标都考虑进去。

配置优化方案

在选择完需要参数化的变量和目标后,要在编辑器中创建这些选出的变量和目标。目标的创建和上面介绍的一样。下面介绍变量的创建,在设置变量处,点击“创建变量”。

AB测试-全栈APISDK-创建变量

 

在弹出的界面中填写变量名称,选择变量类型和默认值,点击“保存”完成创建。

AB测试-全栈APISDK-创建变量2

 

修改应用程序

在创建完变量和目标之后,就要修改我们的应用程序了。首先是变量的改动,可以通过调用variableString、variationDouble、variationInteger、variationBoolean这四个方法去获取变量,然后在程序中使用这些得到的变量。

NSString *accoutPlaceHolder = [self.eocClient variableString:@"LoginAccountPlaceHolder" userId:self.userId attributes:self.userAttributes activateExperiment:true];
if (accoutPlaceHolder) {
    _loginView.usernameTextField.placeholder = accoutPlaceHolder;
}

NSString *passwordPlaceHolder = [self.eocClient variableString:@"LoginPasswordPlaceHolder" userId:self.userId attributes:self.userAttributes activateExperiment:true];
if (passwordPlaceHolder) {
    _loginView.passwordTextField.placeholder = passwordPlaceHolder;
}

目标的设置方法和非参数化方案相同。

设计实验并配置

完成应用程序的参数化并添加触发目标的代码之后,随时都可以在云眼控制台创建优化方案进行AB测试,而这个过程是不需要更新应用程序的。在假设的基础上设计实验后,可以在编辑器的“设置变量”中添加需要使用的变量,并为各个版本填写相应的值,从而产生不同的版本。之后再添加需要的目标并保存就可以完成实验的配置,最后启动实验。

 

 AB测试-全栈APISDK-添加变量

 

常见问题

Q:上传APP的ipa至iTunes Connect时,如果报错Unsupported Architectures. Your executable contains unsupported architecture ‘[x86_64, i386]’,该如何解决?

A报错原因是APP项目包含x86_64和i386架构(这两种架构只用于模拟器)的Framework,解决办法就是在Archive项目时使用脚本去除这两种架构。首先,在Xcode中选择项目 -> Targets -> Build Phases。接着,在Xcode的菜单栏中选择Editor -> Add Build Phase -> Add Run Script Build Phase。

AB测试-全栈APISDK-创建Run Script

然后,拷贝以下代码并粘贴至Run Script的代码框中。

AB测试-全栈APISDK-Run Script

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done

最后,再次Archive项目并重新上传ipa至iTunes Connect。

 

Demo代码

下载demo代码(包含三个demo对应文中的三种方式)
iOSFullStackActivateDifferentView:非参数化第一种方案
iOSFullStackActivateNormal:非参数化第二种方案
iOSFullStackParameter:参数化方案

 

免费试用,转化率提升就是这样简单快速 立即试用