PageSense iOS SDK — React Nativeブリッジ連携ガイド

PageSense iOS SDK — React Nativeブリッジ連携ガイド

お知らせ:当社は、お客様により充実したサポート情報を迅速に提供するため、本ページのコンテンツは機械翻訳を用いて日本語に翻訳しています。正確かつ最新のサポート情報をご覧いただくには、本内容の英語版を参照してください。

概要  

このガイドでは、React Native Native Bridgeを使用して、PageSense iOS SDKをReact Nativeアプリケーションに連携する方法を説明します。このブリッジにより、React NativeのJavaScriptコードからiOS SDKの関数を呼び出し、モバイルアプリケーション内でFullStack実験機能を使用できます。
このガイドを完了すると、React Nativeアプリケーションから次のPageSense SDK関数をJavaScriptから直接呼び出せるようになります。
  • initialiseSDK
  • activateExperimentByContext
  • getVariationNameByContext
  • trackGoalByContext

React Native Native Bridgeとは  

React Native Native Bridgeは、JavaScriptランタイムとネイティブiOSコード間の通信レイヤーです。JavaScriptからネイティブ関数を呼び出し、プラットフォーム固有のSDK機能にアクセスし、ネイティブモジュールから応答を受け取ることができます。
JavaScriptはSwiftと直接やり取りできないため、React Nativeでは中間層としてブリッジが提供されています。ネイティブメソッドはSwiftで実装され、Objective-Cブリッジファイルを通じてReact Nativeに公開されます。

IdeaReact Native UI

React Nativeラッパー(TypeScript/JavaScript)

React Native Native Bridge

Objective-Cブリッジモジュール(.m)

Swiftネイティブモジュール(.swift)

PageSense iOS SDK

ネイティブモジュールはSwiftで実装され、Objective-Cブリッジファイルを介してReact NativeのJavaScriptレイヤーに公開されます。登録されると、このモジュールにはReact NativeのNativeModules APIからアクセスできます。

前提条件  

  • Node.js
  • npm
  • React Native CLI
  • Xcode
  • CocoaPods
bash
node -v
npm -v
pod --version

PageSense iOS SDKのインストール  

PageSense iOS SDKはXCFrameworkとして配布されています。
  1. 次のPageSenseFramework.xcframework.zipZohoPageSenseSDKリポジトリーのFrameworksフォルダーからダウンロードし、内容を展開します。
  2. 展開したフレームワークをiOSプロジェクトフォルダーに配置します。
/ProjectRoot/ios/Frameworks/PageSenseFramework.xcframework
  1. Xcodeでワークスペースを開き、次の場所に移動します。
[Target]→[General]→[Frameworks, Libraries and Embedded Content]
  1. PageSenseFramework.xcframeworkを追加し、オプションを[Embed & Sign]に設定します。

iOS向けReact Nativeライブラリー  

次のReact Nativeライブラリーが必要です。pod installiosディレクトリーから実行すると、自動的にインストールされます。
  • React-Core
  • React-RCTBridge
  • React-RCTNetwork
  • React-RCTText
  • React-RCTImage
React Native Bridgeを実装するには、次のiOSフレームワークの依存関係が必要です。
  • React
  • RCTBridgeModule
  • Foundation

ネイティブブリッジモジュールの作成  

iOSのネイティブブリッジは、SwiftネイティブモジュールとObjective-Cブリッジファイルの2つの部分で実装します。

パート1 — Swiftネイティブモジュール  

