Skip to main content

SmartStore を使用した安全なオフラインストレージの実装

学習の目的

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

  • SmartStore の基本的な用語と概念を理解する。
  • SmartStore を使用して SELECT クエリを発行する。
  • 任意の対象プラットフォーム (iOS、Android、ハイブリッド、React Native) で SmartStore データを登録、入力、管理する。
  • SmartStore インスペクターを使用する。

オフラインストレージのための SmartStore の使用

モバイルデバイスの接続はいつでも失われる可能性があり、病院や飛行機などの環境では接続が禁止されることがよくあります。このような状況に対処するには、モバイルアプリケーションがオフラインになっても引き続き動作するようにすることが重要です。 

Mobile SDK は、モバイルデバイス向けの安全なマルチスレッドオフラインストレージソリューションである SmartStore を備えています。SmartStore により、ユーザーはデバイスの接続が失われても安全な環境で作業を続行できます。SmartStore を Mobile Sync と組み合わせると、接続が再開されたときにローカルの SmartStore データを Salesforce サーバーと簡単に同期できます。

SmartStore は、単純な 1 つのテーブルのデータベースにデータを JSON ドキュメントとして保存します。Mobile SDK ヘルパーメソッドか、SmartStore の Smart SQL 言語を使用するカスタムクエリのいずれかを使用して SmartStore データを取得できます。

スープ

SmartStore は、スープと呼ばれる論理コレクションにオフラインデータを保存します。SmartStore スープは、基盤となる SQLite データベース (ストア) の 1 つのテーブルを表し、通常は標準またはカスタム Salesforce オブジェクトに対応付けられます。スープにはスープ要素が含まれます。各要素は、1 つのデータベース行を反映する JSON オブジェクトです。データアクセスを合理化するには、各スープのインデックスを定義します。SmartStore ヘルパーメソッドまたは SmartStore の Smart SQL クエリ言語でこれらのインデックスを使用してスープを照会します。SmartStore インデックスはまた、全文検索クエリをサポートするため、管理業務が容易になります。

スープはテーブル、ストアはデータベースと考えるとわかりやすいでしょう。スープはアプリケーション内に必要なだけ定義できます。スープは自己完結型のデータセットであるため、相互の定義済みリレーションはありませんが、Smart SQL 結合を使用してそれらを照会できます。また、ネイティブアプリケーションでは 1 つのトランザクション内で複数のスープに出力できます。

メモ

SmartStore データは揮発性です。ほとんどの場合、この有効期限は、認証されたユーザーおよび OAuth トークンの状態に関係します。ユーザーがアプリケーションからログアウトすると、そのユーザーに関連付けられているスープデータは SmartStore によりすべて削除されます。同様に、OAuth 更新トークンが取り消されるか期限切れになると、ユーザーのアプリケーション状態がリセットされ、SmartStore のすべてのデータが消去されます。アプリケーションを設計するときは、特に組織で更新トークンの有効期限を短期に設定する場合に、SmartStore データの揮発性に留意します。

Smart SQL

SmartStore では、自由形式の SELECT ステートメントに Smart SQL クエリ言語をサポートしています。サポートされるのは、SELECT ステートメントのみです。Smart SQL では、すべての標準の SQL SELECT 文法と特別な構文を組み合わせてスープおよびスープ項目を参照します。この方法により、結合が使用可能になるなど、制御および柔軟性が最大限に高まります。

構文

構文は標準 SQL SELECT の仕様と同じですが、次の点で違いがあります。

使用方法 構文
列を指定する {<soupName>:<path>}
テーブルを指定する {<soupName>}
スープエントリの JSON 文字列全体を参照する {<soupName>:_soup}
内部スープエントリ ID を参照する {<soupName>:_soupEntryId}
最終更新日を参照する {<soupName>:_soupLastModifiedDate}

サンプルクエリ

Employees および Departments という名前の 2 つのスープについて考えます。Employees スープには、次の標準項目が含まれています。

  • 名 (firstName)
  • 姓 (lastName)
  • 部署コード (deptCode)
  • 従業員 ID (employeeId)
  • マネージャー ID (managerId)

Departments スープには、次の項目が含まれています。

  • 名 (name)
  • 部署コード (deptCode)

次に、これらのスープを使用した基本的な Smart SQL クエリをいくつか示します。

select {employees:firstName}, {employees:lastName} 
 from {employees} order by {employees:lastName}
select {departments:name} 
 from {departments} 
 order by {departments:deptCode}

結合

Smart SQL では、ユーザーが結合を使用することもできます。次に例を示します。

select {departments:name}, {employees:firstName} || ' ' || {employees:lastName}  
 from {employees}, {departments}  
 where {departments:deptCode} = {employees:deptCode}  
 order by {departments:name}, {employees:lastName}

さらに、自己結合を行うこともできます。

select mgr.{employees:lastName}, e.{employees:lastName}  
 from {employees} as mgr, {employees} as e  
 where mgr.{employees:employeeId} = e.{employees:managerId}
