Skip to main content

Mobile Sync を使用したオフラインでの変更の同期

学習の目的

この単元を完了すると、次のことができるようになります。

  • Mobile Sync の基本的な概念を理解する。
  • Mobile Sync ライブラリを使用して、ユーザーのローカルデバイスと Salesforce サーバー間でデータを同期する。

Mobile Sync を使用したオフラインでの変更の同期

Mobile Sync は、SmartStore と連携して、Salesforce レコードの整合性と一貫性を保証する Mobile SDK ライブラリです。Mobile Sync により、Mobile SDK アプリケーションを使用するお客様のモバイルデバイスの接続が失われたときに、Salesforce レコードを引き続きローカルで編集することが可能になります。接続が回復すると、Mobile SDK アプリケーションはこのフレームワークを使用して、SmartStore 内のローカル変更を Salesforce サーバー上の対応するレコードに同期します。

ネットワーク要求について

Mobile Sync アプリケーションのデータ要求はすべて非同期です。同期メソッドは要求をフォーマットし、Salesforce Cloud に送信します。アプリケーションは、プラットフォームに応じて、サーバーの応答をコールバックメソッド、更新ブロック、promise で受信します。応答には、同期操作の設定と状況を説明する同期状態オブジェクトが含まれます。

Mobile Sync のメソッドにより、sObject メタデータの取得、最近使用されたオブジェクトのリストの取得、SOQL および SOSL クエリの発行など、よく行うネットワーク作業を自動化できます。アプリケーションは同期マネージャーメソッドをいつでもコールできますが、成功するのはホストデバイスがインターネットに接続されている場合のみです。デバイスの接続状況はアプリケーションによって判断されます。

Mobile Sync を使用するアプリケーションの設定

Mobile SDK アプリケーションでは、中央 SDK マネージャーオブジェクトを使用して、適切なアプリケーションの起動と認証のプロトコルを適用します。アプリケーションで使用されるマネージャーによって、そのアプリケーションに含まれる SDK 機能の範囲も決まります。アプリケーションはブートストラップの初期化時に MobileSyncSDKManager オブジェクトを初期化します。

ネイティブ Android および iOS の SDK は、3 つのマネージャーオブジェクトを実装します。

SalesforceSDKManager

ほとんどの基本的なアプリケーション用。これらのアプリケーションは、Mobile SDK のオフライン機能を使用しません。

SmartStoreSDKManager

SmartStore は使用するが Mobile Sync を使用しないアプリケーション用。

MobileSyncSDKManager

SmartStore と Mobile Sync の両方を使用するアプリケーション用。

forcedroid または forceios を使用してネイティブアプリケーションを作成すると、新しいアプリケーションは MobileSyncSDKManager を使用するように自動的に設定されます。このマネージャーは、SmartStore、Mobile Sync、その他のすべての Mobile SDK ライブラリをインポートします。 

同期設定

SmartStore から Salesforce への上位同期を行っている場合でも、Salesforce から SmartStore への下位同期を行っている場合でも、同期設定の詳細を指定します。同期操作はコードまたは JSON 設定ファイルで設定できます。いずれの場合も、Mobile SDK は、実行時にその設定を同期状態オブジェクトで返します。

最も基本的なレベルでは、次のものを指定します。

  • store — ストアカタロググローバルストアとユーザーストアのどちらのストア使用しているかを示します。指定ストアを使用している場合は、その名前も指定します。
  • soup name — スープ名を上位同期操作のソースまたは下位同期操作の受信側となるスープの名前。このスープでは、__local__ という名前のインデックス付けされた文字列項目がサポートされている必要があります。

その他の 2 つのパラメーターは、同期操作の動作を定義します。

  • target — どのレコードを同期するかを示します。ターゲットは、あらゆる同期動作の中心的な設定値です。ターゲットは、操作に必要なデータの範囲を表すものであると考えてください。
  • options — 操作によってどのようにデータがマージされるかを制御します。場合によっては、ターゲットの代わりにオプションを使用して、同期する項目名のリストを指定できます。

target パラメーターと options パラメーターの使用方法は、上位同期と下位同期で異なります。これについては、以降のセクションで説明します。場合によっては、どちらか一方のパラメーターを省略できます。target または options を省略すると、Mobile Sync はデフォルト値を使用します。

同期操作の完了後、アプリケーションはフォローアップタスクの実行を許可する非同期の通知を受信します。このタスクには、イベント通知を送信したり、成功を記録したり、アプリケーションを強化するその他の方法でデータを使用することが含まれます。

下位同期のターゲットとオプション

  • target — ソース sObject 型とスープにダウンロードする項目を指定します。次のいずれかの文字列を使用できます。
    • SOQL クエリの場合: {type:"soql", query:"<soql_query>"}
      指定した SOQL クエリで返された sObject をダウンロードします。
    • SOSL クエリの場合: {type:"sosl", query:"<sosl_query>"}
      指定した SOSL クエリで返された sObject をダウンロードします。
    • MRU クエリの場合: {type:"mru", sobjectType:"<sobject_type>", fieldlist:"<fields_to_fetch>"}
      指定した sObject 型に該当する、最近使用した sObject の、指定した項目をダウンロードします。Mobile SDK は要求を Salesforce に送信し、応答を受信し、設定を使用してスープに入力します。
  • options — (省略可能) 次のキーと対応付けます。
    • mergeMode — Mobile Sync がローカルのスープにデータをマージする方法を制御するには、このキーを次のいずれかのモードに設定します。
      Overwrite
      (デフォルト) 変更されたレコードを上書きします。マージモードを指定しない場合は、Mobile Sync がローカルデータを上書きします。
      Leave if changed
      変更されたレコードを維持します。このモードでは、サーバーへの追加の往復処理が必要になるため、慎重に使用してください。