PageSenseSDKModule.swiftをiOSアプリケーションディレクトリー内に作成します。
  1. /ProjectRoot/ios/SampleApp/PageSenseSDKModule.swift
  2. swift
  3. import Foundation
  4. import React
  5. import PageSenseFramework
  6. @objc(PageSenseSDKModule)
  7. class PageSenseSDKModule: NSObject {
  8.     private varclient: PageSenseClient?
  9.     private varisInitialized: Bool= false
  10.     private static let LOG_TAG= '[PageSenseSDK]'
  11.     @objc
  12.     static func requiresMainQueueSetup()-> Bool {
  13.         return false
  14.     }
  15.     @objc
  16.     func initialiseSDK(
  17.         _accountId: String,
  18. sdkKey: String,
  19. projectName: String,
  20. resolver resolve: @escaping RCTPromiseResolveBlock,
  21. rejecter reject: @escaping RCTPromiseRejectBlock
  22.     ) {
  23.         if self.isInitialized { resolve(true); return }
  24.         letpageSenseSDKOptions = PageSenseSDKOptions()
  25. pageSenseSDKOptions.logLevel = .DEBUG
  26. pageSenseSDKOptions.pollingInterval = 5
  27.         PageSense.shared.createNewPageSenseClient(
  28. sdkAccountIDForDC:accountId,
  29. sdkKey:sdkKey,
  30. projectname:projectName,
  31. sdkOptions:pageSenseSDKOptions
  32.         ) {pagesenseclient in
  33.             guard letclient = pagesenseclient else { resolve(false); return }
  34.             self.client = client
  35.             self.isInitialized = true
  36.             resolve(true)
  37.         }
  38.     }
  39.     @objc
  40.     func activateExperimentByContext(
  41.         _experimentName: String,
  42. userContext: NSDictionary,
  43. resolver resolve: @escaping RCTPromiseResolveBlock,
  44. rejecter reject: @escaping RCTPromiseRejectBlock
  45.     ) {
  46.         guard letclient = self.client,
  47.               letpageSenseUserContext = validateAndBuildUserContext(userContext) else {
  48.             resolve(nil); return
  49.         }
  50. client.activateExperiment(experimentName:experimentName,userContext:pageSenseUserContext) {variation in
  51.             resolve(variation)
  52.         }
  53.     }
  54.     @objc
  55.     func getVariationNameByContext(
  56.         _experimentName: String,
  57. userContext: NSDictionary,
  58. resolver resolve: @escaping RCTPromiseResolveBlock,
  59. rejecter reject: @escaping RCTPromiseRejectBlock
  60.     ) {
  61.         guard letclient = self.client,
  62.               letpageSenseUserContext = validateAndBuildUserContext(userContext) else {
  63.             resolve(nil); return
  64.         }
  65.         resolve(client.getVariationName(experimentName:experimentName,userContext:pageSenseUserContext))
  66.     }
  67.     @objc
  68.     func trackGoalByContext(_goalName: String,userContext: NSDictionary) {
  69.         guard letclient = self.client,
  70.               letpageSenseUserContext = validateAndBuildUserContext(userContext) else { return }
  71. client.trackGoal(goalName:goalName,userContext:pageSenseUserContext)
  72.     }
  73.     @objc
  74.     func trackGoalWithPropertiesByContext(
  75.         _goalName: String,
  76. userContext: NSDictionary,
  77. goalProperties: NSDictionary
  78.     ) {
  79.         guard letclient = self.client,
  80.               letpageSenseUserContext = validateAndBuildUserContext(userContext),
  81.               letgoalPropertiesMap = validateAndConvertGoalPropertiesNonEmpty(goalProperties) else { return }
  82. client.trackGoal(goalName:goalName,userContext:pageSenseUserContext,goalProperties:goalPropertiesMap)
  83.     }
  84.     private func validateAndBuildUserContext(_userContextDict: NSDictionary)-> PageSenseUserContext? {
  85.         guard letuserId = userContextDict['userId'] as? String,
  86. !userId.trimmingCharacters(in: .whitespaces).isEmpty else { return nil }
  87.         varuserAttributes: [String: String]= [:]
  88.         if letrawAttrs = userContextDict['userAttributes'] as? NSDictionary {
  89.             for (key,value) inrawAttrs {
  90.                 guard letk = key as? String, letv = value as? String else { return nil }
  91. userAttributes[k]= v
  92.             }
  93.         }
  94.         return PageSenseUserContext(userId:userId,userAttributes:userAttributes)
  95.     }
  96.     private func validateAndConvertGoalPropertiesNonEmpty(_goalPropertiesDict: NSDictionary)-> [GoalProperty: String]? {
  97.         ifgoalPropertiesDict.count == 0 { return nil }
  98.         varresult: [GoalProperty: String]= [:]
  99.         for (key,value) ingoalPropertiesDict {
  100.             guard letkeyString = key as? String,
  101.                   letgoalProperty = GoalProperty(rawValue:keyString),
  102.                   letvalueString = value as? String else { return nil }
  103. result[goalProperty]= valueString
  104.         }
  105.         returnresult
  106.     }
  107. }
パート2 — Objective-Cブリッジファイル  