メモ

JSON1 インデックスで結合を行うには、若干拡張された構文が必要です。たとえば、

select {soup1:path1} from {soup1}, {soup2}

ではなく、次を使用します。

select {soup1}.{soup1:path1} from {soup1}, {soup2}

集計関数

Smart SQL では次のような集計関数の使用がサポートされています。

  • COUNT
  • SUM
  • AVG

次に例を示します。

select {account:name}, 
    count({opportunity:name}),
    sum({opportunity:amount}),
    avg({opportunity:amount}),
    {account:id},
    {opportunity:accountid} 
 from {account},
    {opportunity} 
 where {account:id} = {opportunity:accountid} 
 group by {account:name}

スープの登録

スープを使用する前に、登録する必要があります。スープが存在していない場合は、登録によってスープが作成されます。スープが存在する場合は、登録することでスープにアクセスできます。スープを登録するには、JSON 設定ファイルを作成し、実行時に読み込みます。1 つの設定ファイルでアプリケーションのすべてのスープを定義できます。Mobile SDK では、ネイティブおよびハイブリッドのアプリケーションで設定ファイルがサポートされています。

設定ファイルは、スープをデフォルトのユーザーストアまたはデフォルトのグローバルストアのみに登録します。その他の指定ストアや外部ストアを使用するには、SmartStore API を使用してスープを登録する必要があります。Mobile SDK には、スープの名前とインデックスを定義し、スープを登録するメソッドが用意されています。

スープを登録する場合、データを待機しているメモリ内に空の名前付き構造を作成します。通常、Salesforce 組織のデータを使用してスープを初期化します。Salesforce データを取得するには、Mobile SDK の標準 REST 要求メカニズムを使用します。REST の成功応答が返されたら、応答オブジェクトのデータを抽出して、スープに更新/挿入します。

スープの作成時にエラーが発生する場合、次のようなさまざまな理由が考えられます。

  • スープ名が無効または不正である
  • インデックスがない (少なくとも 1 つのインデックス指定が必要)
  • データベースエラーなど、他の予期しないエラーが発生した

スープの構造

スープを定義するには、スープ名と 1 つ以上のインデックス指定のリストを指定します。インデックスはスープ項目に基づいています。スープに保存するすべての項目のインデックス指定を指定する必要はありません。たとえば、シンプルなキー/値ストアとしてスープを使用している場合は、string 型の 1 つのインデックス指定を使用します。スープが作成されると、SmartStore はインデックスを使用して、すべての挿入、更新、削除操作を追跡します。

SmartStore では、次のインデックスデータ型がサポートされます。

  • string
  • integer
  • floating
  • full_text
  • json1

インデックスの定義

ネイティブおよびハイブリッドのアプリケーションでは、設定ファイルを使用してスープを登録し、インデックスを定義できます。React Native アプリケーションでは、JavaScript コードを使用します。すべての場合に適用されるいくつかのルールがあります。

  • インデックスパスでは大文字と小文字が区別され、Owner.Name などの複合パスを含めることができます。
  • インデックス指定配列で記述された項目が不足しているインデックスエントリは、そのインデックスで追跡されなくなります。
  • インデックスのタイプは、インデックスにのみ適用されます。インデックス付き項目 (「select {soup:path} from {soup}」など) をクエリすると、インデックス指定で指定した型のデータが返されます。
  • インデックス列には null 項目を含めることができます。
  • Mobile SDK 9.1 以降、SELECT 句または WHERE 句で参照される項目のインデックスパスは不要になりました。
  • 内部 (非リーフ) ノードを参照するインデックスパスを指定できます。内部パスは、like および match (全文) クエリで使用できます。内部ノードパスを定義する場合は、string 型を使用します。たとえば、「spies」という名前のスープ内の次の要素について考えます。
    {  
       "first_name":"James",
       "last_name":"Bond",
       "address":{  
          "street_number":10,
          "street_name":"downing",
          "city":"london"
       }
     }
    この場合、「address」には子があるため、これは内部ノードです。パス「address」のインデックスを通じて、like または match クエリを使用して「address」内の「city」の値である「london」を見つけることができます。次に例を示します。
    SELECT {spies:first_name, spies:last_name} FROM spies WHERE {spies:address} LIKE 'london'

設定ファイルの形式

次に、2 つのスープ (soup1soup2) を定義し、インデックスデータ型の全範囲を示す JSON 設定ファイルの理論的な例を示します。

{  "soups": [
    {
      "soupName": "soup1",
      "indexes": [
        { "path": "stringField1", "type": "string"},
        { "path": "integerField1", "type": "integer"},
        { "path": "floatingField1", "type": "floating"},
        { "path": "json1Field1", "type": "json1"},
        { "path": "ftsField1", "type": "full_text"}
      ]
    },
    {
      "soupName": "soup2",
      "indexes": [
        { "path": "stringField2", "type": "string"},
        { "path": "integerField2", "type": "integer"},
        { "path": "floatingField2", "type": "floating"},
        { "path": "json1Field2", "type": "json1"},
        { "path": "ftsField2", "type": "full_text"}
      ]
    }
  ]
 }