上位同期のターゲットとオプション

  • target — 次のいずれかまたは両方のキーと対応付けます。
    • createFieldlist — 新しく作成された Salesforce レコードに値が挿入されるスープ項目名のカンマ区切りリスト。ロックされた (参照のみの) 項目を含めることができます。
    • updateFieldlist — 既存の Salesforce レコードの書き込み可能な項目を更新するために使用されるスープ項目名のカンマ区切りリスト。ロックされた (参照のみの) 項目を含めることはできません。
  • target は、一部の上位同期メソッドでは必須ではありません。そのような場合、optionsfieldlist キーを使用して、同期する項目を指定します。
  • options — (省略可能) 次の 1 つ以上のキーと対応付けます。
    • fieldlist — サーバーに送信するスープ項目名のカンマ区切りリスト。このキーを target 項目リストの代わりに使用するか、target の createFieldList と使用できます。上位同期操作によって、サーバーの参照のみの項目を更新しようとしていないことを確認してください。
    • mergeMode — Mobile Sync が Salesforce 組織にローカルデータをマージする方法を制御するには、このキーを次のいずれかのモードに設定します。
      Overwrite
      (デフォルト設定) 変更されたサーバーレコードを上書きします。
      Leave if changed
      変更されたサーバーレコードを維持します。このモードでは、サーバーへの追加の往復処理が必要になるため、慎重に使用してください。

ストアパラメーター

ハイブリッドアプリケーションと React Native アプリケーションは、省略可能な storeConfig パラメーターで使用しているストアを示します。このパラメーターは、上位同期操作と下位同期操作に適用されます。次の 2 つのいずれかの方法で指定できます。

次のキー - 値ペアの対応付け

  • isGlobalStore — (省略可能) グローバルストア (true) とユーザーストア (false) のどちらを使用しているかを示す Boolean 値。デフォルトは false です。
  • storeName — (省略可能) ストアの名前。デフォルト値はデフォルトストアの名前です。

次に例を示します。

{"isGlobalStore" : false, "storeName" : "MyStore"}

Boolean 値

代わりに単純な Boolean 値を渡すことができます。その場合、Mobile SDK は値を isGlobalStore キーに割り当てます。

store パラメーターを省略すると、Mobile Sync が現在のユーザーストアで操作を実行します。

名前付き同期と同期設定ファイルの使用

標準の上位同期メソッドと下位同期メソッドでは、各同期パラメーターを別個の引数として渡す必要があります。Mobile SDK には、同期を整理して合理化する 2 つの機能があります。導入された機能は、同期名同期設定ファイルで、最小限の労力で効果が得られます。設定ファイルを使用することは必須ではありませんが、このトレイルでは幅広く使用します。

同期を再実行するには、プラットフォームの reSync メソッドを使用します。再同期メソッドは、次のいずれかの識別子を使用して設定を見つけます。

  • 同期 ID — すべての同期メソッドは状態オブジェクトの一部としてこの ID を返します。この ID を reSync に渡して、現在のセッションですでに使用した同期を再実行できます。
  • 同期名 (省略可能) — 最初の実行時にメモリ内の同期設定に名前を付けるか、外部 JSON ファイルから名前と同期設定をインポートすることができます。

同期名

同期操作の設定は複雑になることがあり、よく再利用されます。同期名は同期を記録せずに再実行するのに便利です。たとえば、同期名を使用して次のことを実行できます。

  • コードではなく外部 JSON ファイルで同期操作を設定する
  • 以前使用した操作を再設定せずに再実行する
  • 進行中の同期操作の状況を取得する
  • 同期設定の存在を確認する
  • 同期設定を削除する

名前付き同期は、API または同期設定ファイルで作成できます。Mobile SDK では、すべてのプラットフォームおよびすべてのアプリケーション種別で名前付き同期がサポートされています。

同期設定ファイル

同期設定ファイルを使用する場合、静的な上位同期と下位同期定義を JSON ファイルで 1 回記述すれば、アプリケーションのすべてのバージョンでインポートできます。Mobile SDK では、ネイティブおよびハイブリッドのアプリケーションで同期設定ファイルがサポートされています。これらのファイルの構造とルールを見てみましょう。

ファイル名

Mobile SDK では、デフォルトグローバルストアとデフォルトユーザーストアでのみ設定ファイルがサポートされています。

  • デフォルトグローバルストアの場合、globalsyncs.json という名前のファイルを指定します。
  • デフォルトユーザーストアの場合、usersyncs.json という名前のファイルを指定します。

ファイルの場所

設定ファイルは次の場所に置きます。
iOS:

  • ネイティブアプリケーションと React Native アプリケーション: Resources バンドルの /
  • ハイブリッドアプリケーション: Resources バンドルの /www

Android:

  • ネイティブアプリケーションと React Native ネイティブアプリケーション: /res/raw
  • ハイブリッドアプリケーション: /assets/www

ファイル形式

ファイル形式は、アプリケーション種別やターゲットプラットフォームに関係なく同じです。同期定義には次の 5 つの項目が必要です。

  • 同期名
  • 同期種別
  • スープ名
  • 対象
  • オプション

以下の設定ファイルは、下位同期操作と上位同期操作を定義します。両方の設定で同じ項目 (Id、Name、LastModifiedDate) を使用します。ただし、値の配置について次の 2 つの重要なルールがあります。

  • 下位同期設定では「target」メンバーで SOQL クエリを使用して項目を指定するのに対して、上位同期では「options」でリストとして指定します。
  • 下位同期操作では、マージモードを「options」で指定するのに対して、上位同期では「merge_mode」で指定します。

反対のマージモード (下位同期の「OVERWRITE」に対して上位同期の「LEAVE_IF_CHANGED」) に設定することで、一元的で正確な情報を維持できます。この場合、サーバーデータはスープデータを上書きできますが、逆はできません。