同じディレクトリーにPageSenseSDKModule.mを作成します。
  1. /ProjectRoot/ios/SampleApp/PageSenseSDKModule.m
  2. objc
  3. #import <React/RCTBridgeModule.h>
  4. @interface RCT_EXTERN_MODULE(PageSenseSDKModule,NSObject)
  5. RCT_EXTERN_METHOD(initialiseSDK:(NSString *)accountId
  6. sdkKey:(NSString *)sdkKey
  7. projectName:(NSString *)projectName
  8. resolver:(RCTPromiseResolveBlock)resolve
  9. rejecter:(RCTPromiseRejectBlock)reject)
  10. RCT_EXTERN_METHOD(activateExperimentByContext:(NSString *)experimentName
  11. userContext:(NSDictionary *)userContext
  12. resolver:(RCTPromiseResolveBlock)resolve
  13. rejecter:(RCTPromiseRejectBlock)reject)
  14. RCT_EXTERN_METHOD(getVariationNameByContext:(NSString *)experimentName
  15. userContext:(NSDictionary *)userContext
  16. resolver:(RCTPromiseResolveBlock)resolve
  17. rejecter:(RCTPromiseRejectBlock)reject)
  18. RCT_EXTERN_METHOD(trackGoalByContext:(NSString *)goalName
  19. userContext:(NSDictionary *)userContext)
  20. RCT_EXTERN_METHOD(trackGoalWithPropertiesByContext:(NSString *)goalName
  21. userContext:(NSDictionary *)userContext
  22. goalProperties:(NSDictionary *)goalProperties)
  23. @end
Objective-Cブリッジングヘッダーの追加  

SwiftとObjective-C間の通信を有効にするには、以下を行います。
  1. 次の場所にブリッジングヘッダーファイルを作成します。
/ProjectRoot/ios/SampleApp/SampleApp-Bridging-Header.h
  1. Xcodeでプロジェクトを開き、[Build Settings]に移動します。
  2. [Objective-C Bridging Header]を見つけ、値を以下に設定します。
SampleApp/SampleApp-Bridging-Header.h

GoalPropertyのマッピング  


iOSでは、PageSense SDKはゴールプロパティを[GoalProperty: String]として受け取ります。ここでGoalPropertyは、PageSenseFrameworkによって公開される、小文字のraw値を持つ列挙型です。React Nativeブリッジは、JavaScriptからゴールプロパティをNSDictionaryとして受け取ります。これは文字列キー/valueのペアで構成され、各小文字の文字列キーを対応するGoalProperty列挙型ケースに、GoalProperty(rawValue:)を使用してマッピングします。
JavaScriptキー
文字列値
iOS GoalProperty列挙型ケース
GoalProperty.REVENUE
'revenue'
GoalProperty.revenue

ベストプラクティス  

  • SDKの初期化は、プロジェクトごとにアプリケーションのライフサイクル中に1回だけ行い、実験を有効化する前に実行してください。
  • SDKの初期化に失敗しても、UIのレンダリングをブロックしないでください。必ずフォールバックUIを使用してください。
  • アプリケーションセッション全体でPageSenseClientインスタンスを保持してください。
  • ユーザーごとに一意のユーザーIDを作成し、全体で再利用してください。
  • ユーザーセッションごとに1回だけPageSenseUserContextを構築し、SDK呼び出し全体で再利用してください。
  • 収益ゴールを追跡する場合は、収益額を必ずセント単位の文字列として渡してください。ドル金額に100を掛け、最も近い整数に丸めます。
  • ハードコードされた文字列ではなく、JavaScriptラッパーによってエクスポートされるGoalProperty定数を使用してください。小文字の文字列値は、iOSのGoalProperty列挙型のraw値に直接マッピングされます。
  • nullバリエーションは必ず処理し、安全なデフォルトUIにフォールバックしてください。
  • ネイティブモジュールには、UIの状態ではなくSDK関連のオブジェクトのみを保存してください。
  • 特定のユーザー操作またはイベントが正常に完了した後に、ゴールをトリガーしてください。
  • 新規連携ではすべて、コンテキストベースのSDK関数(activateExperimentByContextgetVariationNameByContexttrackGoalByContext)を使用してください。



このドキュメントは、各手順をスムーズに進められるように作成されています。さらにサポートが必要な場合やご質問がある場合は、support@zohopagesense.com - までお気軽にお問い合わせください。いつでもサポートいたします!