設定ファイルではなくコードを使用したスープの登録についての詳細は、『Salesforce Mobile SDK 開発ガイド』を参照してください。

メモ

コードと設定ファイルの両方が同じ名前でスープを登録すると、Mobile SDK は設定ファイル内の定義を無視します。

次の設定ファイルは、取引先レコードに基づいて 1 つのスープを登録します。このスープは、名前、ID、所有者 (または親) ID 項目をインデックス付けします。スープ名をソースの Salesforce オブジェクトに一致させる必要はありませんが、通常は似ている名前にすることをお勧めします。

{  "soups": [
    {
      "soupName": "account",
      "indexes": [
        { "path": "Name", "type": "string"},
        { "path": "Id", "type": "string"},
        { "path": "OwnerId", "type": "string"}
      ]
    }
  ]
 }

スープエントリの挿入または更新

スープエントリを挿入または更新するには、upsert メソッドを使用します (挿入と更新のどちらのアクションが適切かは SmartStore が判断します)。たとえば、ハイブリッドアプリケーションでは次のいずれかの JavaScript バージョンを使用できます。

navigator.smartStore.upsertSoupEntries(isGlobalStore, soupName, 
    entries[], successCallback, errorCallback)
navigator.smartStore.upsertSoupEntries(storeConfig, soupName, 
    entries[], successCallback, errorCallback)

スープの名前と JSON 文字列形式のエントリの配列を指定し、必要に応じて成功とエラーのコールバック関数も指定します。2 つのメソッドの唯一の違いは最初のパラメーターです。スープが指定ユーザーまたはグローバルストアに存在する場合は、storeConfig パラメーターを使用してストアの種類と名前を指定します。次に例を示します。

{isGlobalStore:true, name:"AcmeSales"}

デフォルトのグローバルストアまたはユーザーストアを使用している場合は、true を渡すだけでグローバルストアであることを示すことができます。そうでない場合は、引数を省略できます。このパラメーターは省略可能で、デフォルトは false です。ネイティブメソッドでは、このパラメーターは存在せず、同じ情報を関連オブジェクトから読み込みます。

SmartStore のクエリと管理

iOS ネイティブアプリケーション用の Mobile SDK では、入力に基づいて「クエリ指定」オブジェクトを作成するファクトリーメソッドが用意されています。ファクトリーメソッドは、次の WHERE 演算子に基づいてクエリを作成します。

  • = (完全一致演算子)
  • LIKE
  • MATCH (全文検索拡張)
  • BETWEEN、<=、>= (範囲クエリ)
  • ALL

これらのクエリオブジェクトは、シンプルで単純なクエリを処理するため、それらを自分で記述する手間が省けます。より複雑な状況でのニュアンスを反映するには、独自の Smart SQL クエリを記述し、それを Smart SQL ファクトリーメソッドに渡します。クエリオブジェクトを取得した後、それを実行メソッドに渡して指定されたスープデータを取得します。

スープの管理

使用されていないデータをクリーンアップしたり、インデックスを変更してスープのパフォーマンスを改善したりする必要がある場合があります。そのために、SmartStore には、診断、メンテナンス、管理タスクを実行する一連のメソッドが用意されています。これらのメソッドを使用すると、スープやストアに関する情報の取得、スープの構造の編集、またはスープやストアの削除を行うことができます。

具体的には、これらのメソッドを使用して次のことを実行できます。

  • 基盤となるデータベースのサイズを取得する
  • すべてのストア (ユーザーまたはグローバル) のリストを取得する
  • 指定した名前のスープが存在するかどうかを確認する
  • スープ指定とそのインデックス指定を取得する
  • スープの設定を変更する
  • スープの再インデックス付けを行う
  • スープからすべてのレコードをクリアする
  • ストアからスープを削除する
  • ストアを削除する
  • すべてのストア (ユーザーまたはグローバル) を削除する

これらのメソッドは、すべてのプラットフォームのすべてのアプリケーション種別で使用できます。

SmartStore インスペクターの使用

テスト中、SmartStore データがコードで意図したとおりに処理されているかどうかを確認できると便利です。SmartStore インスペクターには、そのための UI クラスが用意されています。次の操作を実行できます。

  • スープメタデータ (スープの名前やインデックス指定など) を検証する。
  • スープのコンテンツをクリアする。
  • Smart SQL クエリを実行する。

最も簡単にアクセスするには、[Dev Support (開発サポート)] ダイアログボックスから SmartStore インスペクターを起動します。 

[Dev Support (開発サポート)] メニューへのアクセス方法は、開発環境によって異なります。ダイアログボックスを開くには、次のいずれかの方法を使用します。

Android