{
  "syncs": [
    {
      "syncName": "sync1",
      "syncType": "syncDown",
      "soupName": "accounts",
      "target": {"type":"soql", "query":"SELECT Id, Name, LastModifiedDate 
        FROM Account"},
      "options": {"mergeMode":"OVERWRITE"} 
    },
    {
      "syncName": "sync2",
      "syncType": "syncUp",
      "soupName": "accounts",
      "target": {"createFieldlist":["Name"]},
      "options": {"fieldlist":["Id", "Name", "LastModifiedDate"],
      "mergeMode":"LEAVE_IF_CHANGED"}
    }
  ]
 }

Mobile Sync とブリーフケースの使用

ブリーフケースは、関連レコードのまとまりのあるコレクションを同時に選択する一連のクエリであり、目的に合わせて最適化されます。ブリーフケースは、オフラインになる前のデータプライミングや他の一般的なデータ読み込みの目的でレコードを選択するために使用できます。

同期設定

Mobile Sync Explorer テンプレートアプリケーションで、ユーザー同期の JSON ファイルを更新して下位同期操作と上位同期操作を設定できます。 

"target" プロパティには、次の値を指定します。

target プロパティ

"type":"briefcase"

"infos": <BriefcaseObjectInfo> 項目の配列

取得する特定の sObject と項目およびそれらを配置するスープを記述するオブジェクトの配列。

BriefcaseObjectInfo プロパティ

"soupName": <string>

同期中にこのオブジェクト種別のレコードを保存するスープまたはテーブルの名前。

"sobjectType": <string>

同期する Salesforce オブジェクトの名前。

"fieldlist": <string> の配列

このオブジェクトの同期する項目のリスト。

"idFieldName": <string>

(省略可能) カスタム ID 項目の名前。"idFieldName" を指定した場合、Mobile Sync は、指定された名前の項目を使用して、レコードの ID を取得します。たとえば、"idFieldName":"AcmeId" を指定した場合、Mobile Sync は、デフォルトの Id 項目ではなく、AcmeId 項目からレコードの ID を取得します。

"modificationDateFieldName": <string>

(省略可能) レコードの最終変更日付を含む項目の名前。modificationDateFieldName を指定した場合、Mobile Sync は、この名前の項目を使用して、startFetch がレコードの再同期のために使用する maxTimestamp 値を計算します。デフォルトの項目名は lastModifiedDate です。

必須項目: "soupName"、"sobjectType"、"fieldlist"

下位同期

メモ

組織でモバイルユーザー向けのブリーフケースを使用している場合、Mobile SDK 10.1 でブリーフケース下位同期ターゲットが導入されています。

下位同期用に設定されたブリーフケースファイルの例を見てみましょう。 

{
  "syncs": [
    {
      "syncName": "syncDownContacts", 
      "syncType": "syncDown",
      "soupName": "contacts",
      "target": {
        "type": "briefcase",      
        "infos": [
          {
            "sobjectType": "Account",
            "fieldlist": [
              "Name",
              "Description"
            ],
            "soupName": "accounts"
          },
          {
            "sobjectType": "Contact",
            "fieldlist": [
              “FistName”, "LastName", “Title”, “Email”
            ],
            "soupName": "contacts"
          }
        ]
      },
      "options": {
        "mergeMode": "OVERWRITE"
      }
    }
  ]
}

下位同期するには、“syncType” を “syncDown” として定義します。これにより、下位同期操作が有効になり、データがサーバーからクライアントに同期されます。“syncName” の横に下位同期操作の名前を指定できます。この場合、名前は “syncDownContacts” です。 

“soupName” の横に、下位同期操作が実行されたら更新するテーブルまたはスープを定義します。この場合、更新するスープの名前は “contacts” です。

“target” の下に、ターゲット種別を “briefcase” と定義します。“infos” JSON オブジェクトの下に、同期するエンティティを定義できます。この場合、“Account”“Contact” です。エンティティごとに、同期する項目を “fieldlist” に指定します。 

エンティティごとに、“soupName” の横にクライアント上で更新する、対応するテーブルを指定します。  

“options” の下に “mergeMode” オプションを定義できます。この場合、“OVERWRITE” に設定されています。2 人のユーザーが同じレコードを更新しようとすると、このオプションによって、最後に更新したユーザーに上書き優先権が与えられます。

上位同期

次は、上位同期の設定を見ていきましょう。

{
“syncName: “syncUpContacts”,
“syncType”: “syncUp”,
“soupÑame”: “contacts”,
“target”: {
	“createFieldlist”: [
		“FirstName”, “LastName”, “Title”, “Email”
	]
},
“options”: {
	“fieldlist”: [
		“FirstName”, “LastName”, “Title”, “Email”
	],
	“mergeMode”: “LEAVE_IF_CHANGED”
}
}

上位同期操作を使用するには、“soupName” を介して必要なテーブルを設定します。この場合の名前は “contacts” です。“target”“options” を使用して、同期する項目を指定します。“mergeMode” はこの場合、“LEAVE_IF_CHANGED” に設定されています。サーバーレコードが該当のクライアントに下位同期された後に変更された場合、これにより、サーバーとクライアントレコードの両方が現在の状態のまま保持されます。

iOS ネイティブアプリケーションでの Mobile Sync の使用

下位同期

Mobile Sync には、Salesforce レコードを SmartStore スープにダウンロードするためのさまざまなオプションがあります。

下位同期設定を実行せずに作成できます。

var syncState = syncManager.createSyncDown(target: target, options: options, 
    soupName: CONTACTS_SOUP, syncName: syncState.syncName)

または、オプションを使用せずに名前のない下位同期操作を作成して実行することもできますが、その場合、すべてのローカルな変更が上書きされることに注意してください。

var syncState = syncManager.syncDown(target: target, soupName: CONTACTS_SOUP, onUpdate:updateFunc)

さらに詳細に制御するには、独自のオプションを使用する、名前のない下位同期設定を作成して実行できます。

var syncState = syncManager.syncDown(target: target, options: options, 
    soupName: CONTACTS_SOUP, onUpdate:updateFunc)

または、下位同期設定を作成し、名前を付けて、実行できます。

var syncState = try syncManager.syncDown(target: target, options: options, 
    soupName: CONTACTS_SOUP, syncName: syncState.syncName, onUpdate:updateFunc)

SyncTarget クラスでは、SOQL、SOSL、または MRU クエリ文字列からターゲットオブジェクトを作成するためのファクトリーメソッドが提供されます。SOQL または SOSL クエリ文字列を使用する場合は、ダウンロードするオブジェクトを指定します。MRU クエリターゲットを使用する場合は、項目名のリストのみを指定します。Mobile SDK は、コールバックメソッドまたは実装した更新ブロックを使用して同期操作の進行状況をレポートします。

iOS ネイティブの場合、mergeMode オプションには次のいずれかの値を使用します。

  • SFSyncStateMergeModeOverwrite — 変更されたローカルレコードを上書きし、ローカルの変更をすべて消去します。
  • SFSyncStateMergeModeLeaveIfChanged — すべてのローカルの変更とローカルで変更されたレコードを保持します。
メモ

オプションパラメーターを取らない syncDown(target:soupName:onUpdate:) メソッドによってキャッシュ内にある既存の sObject が上書きされる可能性があります。そのような場合にローカルの変更を維持するには、下位同期する前に必ず上位同期を実行します。

上位同期

上位同期では、Mobile Sync は、作成、更新、削除されたスープレコードをサーバー上に複製します。Mobile SDK では、独自のターゲットを指定する代わりに使用できるデフォルトのターゲットが提供されます。デフォルトターゲットは単純に、Salesforce API を使用して、変更されたすべてのレコードをスープからサーバーに同期するだけです。

ターゲットがある場合は、上位同期設定を実行せずに作成できます。

let syncState = syncManager.createSyncUp(target: target, options: options, 
    soupName: CONTACTS_SOUP, syncName: syncState.syncName)

次の MobileSyncManager メソッドにはデフォルトのターゲットを利用できます。

let syncState = syncManager.syncUp(options: options, soupName: CONTACTS_SOUP, 
    onUpdate: updateFunc)

カスタマイズされた上位同期ターゲットがある場合は、このメソッドをコールして実行できます。

let syncState = syncManager.syncUp(target: target, options: options, 
    soupName: CONTACTS_SOUP, onUpdate: updateFunc)

または、同期操作を作成し、名前を付けて、実行できます。

let syncState = 
    try syncManager.syncUp(target: target, options: options, 
        soupName: CONTACTS_SOUP, syncName: syncState.syncName, 
        onUpdate: updateFunc)

再同期

重要なのに見過ごされがちなのは、再同期メソッドは用途が広く、簡単にコーディングできるということです。これらのメソッドは、別の場所で以前に定義した同期設定を実行します。再同期という名前にもかかわらず、これらのメソッドを使用して名前付き同期を最初に実行することもできます。再同期メソッドは、前回の同期後に作成または更新されたレコードのみを同期します。同期が一度も実行されていない場合は、再同期によってすべての新しいレコードと変更されたレコードがコピーされます。それ以外の場合は、増分同期が実行されます。

再同期メソッドを使用するには、以前の同期の同期 ID または定義済みの同期名を指定します。既存の名前付き同期は次のように実行します。

var syncState = 
    try syncManager.reSync(named: syncState.syncName, onUpdate: updateFunc)

以前の名前のない同期操作で返された SFSyncState.syncId 値をキャッシュしている場合には、それを使用してその同期を再実行できます。

var syncState = 
    try syncManager.reSync(id: syncState.syncId, onUpdate: updateFunc)

reSync メソッドでは、上位同期設定と下位同期設定の両方がサポートされています。

forceios によって作成された Native Swift アプリケーションでは、SmartStore と Mobile Sync の基本機能を示すテンプレートが使用されます。コードを簡略化するために、このテンプレートでは SmartStore と Mobile Sync の Combine パブリッシャーが使用されます。これらのパブリッシャーは、SmartStore クラスと MobileSync クラスの拡張として Mobile SDK で使用できます。これを iOSNativeSwiftTemplate プロジェクトで実装するとどうなるかを見てみましょう。 

最初に、この実装に使用するアプリケーションを作成します。

  1. ターミナルウィンドウで、forceios を使用してアプリケーションを作成します。
    • アプリケーション種別には native_swift を使用します。
    • アプリケーションに offlineSwift と名前を付けます。
  2. forceios が完了したら、Xcode で新しいワークスペースを開きます。

Xcode で、オフライン機能のセットアップと設定を確認します。

  1. プロジェクトナビゲーター (⌘1) で、[offlineSwift] > [offlineSwift] > [Supporting Files (サポートファイル)] を展開します。ここに userstore.json ファイルと usersyncs.json ファイルがあります。エディターでこれらのファイルを開くと、取引先レコードのアクセスと定義の基本設定が定義されていることがわかります。
  2. テンプレートによって userstore.jsonusersyncs.json がプロジェクトの [Build Phases (フェーズの作成)] 設定のアプリケーションバンドルに追加されます。 
    1. Xcode プロジェクトナビゲーターで、プロジェクトノードを選択します。
    2. エディターウィンドウで、[Build Phases (フェーズの作成)] を選択します。
    3. [Copy Bundle Resources (バンドルリソースのコピー)] を展開します。userstore.jsonusersyncs.json が表示されます。つまり、アプリケーションの作成時にこれらがアプリケーションバンドルに追加されます。

では、コードを詳しく見てみましょう。

  1. プロジェクトナビゲーターで、[offlineSwift] > [offlineSwift] > [Classes (クラス)] > [SwiftUI] を展開します。
  2. SceneDelegate.swift で、オフライン設定をメモリに読み込む setupRootViewController() 関数がテンプレートによって追加されます。この関数はユーザーセッションごとに一度だけ意図的にコールされます。この設定メソッドは 1 つのセッションで複数回コールできません。または異なる設定を読み込むために使用できません。 
    // Set up store based on config userstore.json
    MobileSyncSDKManager.shared.setupUserStoreFromDefaultConfig()
    // Set up syncs based on config usersyncs.json
    MobileSyncSDKManager.shared.setupUserSyncsFromDefaultConfig()
  3. プロジェクトナビゲーターで、[offlineSwift] > [offlineSwift] > [Classes (クラス)] > [Models (モデル)] を展開します。
  4. AccountsListModel.swiftで、AccountsListModel クラスの 1 行目を見てください。この行は、取引先オブジェクトの空のパブリッシュ済み配列を定義します。アプリケーションの SwiftUI オブジェクトは、パブリッシュ済み配列が変更されたときに更新を受信するように AccountsListModel にサブスクライブします。
    /**
    ViewModel for Account List
    */
    class AccountsListModel: ObservableObject {
      @Published var accounts: [Account] = []
      ...
  5. fetchAccounts() 関数を見つけます。
    func fetchAccounts(){
      syncTaskCancellable = syncManager?.publisher(for: "syncDownAccounts")
        .receive(on: RunLoop.main)
        .sink(receiveCompletion: { _ in }, receiveValue: { _ in
          self.loadFromSmartStore()
      })
      self.loadFromSmartStore()
    }
    ここで、Mobile Sync パブリッシャーが usersyncs.json で定義された名前付き同期操作 "syncDownAccounts" を取り、再同期します。次にパブリッシャーは返されたデータをデフォルトの SmartStore ストアにあるスープにコピーします。fetchAccounts() がパブリッシャーから応答を受信すると、loadFromSmartStore() をコールして、SmartStore の更新されたデータをパブリッシュ済みの accounts 配列に非同期でコピーします。応答の受信を待っている間、fetchAccounts()loadFromSmartStore() をコールして、accounts 配列にローカルの変更が適用されていることを確認します。では、loadFromSmartStore() がこの更新をどう実行するかを見てみましょう。
  6. loadFromSmartStore() 関数は、SmartStore パブリッシャーを使用して Smart SQL クエリを発行し、非同期応答を受信します。応答が成功を示す場合、関数は結果のデータセットをコール元にパブリッシュします。ここで、コール元が解析済みの応答を AccountsListModel オブジェクトの accounts 配列にコピーします。  
    import MobileSync
    ...
    private func loadFromSmartStore() {
      storeTaskCancellable = self.store?.publisher(for: "select {Account:Name}, {Account:Industry}, {Account:Id} from {Account}")
      .receive(on: RunLoop.main)
      .tryMap {
        $0.map { (row) -> Account in
          let r = row as! [String?]
          return Account(id: r[2] ?? "", name: r[0] ?? "", industry: r[1] ?? "Unknown Industry" )
        }
      }
      .catch { error -> Just<[Account]> in
        print(error)
        return Just([Account]())
      }
      .assign(to: \AccountsListModel.accounts, on:self)
    } 

accounts 配列が入力されたら、アプリケーションはプライマリロジック、この場合は次の操作に進むことができます。 

  • 照会された取引先とその詳細を表示する
  • 顧客が選択した取引先について照会し、取引先責任者とその詳細を表示する。

Android ネイティブアプリケーションでの Mobile Sync の使用

下位同期

サーバーからローカルの Mobile Sync スープに sObject をダウンロードするには、次のいずれかの SyncManager メソッドを使用します。

下位同期設定を実行せずに作成できます。

public SyncState createSyncDown(SyncDownTarget target, 
    SyncOptions options, String soupName, String syncName) 
    throws JSONException;

または、オプションを使用せずに名前のない下位同期操作を作成して実行することもできますが、その場合、すべてのローカルな変更が上書きされることに注意してください。

public SyncState syncDown(SyncDownTarget target, String soupName, 
    SyncUpdateCallback callback) throws JSONException;

さらに詳細に制御するには、独自のマージオプションを使用する、名前のない下位同期操作を作成して実行できます。

public SyncState syncDown(SyncDownTarget target, SyncOptions options,
    String soupName, SyncUpdateCallback callback) 
    throws JSONException;

または、下位同期設定を作成するときに名前を付けてから実行することができます。

public SyncState syncDown(SyncDownTarget target, SyncOptions options, 
    String soupName, SyncUpdateCallback callback) 
    throws JSONException;

上位同期

ローカルの変更をサーバーに適用するには、次のいずれかの SyncManager メソッドを使用します。

  • 上位同期設定を実行せずに作成できます。
    public SyncState createSyncUp(SyncUpTarget target, 
        SyncOptions options, 
        String soupName, 
        String syncName) 
        throws JSONException;
  • 1 回のコールで、名前のない同期設定を作成して実行できます。
    public SyncState syncUp(SyncUpTarget target, 
        SyncOptions options, 
        String soupName, 
        SyncUpdateCallback callback) 
        throws JSONException;
  • または、1 回のコールで、名前付き上位同期設定を作成して実行できます。
    public SyncState syncUp(SyncUpTarget target, 
        SyncOptions options, 
        String soupName, 
        String syncName, 
        SyncUpdateCallback callback) 
        throws JSONException;

syncUp メソッドは、指定した SmartStore スープのデータでサーバーを更新します。このメソッドはスープ内の作成、更新、または削除済みのレコードを検索して、その変更をサーバーに複製します。targetoptions のいずれかまたは両方は、更新する項目のリストを指定します。target では、レコードを作成する createFieldlist と既存のレコードを更新する updateFieldlist を区別できます。これらの設定によって、既存のレコードの参照のみの項目を更新しないようにしながら新しいレコードを同期できます。target 項目リストと options 項目リストを定義すると、options 設定は無視されます。

再同期

重要なのに見過ごされがちなのは、2 つの再同期メソッドは用途が広く、簡単にコーディングできるということです。これらのメソッドは、別の場所で以前に定義した同期設定を実行します。再同期という名前にもかかわらず、これらのメソッドを使用して名前付き同期を最初に実行することもできます。後続の実行では、再同期メソッドは、前の同期後に作成または更新されたレコードのみを同期します。同期が一度も実行されていない場合は、再同期によってすべての新しいレコードと変更されたレコードがコピーされます。それ以外の場合は、増分同期が実行されます。

再同期メソッドを使用するには、以前の同期の同期 ID または定義済みの同期名を指定します。名前付き同期は次のように実行します。

public SyncState reSync(String syncName, SyncUpdateCallback callback) 
    throws JSONException;

以前の同期操作で返された SFSyncState.syncId 値をキャッシュしている場合には、reSync:updateBlock: を使用してその同期を再実行できます。

public SyncState reSync(long syncId, SyncUpdateCallback callback) 
    throws JSONException;

reSync メソッドでは、上位同期設定と下位同期設定の両方がサポートされています。

Android の場合、MobileSyncExplorer ネイティブサンプルアプリケーションは、Contact レコードに名前付き同期および同期設定ファイルを使用する方法を示します。このサンプルでは Salesforce Contact レコードを Java オブジェクトとして表す ContactObject クラスを定義します。Contact データを SmartStore スープと下位同期するために、syncDown() メソッドは、SOQL クエリを定義する名前付き下位同期設定を再同期します。

このアプリケーションは、userstore.json ファイルと usersyncs.json ファイルを /res/raw プロジェクトフォルダーに保存します。以下は userstore.json です。

{
  "soups": [
    {
      "soupName": "contacts",
      "indexes": [
        { "path": "Id", "type": "string"},
        { "path": "FirstName", "type": "string"},
        { "path": "LastName", "type": "string"},
        { "path": "__local__", "type": "string"},
        { "path": "__locally_created__", "type": "string"},
        { "path": "__locally_updated__", "type": "string"},
        { "path": "__locally_deleted__", "type": "string"},
        { "path": "__sync_id__", "type": "integer"}
      ]
    }
  ]
}

以下は usersyncs.json です。

{
  "syncs": [
    {
      "syncName": "syncDownContacts",
      "syncType": "syncDown",
      "soupName": "contacts",
      "target": {"type":"soql", "query":"SELECT FirstName, LastName, Title, MobilePhone, Email, Department, HomePhone FROM Contact LIMIT 10000"},
      "options": {"mergeMode":"OVERWRITE"}
    },
    {
      "syncName": "syncUpContacts",
      "syncType": "syncUp",
      "soupName": "contacts",
      "target": {"createFieldlist":["FirstName", "LastName", "Title", "MobilePhone", "Email", "Department", "HomePhone"]},
      "options": {"fieldlist":["Id", "FirstName", "LastName", "Title", "MobilePhone", "Email", "Department", "HomePhone"], "mergeMode":"LEAVE_IF_CHANGED"}
    }
  ]
 }

このサンプルアプリケーションは、これらのファイルを ContactListLoader コンストラクターのメモリに読み込みます。 

public ContactListLoader(Context context, UserAccount account) {
  super(context);
  MobileSyncSDKManager sdkManager = MobileSyncSDKManager.getInstance();
  smartStore = sdkManager.getSmartStore(account);
  syncMgr = SyncManager.getInstance(account);
  // Setup schema if needed
  sdkManager.setupUserStoreFromDefaultConfig();
  // Setup syncs if needed
  sdkManager.setupUserSyncsFromDefaultConfig();
}

ContactListLoader クラスからの次のメソッドで、ローカルの syncDown() メソッドは syncDownContacts 設定を使用して reSync() を実行します。

public synchronized void syncDown() {
    
    try {
      syncMgr.reSync(SYNC_DOWN_NAME /* see usersyncs.json */, new SyncUpdateCallback() {
            @Override
            public void onUpdate(SyncState sync) {
                if (Status.DONE.equals(sync.getStatus())) {
                    fireLoadCompleteIntent();
                }
            }
        });
    } catch (JSONException e) {
        Log.e(TAG, "JSONException occurred while parsing", e);
    } catch (MobileSyncException e) {
        Log.e(TAG, "MobileSyncException occurred while attempting to sync down", e);
    }
 
}

下位同期操作が成功した場合、つまり sync.getStatus()Status.DONE の場合は、受信したデータが指定されたスープに取り込まれます。その後、コールバックメソッドは、Contact リストのデータを再読み込みするインテントを起動します。

同様に、ContactListLoader のローカルの syncUp() メソッドは syncUpContacts 設定を使用して reSync() を実行します。

public synchronized void syncUp() {
       
    try {
        
        syncMgr.reSync(SYNC_UP_NAME /* see usersyncs.json */, new SyncUpdateCallback() { 
            new SyncUpdateCallback() {

            
                @Override
            
                public void onUpdate(SyncState sync) {
                
                    if (Status.DONE.equals(sync.getStatus())) {
                    
                        syncDown();
                
                    }
            
                }
        
            });
    
    } catch (JSONException e) {
           
        Log.e(TAG, "JSONException occurred while parsing", e);
    
    } catch (MobileSyncException e) {
           
        Log.e(TAG, "MobileSyncException occurred while attempting to sync up", e);
    
    }

 }

非同期応答が Status.DONE を返すと、ここで SyncUpdateCallback 実装は syncDown() をコールする追加ステップを実行します。この推奨ステップにより、SmartStore スープにサーバーでの Contact への最近の変更がすべて反映され、最新の状態であることが保証されます。

ハイブリッドアプリケーションでの Mobile Sync の使用

Mobile Sync には、ハイブリッドアプリケーション向けに 2 つの異なる方法が用意されています。

  • com.salesforce.plugin.mobilesync — この Cordova プラグインは、Mobile Sync のネイティブな「下位同期」および「上位同期」機能への JavaScript アクセスを提供します。そのため、パフォーマンス集約的な操作 (ネットワークネゴシエーション、解析、SmartStore 管理など) がネイティブスレッド上で実行され、Web ビューの操作に影響を与えません。Web ビューではなくネイティブスレッドで大量のレコードを迅速に同期するなど、比較的単純なシナリオではプラグインを使用します。
  • mobilesync.js — この JavaScript ライブラリは、より複雑な同期操作のために Force SObject データフレームワークを提供します。このライブラリは、拡張性の高いデータモデリングメカニズムを定義するオープンソースの JavaScript フレームワークである Backbone.js をベースにしています。mobilesync.js を使用すると、Salesforce オブジェクトのモデルを作成でき、そのモデルのデータを変更するだけで基盤となるレコードを操作できます。SOQL または SOSL クエリを実行すると、JSON 文字列としてではなくモデルコレクションで結果レコードが取得されます。

高い効果を得るために、同じアプリケーション内でこれらの方法を組み合わせることができます。たとえば、プラグインは、syncDown()syncUp() の 2 つのメソッドを公開します。これらのメソッドを使用する場合、次のガイドラインに従うと作業が簡単になります。

  • SmartStore スープのレコードを作成、更新、削除するには、mobilesync.jsForce.SObject を使用します。mobilesync.js ライブラリは、プラグインが想定する特別な項目をスープレコード上に自動的に作成します。その後、プラグインで syncUp() をコールして、サーバーを更新できます。
  • 同様に、同期操作で使用するスープを作成するには、mobilesync.jsForce.StoreCache を使用します。
  • スープのオブジェクトを変更している場合は、syncDown() をコールする前に必ず syncUp() をコールします。

forcehybrid を使用して作成されたハイブリッドアプリケーションには、Mobile Sync プラグインが自動的に含まれます。

下位同期

以下は、Mobile Sync プラグインの loadFromStore() メソッドです。

cordova.require("com.salesforce.plugin.mobilesync").syncDown
    ([storeConfig,] target, soupName, options, [syncName,] success, error);

この設定の同期名が定義されている場合は、その同期名を指定できます。同期名は、Web アプリケーションの最上位に含める usersyncs.json 設定ファイルで定義します。

ハイブリッドアプリケーションでは、マージモードオプションに次の識別子を使用します。

  • {mergeMode:Force.MERGE_MODE_DOWNLOAD.OVERWRITE} (デフォルト)
  • {mergeMode:Force.MERGE_MODE_DOWNLOAD.LEAVE_IF_CHANGED}

success パラメーターは、同期操作中に複数回コールされるコールバック関数を指定します。

  • 同期操作の開始時
  • 内部 REST 要求の完了時
  • 結果の各ページをダウンロード後 (すべての結果を受信するまで)

同期操作の実行中に、状況更新がブラウザーイベントによって通知されます。これらの更新をリスンするには、次のイベントリスナーを実装します。

document.addEventListener("sync",
   function(event)
      {
         // event.detail contains the status of the sync operation
      }
 );

event.detail メンバーは、同期メタデータと、さらに重要な操作の現在の進行状況と状況についての情報を提供します。

  • syncId — この同期操作の ID
  • type—syncDown
  • target — 指定したターゲット
  • soupName — 指定したスープ名
  • options — 指定したオプションの配列
  • status — 同期状況 (次のいずれか)
    1. NEW
    2. RUNNING
    3. DONE
    4. FAILED
  • progress – 現時点で処理済みの合計レコードの割合 (整数、0 ~ 100%)
  • totalSize — 現時点で処理済みのレコード数

同期 ID を保存しておくことをお勧めします。後で、この値を使用して同期操作を再実行できます。

上位同期

Mobile Sync プラグインの syncUp() メソッドは、作成、削除、または更新済みのレコードを SmartStore スープから Salesforce サーバーに複製します。

cordova.require("com.salesforce.plugin.mobilesync").syncUp
   ([storeConfig,] [target,] soupName, options, [syncName,] successCb, errorCb);

操作について、次のような設定を指定します。

  • target — カスタムネイティブ上位同期ターゲットが定義されている場合は、このパラメーターを使用してそのクラスを識別します
  • syncName — この設定の同期名が定義されている場合は、その同期名を指定できます。同期名は、Web アプリケーションの最上位に含める usersyncs.json 設定ファイルで定義します。
  • options — 次のキーとの対応付けを指定します
    1. fieldlist — サーバーに送信される項目のリスト
  • successCberrorCb — 成功とエラーのコールバック関数

同期操作の状況更新は、syncDown に実装したのと同じイベントハンドラーによって通知されます。

document.addEventListener("sync",
   function(event)
      {
         // event.detail contains the status of the sync operation
      }
 );

下位同期操作と同様、event.detail メンバーは、同期メタデータを提供し、操作の現在の進行状況と状況についてのレポートを表示します。

再同期

ネイティブアプリケーションと同様に、reSync メソッドは非常に優れた柔軟性とパフォーマンスを提供し、コーディングをしやすくします。指定した同期名がこれまで同期されていない場合、reSync() は完全な同期を実行します。すでに同期されたことがある場合は、新規レコード、変更されたレコード、削除されたレコードのみの増分同期が実行されます。

cordova.require("com.salesforce.plugin.mobilesync").reSync([storeConfig], syncIdOrName, successCB, errorCB)

操作について、次のような設定を指定します。

  • syncIdOrName — この同期操作の ID または usersyncs.json ファイルの同期名。
  • successCberrorCb — 成功とエラーのコールバック関数

この例では、次の usersyncs.json ファイルで定義される名前付き同期を使用します。完全なソースコードは SalesforceMobileSDK-Shared の GitHub リポジトリにあります。

{
  "syncs": [
    {
      "syncName": "syncDownContacts",
      "syncType": "syncDown",
      "soupName": "contacts",
      "target": {"type":"soql", 
      "query":"SELECT FirstName, LastName, Title, MobilePhone, 
               Email, Department, HomePhone FROM Contact LIMIT 10000"},
      "options": {"mergeMode":"OVERWRITE"}
    },
    {
      "syncName": "syncUpContacts",
      "syncType": "syncUp",
      "soupName": "contacts",
      "target": {
         "createFieldlist":["FirstName", "LastName", "Title", "MobilePhone", 
                            "Email", "Department", "HomePhone"]},
      "options": {
         "fieldlist":["Id", "FirstName", "LastName", "Title", 
                      "MobilePhone", "Email", "Department", "HomePhone"], 
         "mergeMode":"LEAVE_IF_CHANGED"}
    }
  ]
}

次の例では、上位同期操作と下位同期操作の両方で Mobile Sync Cordova プラグインの reSync() をコールし、同期名を渡します。すべての同期操作は、成功コールバックに handleSyncUpdate() 関数を使用します。この関数は、syncUp が正常に実行された後に syncDown() をコールします。スープがサーバー上で最新の変更を反映するように、syncUp() の後に syncDown() をコールすることをお勧めします。 

現在の形式では、syncDown() の成功コールバックによって UI とそのコンテンツがリセットされます。

 handleSyncUpdate: function(sync) {
  // Called after sync operations succeed 
  if (sync.type === "syncDown") {
    // Reset UI buttons, then search (fetch) from SmartStore
    this.syncInFlight = false;
    $(".sync").disabled = false;
    this.search();
  }
  if (sync.type === "syncUp") {
      this.syncDown();
    }
  }
 },
 syncDown: function() {
   cordova.require("com.salesforce.plugin.mobilesync").
     reSync("syncDownContacts" /* see usersyncs.json */, 
     this.handleSyncUpdate.bind(this));
 },
 syncUp: function() {
   cordova.require("com.salesforce.plugin.mobilesync").
     reSync("syncUpContacts" /* see usersyncs.json */, 
     this.handleSyncUpdate.bind(this));
 }

React Native アプリケーションでの Mobile Sync の使用

React Native では、Mobile Sync が mobilesync モジュールによってその機能を提供します。JavaScript ファイル内で、次のように react-native-force ライブラリからこのモジュールとその連動関係をインポートできます。

import {oauth, net, smartstore, mobilesync} from 'react-native-force';

forcereact で作成されたアプリケーションには import ステートメントが含まれますが、smartstoremobilesync は使用されません。Mobile SDK オフライン機能をサポートしている場合は、これらのモジュールを追加してください。

同期設定

ご推察の通り、React Native アプリケーションではハイブリッドアプリケーションと同じ設定メタデータを指定します。次に、ターゲット宣言の例を示します。

 const fieldlist = ["Id", "FirstName", "LastName", 
    "Title", "Email", "MobilePhone","Department",
    "HomePhone", "LastModifiedDate"];
 const target = {type:"soql", query:
    `SELECT ${fieldlist.join(",")} FROM Contact LIMIT 10000`};

options のマージモードには、次のいずれかの値を使用します。

  • 変更されたレコードを上書きする場合
    {mergeMode:mobilesync.MERGE_MODE.OVERWRITE}
    mergeMode キーを定義しなかった場合、Mobile Sync はデフォルトとしてこのモードを使用します。
  • 変更されたレコードを上書きせずに維持する場合
    {mergeMode:mobilesync.MERGE_MODE.LEAVE_IF_CHANGED}

下位同期

React Native の syncDown() 関数は、ハイブリッド関数と同じですが、mobilesync モジュールでコールされるという点のみが異なります。この関数は、指定された Salesforce レコードを SmartStore スープにダウンロードします。

mobilesync.syncDown
    ([storeConfig,] target, soupName, options, [syncName,] success, error);

スープ内の既存のレコードの ID がクエリによって返されたレコードと同じ場合は、Mobile Sync がデフォルトで重複するスープレコードを上書きします。ダウンロードされたデータとスープ内の編集済みのレコードをマージする方法を制御するには、options パラメーターのマージモードを指定します。

success パラメーターは、同期操作が正常に完了したときに内部同期マネージャーがコールするコールバック関数を指定します。ハイブリッド実装とは異なり、React Native ライブラリは、操作の終了時にこの関数を 1 回だけコールします。

Mobile SDK は、1 つの引数を成功コールバックに渡します。この引数は、同期 ID を含む次の同期メタデータを返します。

  • syncId — この同期操作の ID
  • type—syncDown
  • target — 指定したターゲット
  • options — (省略可能) マージモードと項目リストのいずれかまたは両方を指定する配列
  • soupName — 指定したスープ名
  • syncName — 指定した同期名 (ある場合)

成功コールバックにその他のアクションがなかった場合でも、コールバックを実装し、同期 ID を保存しておくことをお勧めします。この値は、後で必要に応じて再同期割り当ての操作に使用できます。次に下位同期の例を示します。

const syncDown = forceUtil.promiserNoRejection(mobilesync.syncDown);
...
const fieldlist = ["Id", "FirstName", "LastName", 
    "Title", "Email", "MobilePhone","Department",
    "HomePhone", "LastModifiedDate"];
const target = {type:"soql", 
    query:`SELECT ${fieldlist.join(",")} 
           FROM Contact 
           LIMIT 10000`};
syncDown(false,
    target,
    "contacts",
    {mergeMode:mobilesync.MERGE_MODE.OVERWRITE},
    syncName,
    (sync) => {/* Do something meaningful or omit this member */},
    (error) => {/* Do something meaningful or omit this member */}
 );

上位同期

Mobile Sync プラグインの syncUp() 関数は、作成、更新、または削除済みのレコードを SmartStore スープから、Salesforce サーバーにコピーします。

mobilesync.syncUp
   ([storeConfig,] target, soupName, options, [syncName,] successCb, errorCb);

下位同期操作と同様に、成功コールバック関数の入力引数には、同期 ID を含む同期メタデータが含まれます。

次に、上位同期の例を示します。

const syncUp = forceUtil.promiserNoRejection(mobilesync.syncUp);
...
const fieldlist = ["FirstName", "LastName", "Title", "Email", 
  "MobilePhone","Department"];
syncUp(false,
  {},
  "contacts",
  {mergeMode:mobilesync.MERGE_MODE.OVERWRITE, fieldlist},
  (sync) => {/* Do something meaningful or omit this callback */},
  (error) => {/* Do something meaningful or omit this callback */}
);

再同期

ネイティブアプリケーションと同様に、reSync() メソッドは非常に優れた柔軟性とパフォーマンスを提供し、コーディングをしやすくします。指定した同期名がこれまで同期されていない場合、reSync() は完全な同期を実行します。すでに同期されたことがある場合は、作成、更新、または削除されたレコードのみの増分同期が実行されます。

mobilesync.reSync
   ([storeConfig,] syncIdOrName, successCB, errorCB); 

操作について、次のような設定を指定します。

  • syncIdOrName — この同期操作の ID または usersyncs.json ファイルの同期名。
  • successCberrorCb — 成功とエラーのコールバック関数

リソース

Salesforce ヘルプで Trailhead のフィードバックを共有してください。

Trailhead についての感想をお聞かせください。[Salesforce ヘルプ] サイトから新しいフィードバックフォームにいつでもアクセスできるようになりました。

詳細はこちら フィードバックの共有に進む