forceios ネイティブアプリケーションの変更
学習の目的
この単元を完了すると、次のことができるようになります。
- ユーザーが Salesforce 取引先責任者を削除できるボタンを追加する。
- 新しい REST 要求を iOS ネイティブテンプレートアプリケーションに追加する。
- REST 応答を処理する。
一緒にトレイルを進みましょう
エキスパートの説明を見ながらこのステップを実行したい場合は、次の動画をご覧ください。これは「Trail Together」(一緒にトレイル) シリーズの一部です。
(この動画は 18:11 の時点から始まります。戻して手順の最初から見直す場合はご注意ください。)
forceios Swift アプリケーションのカスタマイズ
- 選択した取引先責任者レコードを削除する REST 要求を送信する。
- 顧客に削除を確定するよう求める。
この演習を行う場合は、「ネイティブ iOS」の最初の単元で作成した Mobile SDK ワークスペースを使用します。
ビューとモデル
Xcode プロジェクトの Classes フォルダーを開くと、SwiftUI と Models という 2 つのサブフォルダーが表示されます。この 2 つのフォルダーは、その名が示すとおり、SwiftUI アプリケーションのビュー-モデルアーキテクチャを表しています。
- SwiftUI フォルダーには、ビュー定義 (「UI 関連」ともいう) が格納されています。ほとんどの場合は、これらのファイルに SwiftUI のレイアウト設定とそのビジュアル属性が含まれます。SwiftUI のビューは通常、構造体として定義されます。
- Models フォルダーには、SwiftUI の ビューを動作させるデータ機能が格納されています。ビューは、Salesforce 取引先名の取得や取引先責任者レコードの削除などのデータタスクを実行するモデルをコールします。モデルには通常、機能を定義する中心的なクラス定義と、おそらくはデータ組織の構造体がいくつか含まれます。
このテンプレートアプリケーションの各モデルは、ペアのビューとファイル名のプレフィックスを共有します。たとえば、ContactDetailsModel.swift は ContactDetailsView.swift のモデルになります。
削除ボタンの追加
では、ビジュアル面の実装から始めましょう。[Delete Contact (取引先責任者の削除)] ボタンを設計してコーディングしてからテストして、完全に機能させます。
テンプレートの ContactDetailView
構造体は、このファイルの他の場所で定義された項目を集約したリストです。ボタンとこの既存の List
要素を相互に独立した状態で維持するには、この両方をラップする VStack
コンテナを作成します。このコンテナは、コードに要素が配置されるときに垂直方向に並べるように iOS に指示します。たとえば、[Delete (削除)] ボタンをシーンの下部に表示するには、VStack の List
の後に配置します。
SwiftUI では、ボタンコンストラクターに action
引数が必要です。顧客がボタンをタップすると、このアクションが実行されます。ここでは単に、顧客のタップを確認するメッセージを Xcode コンソールに送信します。
- Xcode プロジェクトエクスプローラーで、[Classes (クラス)] > [SwiftUI] > [ContactDetailsView.swift] を開きます。
-
ContactDetailView
構造体までスクロールします。この構造体は、次の文字列を含む行で始まります。struct ContactDetailView: View {
-
var body: some View
の定義で、List
宣言をVStack
でラップします。List
の前のreturn
キーワードを削除します。 -
var body: some View { VStack(alignment: .center, spacing: 3) { List { FieldView(label: "First Name", value: contact.FirstName) FieldView(label: "Last Name", value: contact.LastName) FieldView(label: "Email", value: contact.Email) FieldView(label: "Phone Number", value: contact.PhoneNumber) AddressView(contact: contact) } } }
-
List ブロックの下にある VStack の閉じ中括弧の直前に
Button
ブロックを追加します。var body: some View { VStack(alignment: .center, spacing: 3) { List { FieldView(label: "First Name", value: contact.FirstName) FieldView(label: "Last Name", value: contact.LastName) FieldView(label: "Email", value: contact.Email) FieldView(label: "Phone Number", value: contact.PhoneNumber) AddressView(contact: contact) } Button(action:{ print("Delete Contact button tapped.")}) { Text("Delete Contact") .bold() .font(.title) .padding() .foregroundColor(Color.white) .background(Color.gray) } } }
-
ContactDetailView
定義の先頭に、deleteWarning
という非公開の状態変数を追加して、false
に設定します。この変数によってアクションシートの表示が制御されます。struct ContactDetailView: View { @State private var deleteWarning = false
- 定義した Button アクションで、
deleteWarning
をtrue
に設定します。顧客がボタンをタップすると、この割り当てが実行されます。Button(action:{ self.deleteWarning = true print("Delete Contact button tapped.")})
-
VStack
ブロックの閉じ中括弧の後に、アクションシート定義を追加します。.actionSheet(isPresented: $deleteWarning) { ActionSheet(title: Text("Deleting Contact"), message: Text("This action deletes this contact in your org."), buttons: [ .cancel {}, .default(Text("OK")) { // TO DO } ] ) }
この時点までのビュー定義は次のようになります。
var body: some View { VStack(alignment: .center, spacing: 3) { List { FieldView(label: "First Name", value: contact.FirstName) FieldView(label: "Last Name", value: contact.LastName) FieldView(label: "Email", value: contact.Email) FieldView(label: "Phone Number", value: contact.PhoneNumber) AddressView(contact: contact) } Button(action:{ self.deleteWarning = true print("Delete Contact button tapped.")}) { Text("Delete Contact") .bold() .font(.title) .padding() .foregroundColor(Color.white) .background(Color.gray) } } .actionSheet(isPresented: $deleteWarning) { ActionSheet(title: Text("Deleting Contact"), message: Text("This action deletes this contact in your org."), buttons: [ .cancel {}, .default(Text("OK")) { // TODO! } ] ) } }
- [Run (実行)] をクリックし、アプリケーションでエラーを確認します。ここまでの作業にエラーがなければ、iPhone Simulator が起動し、数秒後に Salesforce のログイン画面が表示されます。
- 開発者組織にログインして、データへのアクセスを承認します。
- [取引先] ビューで、任意の取引先名をクリックして取引先責任者リストを表示します。
- 任意の取引先責任者の名前をクリックすると、詳細が表示されます。現在開いているのが ContactDetails ビューです。
- [Delete Contact (取引先責任者の削除)] ボタンをクリックします。このボタンが表示されていない場合は、コードを再確認します。
- ボタンが正しく設定されていることを確認するには、Xcode デバッグコンソールに「Delete Contact button tapped」という行があるかどうかを確認します。
- 色を変えたい場合は、ボタンの
foregroundColor
プロパティとbackground
プロパティを変更します。変更を確認するために、アプリケーションを終了して再起動します。
次に、このボタンによって実際に取引先責任者レコードが削除されるようにします。
削除要求の Salesforce への送信
取引先責任者レコードを削除するには、コードをモデルソースファイルに追加します。Classes/Models/ContactDetailModel.swift ファイルを参照すると、ContactDetailModel
クラスのコードが最小限であることがわかります。
class ContactDetailModel: ObservableObject{ @Published var contact: Contact init(with contact: Contact){ self.contact = contact } }
self.contact
プロパティの Id
メンバーで、現在表示しているレコードを特定できます。
REST 要求を作成するには、RestClient.shared.requestForDelete(withObjectType:objectId:apiVersion:)
メソッドをコールします。この要求は、成功した場合にはコール元に何ら重要な情報が返されないという点で、Salesforce API の中でも珍しいものです。アプリケーションが解析やリリースするデータパッケージを受信しないため、この REST 応答はシンプルな完了クロージャーで処理できます。
- Xcode プロジェクトエクスプローラーで、[Classes (クラス)] > [SwiftUI] > [ContactDetailsModel.swift] を開きます。
-
ContactDetailModel
クラスの既存のコードで、deleteContact
という新しい空のfunc
を定義します。この新しいメソッドはContact
種別のcontact
引数を取り、void
を返します。 -
class ContactDetailModel: ObservableObject{ @Published var contact: Contact init(with contact: Contact){ self.contact = contact } func deleteContact(contact: Contact) -> Void { } }
- 新しい関数の最上部で、REST API メソッドをコールし、
request
をRestRequest
オブジェクトに定義します。func deleteContact(contact: Contact) -> Void { let request = RestClient.shared.requestForDelete(withObjectType: "Contact", objectId: contact.Id, apiVersion: nil) }
- Salesforce に要求を送信するには、
RestClient.shared.send(request:_:)
関数をコールします。1 つ目の引数は、先ほど作成したフォーマット済みの要求です。2 つ目の引数は、完了クロージャーのスタブです。func deleteContact(contact: Contact) -> Void { let request = RestClient.shared.requestForDelete(withObjectType: "Contact", objectId: contact.Id, apiVersion: nil) RestClient.shared.send(request: request) {result in } }
- 完了クロージャーに
.success(_)
と.failure(_)
の 2 つのケースを処理するswitch
ブロックを入力します。この簡単な演習では、それぞれの結果の状況メッセージを Xcode デバッグコンソールに出力します。 -
func deleteContact(contact: Contact) -> Void { let request = RestClient.shared.requestForDelete(withObjectType: "Contact", objectId: contact.Id, apiVersion: nil) RestClient.shared.send(request: request) {result in switch result { case .success(_): print("Contact deleted.") case .failure(_): print("Your attempt to delete this contact could not be completed. This is possibly because it is associated with cases or other dependencies.") } } }
deleteContact(_:)
メソッドをコールすることです。このコールをどこで実行するかお分かりになりますか? アクションシートを思い出してください。- ContactDetailsView.swift ファイルを開きます。
-
.actionSheet
定義までスクロールします。アクションシートのbuttons
配列で、.default
ボタン ([OK] ボタン) に空のクロージャーがあります。 -
.default
クロージャーにコールを追加します。最初に、ローカルのcontact
変数を渡すContactDetailModel
のインスタンスを作成し、次にモデルインスタンスでdeleteContact(_:)
をコールします。 -
.actionSheet(isPresented: $deleteWarning) { ActionSheet(title: Text("Deleting Contact"), message: Text("This action deletes this contact in your org."), buttons: [ .cancel {}, .default(Text("OK")) { let model = ContactDetailModel(with: contact) model.deleteContact(contact: contact) } ] ) }
この成果品はまだ本番品質ではありません。顧客が取引先責任者リストビューに戻って取引先責任者が表示されていないことを確認するまで、正常に削除されたかどうかがわかりません。また、取引先責任者がビューから消えていないことに気付いた場合に、何がいけなかったのか見当がつきません。この点を修正可能と思われる 1 つの方法が、RestClient.shared.send(request:_:)
ステートメントを Combine
パブリッシャーに置き換えて、削除コールの結果を公開することです。続いて、ContactDetailView
の ビュー UI またはアラートボックスで結果を顧客に表示します。
リソース
- 開発者ガイド: REST 応答の処理
- 開発者ガイド: ネイティブ Swift テンプレート
- 開発者ガイド: Managing Model Data in Your App (アプリケーションでのモデルデータの管理) (iOS Developer Library)
- 外部リンク: Closures (クロージャー) (The Swift Programming Language)