次のいずれかの操作を実行します。

  • Android エミュレーターでアプリケーションを実行中に、Command+m (Mac) または Ctrl+m (Windows) キーボードショートカットを使用します。
  • システムコマンドシェルで、adb shell input keyevent 82 を実行します。

iOS

  • 物理デバイス上で、シェイク操作を使用して [Dev Support (開発サポート)] メニューを表示し、[SmartStore Inspector (SmartStore インスペクター)] を選択します。
  • iOS シミュレーターで、[Hardware (ハードウェア)] > [Shake Gesture (デバイスのシェイク)] を使用するか、^+Command+z キーボードショートカットを使用します。

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

SmartStore モジュールを新しい iOS ネイティブアプリケーションに追加するために特に何かをする必要はありません。作成する forceios ネイティブアプリケーションに SmartStore ライブラリが自動的に含まれます。

設定ファイルを使用したスープの登録

iOS ネイティブアプリケーションの場合、Mobile SDK は Resources バンドルの / (最上位) の下で設定ファイルを探します。

  1. 設定ファイルをプロジェクトに追加します。
    1. Xcode プロジェクトナビゲーターで、プロジェクトノードを選択します。
    2. エディターウィンドウで、[Build Phases (フェーズの作成)] を選択します。
    3. [Copy Bundle Resources (バンドルリソースのコピー)] を展開します。
    4. [+] ([Add items (項目を追加)]) をクリックします。
    5. スープ設定ファイルを選択します。Xcode プロジェクトフォルダーにまだファイルが準備できていない場合は、次の手順を実行します。
      1. ファインダーでファイルを選択するには、[Add Other... (その他を追加...)] をクリックします。
      2. グループを作成するように要求されたら、[Finish (完了)] をクリックします。
  2. 指定したファイルごとに 1 行のコードを追加します。
    • userstore.json ファイルを読み込むには、次のいずれかのコードを使用します。
      SmartStoreSDKManager.shared().setupUserStoreFromDefaultConfig()
    • globalstore.json ファイルを読み込むには、次のいずれかのコードを使用します。
      SalesforceManager.shared().setupGlobalStoreFromDefaultConfig()

スープの入力

スープを登録する場合、データを待機しているメモリ内に空の名前付き構造を作成します。スープに Salesforce からのデータを入力するには、標準 REST 要求メカニズムを使用してデータを取得します。REST の成功応答が返されたら、応答オブジェクトのデータを抽出して、スープに更新/挿入します。コーディングについての詳細は、この iOS セクションの末尾の例を参照してください。

スープデータのクエリ

iOS では、SFQuerySpec クラスのクラスメソッドをコールすることによってクエリ指定オブジェクトを作成します。たとえば、Objective-C で newSmartQuerySpec:withPageSize: メソッドは、指定された Smart SQL クエリ文字列をカプセル化する SFQuerySpec オブジェクトを返します。

var querySpec = store.buildSmartQuerySpec(
    smartSql: "select {account:Name} from {account}",
    pageSize: 10)

ページサイズパラメーターによって各結果ページで送信されるレコード数が決まります。これらのメソッドでは、独自の Smart SQL SELECT ステートメントを指定するため、他のクエリファクトリー関数に比べて柔軟性が高まります。たとえば、次のコードでは、SQL COUNT 関数をコールするクエリを発行します。COUNT は 1 つの値を返すため、可能なページサイズは 1 のみになります。

次のコードでは、SQL COUNT 関数をコールするクエリを発行します。COUNT は 1 つの値を返すため、可能なページサイズは 1 のみになります。

クエリを実行するには、SFQuerySpec オブジェクトを、SFSmartStore オブジェクトの query() メソッドに渡します。

var querySpec = store.buildSmartQuerySpec(
    smartSql: "select count(*) from {employees}",
    pageSize: 1)

スープの管理

iOS ネイティブアプリケーションで Objective-C スープ管理 API を使用するには、SmartStore/SFSmartStore.h をインポートします。SFSmartStore 共有インスタンスでスープ管理メソッドをコールします。次のいずれかの SFSmartStore クラスメソッドを使用して、共有インスタンスを取得します。

現在のユーザーの SmartStore インスタンスを取得する場合

var store = SmartStore.shared(withName:storeName)

指定したユーザーの SmartStore インスタンスを取得する場合

var store = SmartStore.shared(withName:storeName, forUserAccount:user)

たとえば、removeSoup: 管理メソッドをコールする場合

self.store = [SFSmartStore sharedStoreWithName:kDefaultSmartStoreName];
 if ([self.store soupExists:@"Accounts"]) {
    [self.store removeSoup:@"Accounts"];
 }

この例では、Swift アプリケーションを作成します。forceios のネイティブ Swift テンプレートは SmartStore 実装にすでに含まれているため、設定の確認だけしておきます。 

  1. forceios を使用して、次の例のようなネイティブ Swift プロジェクトを作成します。
    $ forceios create Enter your application type (native_swift or native, leave empty for native_swift):<Press RETURN>
    Enter your application name:<Enter any name you like>
    Enter your package name:com.myapps.ios
    Enter your organization name (Acme, Inc.):MyApps.com
    Enter output directory for your app (leave empty for the current directory):<Press RETURN or enter a directory name>
  2. Xcode で、アプリケーションの .xcworkspace ファイルを開きます。
  3. project フォルダーで、[Supporting Files (サポートファイル)] を展開します。
  4. userstore.json ファイルを開き、SmartStore 設定を調べます。
  5. コンパイルされたアプリケーションのバンドルに設定ファイルを追加するために、テンプレートではプロジェクト設定でこれらのファイルが参照されます。
    1. Xcode プロジェクトナビゲーターで、プロジェクトの最上位ノードを選択します。
    2. エディターウィンドウで、[Build Phases (フェーズの作成)] を選択します。
    3. [Copy Bundle Resources (バンドルリソースのコピー)] を展開します。
    4. userstore.json がリストに含まれています。
  6. プロジェクトのソースコードフォルダーで、Classes/SceneDelegate.swift を選択します。
  7. scene(_:willConnectTo:options:) メソッドで、setupRootViewController() へのコールを見つけます。
  8. SmartStore 設定ファイルが読み込まれいている場所を確認するには、control キーを押しながら setupRootViewController() をクリックして [Jump to Definition (定義へ移動)] を選択します。

実行時にアプリケーションの SmartStore 設定を表示する手順は、次のとおりです。 

  1. アプリケーションを起動します。
  2. ログインしてアプリケーションを認証した後、Dev Tools メニューを表示します。
    1. control + command + z キーを押すか (iOS エミュレーターを使用している場合)、iOS デバイスをシェイクします。
    2. [Inspect SmartStore (SmartStore の検査)] をクリックします。
    3. スープのリストと各スープのレコード数を表示するには、[Soups (スープ)] をクリックします。

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

デフォルトでは、すべての forcedroid ネイティブアプリケーションに SmartStore ライブラリと Mobile Sync ライブラリが含まれます。ただし、古い Android アプリケーションでは、カスタム設定の手順が必要になる場合があります。

  1. Android ネイティブプロジェクトで MainApplication.java を開きます。
  2. 次のインポートステートメントを追加します (ない場合)。
    import com.salesforce.androidsdk.mobilesync.app.MobileSyncSDKManager;
  3. initNative() をコールしている行を見つけます。次に例を示します。
    SalesforceSDKManager.initNative(getApplicationContext(), new NativeKeyImpl(), MainActivity.class);
  4. initNative()SalesforceSDKManager でコールされている場合、SalesforceSDKManagerMobileSyncSDKManager に変更します。
    MobileSyncSDKManager.initNative(getApplicationContext(), new NativeKeyImpl(), MainActivity.class);

設定ファイルを使用したスープの登録

  1. スープ設定ファイルを /res/raw/ プロジェクトフォルダーに置きます。
  2. 指定したファイルごとに 1 行のコードを追加します。
    • userstore.json ファイルを読み込むには、次のコードを使用します。
      SmartStoreSDKManager.getInstance().setupUserStoreFromDefaultConfig();
    • globalstore.json ファイルを読み込むには、次のコードを使用します。
      SmartStoreSDKManager.getInstance().setupGlobalStoreFromDefaultConfig();

スープの入力

スープを登録する場合、データを待機しているメモリ内に空の名前付き構造を作成します。スープに Salesforce からのデータを入力するには、標準 REST 要求メカニズムを使用してデータを取得します。REST の成功応答が返されたら、応答オブジェクトのデータを抽出して、スープに更新/挿入します。

public void populateAccounts() throws UnsupportedEncodingException {
    final RestRequest restRequest =
        RestRequest.getRequestForQuery(
            ApiVersionStrings.getVersionNumber(SalesforceSDKManager.getInstance().getAppContext()), 
            "SELECT Name, Id, OwnerId FROM Account");
    client.sendAsync(restRequest, new RestClient.AsyncRequestCallback() {
        @Override
        public void onSuccess(RestRequest request, RestResponse result) {
            result.consumeQuietly(); // always call before switching to main thread (unlike here)
            try {
                JSONArray records = result.asJSONObject().getJSONArray("records");
                insertAccounts(records);
            } catch (Exception e) {
                onError(e);
            } finally {
                Log.println(Log.INFO, "REST Success!", "\nSmartStore insertion successful");
            }
        }
        @Override
        public void onError(Exception e)
        {
            Log.e(TAG, e.getLocalizedMessage());
        }
    });
 }
/**
 * Inserts accounts into the accounts soup.
 *
 * @param accounts Accounts.
 */
 public void insertAccounts(JSONArray accounts)
 {
    try {
        if (accounts != null) {
            for (int i = 0; i < accounts.length(); i++) {
                if (accounts.get(i) != null) {
                    try {
                        smartStore.upsert("Accounts", accounts.getJSONObject(i));
                    }
                    catch (JSONException exc) {
                        Log.e(TAG, "Error occurred while attempting to insert account. "
                                +  "Please verify validity of JSON data set.");
                    }
                }
            }
        }
    }
    catch (JSONException e) {
        Log.e(TAG, "Error occurred while attempting to insert accounts. "
                + "Please verify validity of JSON data set.");
    }
 }

Smart SQL によるスープデータのクエリ

Android では、QuerySpec クラスの静的ファクトリーメソッドをコールすることによってクエリ指定オブジェクトを作成します。たとえば、buildSmartQuerySpec メソッドは、指定されたクエリ文字列をカプセル化する Smart SQL オブジェクトを作成します。

public static QuerySpec buildSmartQuerySpec(String smartSql, int pageSize)

クエリを実行するには、返された QuerySpec オブジェクトを SmartStore.query() メソッドに渡します。この関数では独自の Smart SQL SELECT ステートメントを指定するため、他のクエリファクトリー関数に比べて柔軟性が高まります。pageSize パラメーターによって各結果ページで送信されるレコード数が決まります。

QuerySpec オブジェクトを使用してクエリを実行するには、クエリを SmartStore オブジェクトの query() メソッドに渡します。次のコードでは、SQL COUNT 関数をコールするクエリを発行します。COUNT は 1 つの値を返すため、可能なページサイズは 1 のみになります。

try {
    JSONArray result =
        store.query(QuerySpec.buildSmartQuerySpec(
            "select count(*) from {Accounts}", 1), 0);
    // result should be [[ n ]] if there are n employees
    Log.println(Log.INFO, "REST Success!", "\nFound " + 
        result.getString(0) + " accounts.");
 } catch (JSONException e) {
    Log.e(TAG, "Error occurred while counting the number of account records. "
        +  "Please verify validity of JSON data set.");
 }

スープの管理

Android ネイティブアプリケーションでスープ管理 API を使用するには、SmartStore 共有インスタンスで次のメソッドをコールします。

SmartStore smartStore = 
    SmartStoreSDKManager.getInstance().getSmartStore();
 smartStore.clearSoup("user1Soup");

SmartStore サポートを forcedroid ネイティブアプリケーションに追加するのは簡単です。JSON インポートファイルを再設定して、各 sObject クエリに 1 つずつ、合わせて 2 つのスープを作成しましょう。次に、リストビューの入力と同時にスープに入力できます。

  1. Android Studio で、プロジェクトディレクトリを開きます。
  2. app/res フォルダーで、「raw」という名前のフォルダーを作成します。
  3. app/res/raw を右クリックし、[New (新規)] > [File (ファイル)] を選択します。ファイルに「userstore.json」という名前を付けます。
  4. 新しいファイルに次のテキストを追加します。
    { "soups": [
        {
        "soupName": "Account",
        "indexes": [
            { "path": "Name", "type": "string"},
            { "path": "Id", "type": "string"},
            { "path": "OwnerId", "type": "string"},
            ]
        },
        {
        "soupName": "Contact",
        "indexes": [ 
            { "path": "Name", "type": "string"},
            { "path": "Id", "type": "string"},
            { "path": "OwnerId", "type": "string"},
            ]
        }
     ]}
  5. MainActivity.java を開き、次のファイルをインポートします。
    import com.salesforce.androidsdk.smartstore.app.SmartStoreSDKManager;
     import com.salesforce.androidsdk.smartstore.store.IndexSpec;
     import com.salesforce.androidsdk.smartstore.store.QuerySpec;
     import com.salesforce.androidsdk.smartstore.store.SmartStore;
     import com.salesforce.androidsdk.smartstore.ui.SmartStoreInspectorActivity;
  6. MainActivity クラスの上部で、SmartStore 共有インスタンスを参照する非公開変数と、処理中の sObject を追跡する非公開変数を宣言します。
    private SmartStore smartStore;private String objectType;
  7. onCreate(Bundle savedInstanceState) メソッドで、userstore.json ファイルからスープ定義をインポートします。
    smartStore = SmartStoreSDKManager.getInstance().getSmartStore();
     if (!smartStore.hasSoup("Account") && !smartStore.hasSoup("Contact")) {
        SmartStoreSDKManager.getInstance().setupUserStoreFromDefaultConfig();
     } else {
        // Delete existing records in preparation for new server data
        smartStore.clearSoup("Account");
        smartStore.clearSoup("Contact");
     }
  8. onFetchContactsClick(View v) メソッドで、Contact スープをクリアして重複レコードを作成しないようにします。
    smartStore.clearSoup("Contact");
    objectType = "Contact";
  9. onFetchAccountsClick(View v) メソッドで、Account スープをクリアして重複レコードを作成しないようにします。
    smartStore.clearSoup("Account");
    objectType = "Account";
  10. client.sendAsync() メソッドで、listAdapter オブジェクトに JSON レスポンスを挿入する for ループで upsert() をコールします。
    for (int i = 0; i < records.length(); i++) {
         listAdapter.add(records.getJSONObject(i).getString("Name"));
        try {
             smartStore.upsert((objectType, records.getJSONObject(i));
         } catch (Exception e) {
             onError(e);
         }
    }
  11. アプリケーションを起動し、Dev Tools メニューを使用して作業結果を確認します。
    • メニューを表示するには、Command + m キー (Mac) または Ctrl + m キー (Windows) を押します。
    • [Inspect SmartStore (SmartStore の検査)] をクリックします。
    • スープのリストと各スープのレコード数を表示するには、[Soups (スープ)] をクリックします。
  12. メモ: 「Query: No soups found (クエリ: スープが見つかりません)」が表示される場合は、userstore.json ファイルにエラーがある可能性があります。

これで、2 つの SmartStore スープの作成と入力が完了しましたが、この時点ではまだ役には立ちません。実際には、Account リストと Contact リストの編集インターフェースを作成し、顧客の変更を SmartStore に更新/挿入します。顧客のデバイスが再び接続されたら、Mobile Sync を使用して変更をサーバーにマージできます。

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

設定ファイルを使用したスープの登録

ハイブリッドアプリケーションでは、Mobile SDK で自動的に SmartStore 設定ファイルが読み込まれます。ユーザーは、次の手順で、必要な場所に設定ファイルを置いておく必要があります。

  1. 設定ファイルをハイブリッドプロジェクトの最上位の www/ ディレクトリ (MyProject/www/ など) にコピーします。
  2. ターミナルウィンドウまたは Windows コマンドプロンプトで、プロジェクトディレクトリに移動します (MyProject/ など)。
  3. cordova prepare を実行します。

スープの入力

ハイブリッドアプリケーションでは、Salesforce レコードを取得するために JavaScript ライブラリの標準 force.query() 関数を使用します。レコードセットからのデータをスープに更新/挿入するには、成功コールバックを使用します。

force.query("SELECT Name,Id FROM Contact", 
    onSuccessSfdcContacts, onErrorSfdc); 
var sfSmartstore = function() {
    return cordova.require("com.salesforce.plugin.smartstore");};
function onSuccessSfdcContacts(response) {
    logToConsole()("onSuccessSfdcContacts: received " + 
        response.totalSize + “ contacts");
    var entries = [];
    response.records.forEach(function(contact, i) {
           entries.push(contact);
    });
    if (entries.length > 0) {
        sfSmartstore().upsertSoupEntries(CONTACTS_SOUP_NAME,
            entries,
            function(items) {
                var statusTxt = "upserted: " + items.length + 
                    " contacts";
                logToConsole()(statusTxt);
            }, 
         onErrorUpsert);
    }
 }
function onErrorSfdc(param) {
    logToConsole()("onErrorSfdc: " + param);
 }
function onErrorUpsert(param) {
    logToConsole()("onErrorUpsert: " + param);
 }

Smart SQL によるスープデータのクエリ

ハイブリッドアプリケーションでは、com.salesforce.plugin.smartstore プラグインの SmartStore オブジェクトの関数をコールすることによって、クエリ指定オブジェクトを作成します。たとえば、buildSmartQuerySpec() 関数は、Smart SQL クエリを実行します。

smartstore.buildSmartQuerySpec(smartSql, [pageSize])

ここで、smartSql は実行するクエリです。この関数では独自の SELECT ステートメントを指定するため、他のクエリファクトリー関数に比べて柔軟性が高まります。pageSize は省略可能で、デフォルトは 10 です。

次のコードでは、employees というスープに対して Smart SQL COUNT 関数をコールするクエリを発行します。

var querySpec = 
    navigator.smartstore.buildSmartQuerySpec(
        "select count(*) from {employees}", 1);
navigator.smartstore.runSmartQuery(querySpec, function(cursor) { 
    // result should be [[ n ]] if there are n employees
 });

スープの管理

JavaScript の各スープ管理関数が 2 つのコールバック関数を取ります。1 つは要求されたデータを返す成功コールバックで、もう 1 つはエラーコールバックです。成功コールバックは、そのコールバックを使用するスープ管理関数によって異なります。エラーコールバックは、エラーの説明文字列を含む 1 つの引数を取ります。たとえば、エラーコールバック関数は次のように定義できます。

function(e) { alert(“ERROR:“ + e);}

JavaScript でスープ管理関数をコールするには、最初に Cordova プラグインを呼び出して SmartStore オブジェクトを初期化します。次に、SmartStore オブジェクトを使用してスープ管理関数をコールします。次の例では、名前付きのコールバック関数を個別に定義していますが、匿名のコールバック関数をインラインで定義することもできます。

var sfSmartstore = function() {
    return cordova.require("com.salesforce.plugin.smartstore");};
function onSuccessRemoveSoup(param) {
    logToConsole()("onSuccessRemoveSoup: " + param);
    $("#div_soup_status_line").html("Soup removed: " 
        + SAMPLE_SOUP_NAME);
 }
function onErrorRemoveSoup(param) {
    logToConsole()("onErrorRemoveSoup: " + param);
    $("#div_soup_status_line").html("removeSoup ERROR");
 }
sfSmartstore().removeSoup(SAMPLE_SOUP_NAME,
     onSuccessRemoveSoup, 
     onErrorRemoveSoup);

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

React Native アプリケーションには、ハイブリッドアプリケーションと多くの共通点があります。通常、この 2 つのプラットフォームの SmartStore 関数の署名は同じです。ただし、いくつかの大きな違いがあります。

  • React Native アプリケーションは ES2015 JavaScript が使用されます。ハイブリッドアプリケーションと同じ JavaScript 構文を使用することもできますが、合理化された新しいコーディング規則を利用することもできます。
  • JavaScript に加えて、React Native アプリケーションは静的型付けに TypeScript もサポートしています。TypeScript をどの程度使用するか、またはまったく使用しないかを選択できます。
  • React Native では、SmartStore は backbone.js を使用しません。
  • React Native では、Cordova ハイブリッドライブラリまたはプラグインは使用できません。その代わりに、React Native SmartStore モジュールをインポートします。

SmartStore API を使用するには、smartstore モジュールをインポートします。Salesforce レコードを取得するためのクエリを実行するために最も重要な Salesforce API を使用するには、net モジュールをインポートします。両方のモジュールを 1 つのステートメントでインポートできます。

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

スープの登録

React Native については、設定ファイルではなく、JavaScript コードを使用して SmartStore スープを設定します。次に例を示します。

smartstore.registerSoup(false,
    "contacts", 
    [ {path:"Id", type:"string"}, 
    {path:"FirstName", type:"full_text"}, 
    {path:"LastName", type:"full_text"},    
    {path:"__local__", type:"string"} ],
    () => syncDown()
 );

スープの入力

スープに Salesforce データを入力するには、まず標準 Salesforce API を使用してレコードをクエリします。net モジュールには、ネットワークコールを簡略化するラッパー関数セットが含まれていて、それに対して、クエリ文字列と成功およびエラーコールバックを渡します。成功コールバックでは、smartstore モジュールを使用して、クエリ応答からのレコードをスープに更新/挿入します。(この戦略を聞いたことがあるということは、ハイブリッドセクションを読んだということですね。)

net.query("SELECT Name,Id FROM Contact", 
        onSuccessSfdcContacts, onErrorSfdc);

次に、成功コールバックの例を示します。

function onSuccessSfdcContacts(response) {
    logToConsole()("onSuccessSfdcContacts: received " + 
        response.totalSize + “ contacts");
    var entries = [];
    $.each(response.records, function(i, contact) {
           entries.push(contact);
           logToConsole()("name: " + contact.Name);
    });
    if (entries.length > 0) {
        smartstore().upsertSoupEntries(CONTACTS_SOUP_NAME,
            entries,
            function(items) {
                var statusTxt = "upserted: " + items.length + 
                    " contacts";
                logToConsole()(statusTxt);
            }, 
            onErrorUpsert);
    }
 }

Smart SQL によるスープデータのクエリ

React Native では、smartstore モジュールの関数をコールすることによってクエリ指定オブジェクトを作成します。たとえば、buildSmartQuerySpec() 関数は、Smart SQL クエリオブジェクトを作成します。

buildSmartQuerySpec(smartSql, [pageSize])

この関数では、smartSql は実行するクエリです。この関数では独自の SELECT ステートメントを指定するため、他のクエリファクトリー関数に比べて柔軟性が高まります。pageSize は省略可能で、デフォルトは 10 です。

スマートクエリオブジェクトを作成した後、それを runSmartQuery() 関数に渡し、応答を処理する成功コールバックを指定します。次のコードでは、SQL COUNT 関数をコールするクエリを作成して実行します。

var querySpec = 
    smartstore.buildSmartQuerySpec(
        "select count(*) from {employees}", 1);
// Note that the first parameter--a storeConfig object, or a Boolean indicating whether to use 
// the default global store or the default user store--is required in React Native apps
// but is optional in hybrid apps
smartstore.runSmartQuery(false, querySpec, function(cursor) { 
    // result should be [[ n ]] if there are n employees
 });

SmartStore 管理関数の使用

スープ管理関数は、他の React Native 関数と同じパターンに従います。成功およびエラーコールバックを定義し、smartstore モジュールの関数をコールします。成功コールバックに渡されるパラメーターの数は関数によって異なります。エラーコールバックは、常にエラーの説明文字列の引数のみを取ります。

リソース

無料で学習を続けましょう!
続けるにはアカウントにサインアップしてください。
サインアップすると次のような機能が利用できるようになります。
  • 各自のキャリア目標に合わせてパーソナライズされたおすすめが表示される
  • ハンズオン Challenge やテストでスキルを練習できる
  • 進捗状況を追跡して上司と共有できる
  • メンターやキャリアチャンスと繋がることができる