2015年11月30日月曜日

Swift と ストーリーボードで iOS アプリを作成(2):データモデルの作成

前回は新規のiOSアプリ・プロジェクトを作成して、ストーリーボードで、メインビュー(タブバー・コントローラ)とそれに関連する3つのサブビュー(ナビゲーション・コントローラ+テーブルビュー・コントローラ)を追加しました。そしてマスターブランチとしてコミットしました。今回はテーブルビュー・コントローラでデータを表示できるようにしたいと思います。プロジェクトに変更を加える前に、新しいブランチ(dataModel) を作成して、変更はこのブランチに対して行います。これで、変更に失敗したり、ストーリーを変更したくなったらいつでも元のブランチに戻ってやり直すことができます。

新しいブランチを作成

メニューバーの Source Control > プロジェクト名 > New Branch... を選択して:
新規ブランチ名 を "dataModel" にします:

変更を加える

新しく変更を加えるためのブランチが作成できたので、作業を開始します。今回行う作業の概要は以下のようになります。
  1. テーブルビューのデータソースを設定して、
    • 製品一覧テーブルビューに製品一覧を表示します
    • 映画前売り券一覧テーブルビューに映画前売り券一覧を表示します
具体的な作業を順を追って見ていきます。

1. データモデルの作成

このサンプルアプリで使用する3つの簡単なドメインクラスを作成します。
  • 製品: {ID, 製品名, 単価, 在庫の有無, [ コメント, ...]}
  • コメント: {ID, コメント文}
  • 映画前売り券: {タイトル, 画像, レーティング}
上記の各クラスに対応するファイルを作成します。
  • model グループの作成
    プロジェクト・ナビゲーターで IOSSample101 フォルダを右クリックしてポップアップメニューから "New Group" を選択して、作成されたグループの名前を "model" に変更します。
  • ドメインクラスの作成
    (コメントクラス)
    作成した model グループを右クリックしてポップアップメニューの "New File..." を選択します。表示されたダイアログで iOS/Source/Swift File テンプレートを選択して、Next をクリックします。次のページでファイル名を "Comment.swift" として Create をクリックします。
    作成された Comment.swift を以下のように修正します:
    • 1行:- デフォルトで、Swift ファイルはFoundation フレームワークをインポートするので、コード内で Foundation データ構造を使用できる。
ここでは UIKit のデータを使うので UIKit (Foundationにもアクセスできる)に変更。
    • 5-6行:プロパティ定義:値が変更できるように、定数(let)ではなく変数(var)を使用。
    • 9-12行:クラスのインスタンスを生成するイニシャライザ。

    (製品クラス) コメントクラスと同様にして、model グループに "Product.swift" ファイルを作成して、以下のように修正します:
    (映画前売り券クラス) 同様に "MovieT.swift" ファイルを作成して、以下のように修正します:
    • 7行:前売り券の画像(photo) をオプショナル型(型の後ろに ? をつける)として宣言します。photo 変数は UIImage 型に加えて、空の状態(nil)を保持できます。
      • オプショナルバインディング:
        オプショナル型は、if の条件で使用できる。値が nil の場合はfalse。
        var data: String? = "data"
        if let responseData = data {..}
      • アンラップ:
        オプショナル型を計算で使用する場合などは、アンラップ(変数の後に ! をつける)が必要。ビルドエラーになる。
    • 18-20行:イニシャライザで、プロパティに不正な値(空の名前やマイナスのレーティング)が設定された場合に、インスタンス生成失敗を示す nil を返す。
    • 11行:init の後ろに(?)マークをつける。これは初期化の後で、イニシャライザが nil を返すことができることを表します。

Swift の class についてはこちらのページを参照してください。

2.リポジトリの作成

上記のデータモデルに対して CRUD 処理を行うクラス(リポジトリ)を作成します。
  • repo グループの作成
    プロジェクト・ナビゲーターで IOSSample101 フォルダを右クリックしてポップアップメニューから "New Group" を選択して、作成されたグループの名前を "repo" に変更します。
  • 製品リポジトリでは、REST API により、製品一覧情報を JSON として取得します。このアプリでは JSON を簡単に扱うためのユーティリティとして、https://github.com/dankogai/swift-json/ を使用しています。Util グループを作成して、json.swift をコピーします。
  • リポジトリクラスの作成
    (製品リポジトリ)
    作成した repo グループを右クリックしてポップアップメニューの "New File..." を選択します。表示されたダイアログで iOS/Source/Swift File テンプレートを選択して、Next をクリックします。次のページでファイル名を "ProductRepo.swift" として Create をクリックします。
    作成された ProductRepo.swift を以下のように修正します:
    • 17-76行:製品リポジトリ(ProductRepo)の findAll() 関数は Spring boot を使って作成した RESTful Web Service サンプル・アプリ(bootRestDb)から製品一覧情報を取得します。
    • 21行:REST_URL は bootRestDb REST サービスの製品一覧を取得するための URL です。
      mac の ターミナル.app で bootRestDb アプリを起動して:
      $ java -jar bootRestDb-0.0.1.jar
      
      別の ターミナル.app で curl を使ってこの URL にアクセスすると以下のようなレスポンスが返されます(青字の部分が製品一覧配列)。
      curl http://localhost:9901/bootRestDb/products
      {
        "_links" : {
          "self" : {
            "href" : "http://localhost:9901/bootRestDb/products{?page,size,sort}",
            "templated" : true
          },
          "search" : {
            "href" : "http://localhost:9901/bootRestDb/products/search"
          }
        },
        "_embedded" : {
          "products" : [ {
            "version" : 1,
            "name" : "製品1000",
            "price" : 10000,
            "inStock" : "o",
            "comments" : [ ],
            "_links" : {
              "self" : {
                "href" : "http://localhost:9901/bootRestDb/products/1"
              },
              "product" : {
                "href" : "http://localhost:9901/bootRestDb/products/1/product"
              }
            }
          }, {
            "version" : 1,
            "name" : "製品1001",
            "price" : 10100,
            "inStock" : "x",
            "comments" : [ {
              "text" : "製品1001コメント0"
            } ],
            "_links" : {
              "self" : {
                "href" : "http://localhost:9901/bootRestDb/products/2"
              },
              "product" : {
                "href" : "http://localhost:9901/bootRestDb/products/2/product"
              }
            }
          }
          ...
          ]
        ...
      }
    • 22-25行:上の URL を NSURL 型に変換して正しい URL 文字列かチェックします。
    • 29-73行:上記 URL へのリクエストを発行するタスクを定義して、それを実行します。
    • 37-71行:リクエストの結果、データ(data) が取得できたら、JSONデータとして解析を行います(40,44,..などの print 文は取得データをXcode のコンソールで確認するためのものです)。
    • 41行:取得データの解析にユーティリティの JSON を使用します。
    • 42行:ターミナル.app で確認したように取得したレスポンスデータの製品データ配列は "_embedded" の "products" で取り出せます。
    • 46-64行:製品データ配列から各要素(prods[i])を取り出して、製品オブジェクト(Product)を作成して、製品オブジェクト配列(products) に追加します。
    class のプロパティとメソッドついてはこちらのページを参照してください。

    (映画前売り券リポジトリ) 上と同様にして、 "MovieRepo.swift" を作成して、以下のように修正します:
    • 6行:movies 変数:映画前売り券オブジェクト(MovieT)の配列を宣言します。
    • 9-20行:イニシャライザ init() で、映画前売り券一覧・テーブルビューに表示するダミーデータを作成して、movies 配列に追加します。
    • 10行:前売り券画像(UIImage) を photo1 定数に代入します。ここでは、このサンプルプロジェクトのアセットカタログに登録したイメージの名前を指定して UIImage を生成しています。アセットカタログへのイメージの登録はこちらを参照してください
    • 23-26行:findAll() 関数は movies 配列を返します。


3.App Transport Security の設定

製品リポジトリ(ProductRepo)は REST サービスに HTTP でアクセスします。iOS9 以降では、HTTP通信を行うと自動的にHTTPS通信に置き換えられるため、アクセスに失敗して、製品一覧データは返されません。簡単な対応方法を幾つか説明します。 プロジェクト・ナビゲータで info.plist を選択して、ポップアップメニュー > Open As > Source Code を選択して、info.plist のソースコード(XML)を表示します。
  • 対応方法1:以下のように修正して、ATSを無効にします:
    製品レベルでは推奨しませんが、サンプルアプリで、製品リポジトリの機能をテストするだけならこれでも十分です。

  • 対応方法2:info.plist を次のように修正。これはドメインを指定してATSを無効にします:
    このアプリがアクセスする RESTサービスは、開発マシン上で起動しているので、localhost に対してのみ ATSを無効にします。このアプリではこちらの方法を使用します。


4.データモデルとリポジトリのテスト

データモデルとリポジトリが作成できたので、アプリに統合する前に単体テストを行って、モデルとリポジトリが思った通りに機能するか確認します。
( テストクラスの作成)
  • IOSSample101Tests フォルダを開いて、IOSSample101Tests.swift ファイルを次のように修正します:
    • 1行:XCTest( Xcode のテスト・フレームワーク)をインポートします。
    • 2行:@testable はテストしたいモジュールをインポートします。これにより、このテストクラスで、 ProductRepo, Product などが使用可能になります。
    • 4行:テストクラスは IOSSample101Tests は XCTestCase を継承します。
    • 7-21行:ProductRepo の findAll 関数をテストします。
      このリポジトリに対するテストは、 REST サービス(bootRestDb)のレスポンスの内容に依存するので、ターミナル.app やブラウザなどで、返される内容を確認してテストケースを作成します。
    • 12行:取得した製品一覧配列の件数をテストします。
    • 14-16行:配列要素の1件目の名前、単価、コメント数をテストします。
    • 24-34行:MovieRepo の findAll 関数をテストします。
(単体テストの実行)
  • Command+U を押すことで、全単体テストを同時に実行できます。
    開発マシンで bootREstDb を起動していない場合、タイムアウトでデータの取得に失敗するため、テストは失敗します。
  • まず、bootRestDb を起動します。
    $ java -jar bootRestDb-0.0.1.jar
    
    再度 Command+U を押して、テストを実行します。
    testProductRepoFindAll(), testMovieFindAll() ともテストは成功します。

変更をコミットする

この時点で今回プロジェクトに加えた変更をコミットします。

次回はリポジトリで取得したデータをテーブルビューに表示します。

参考:

0 件のコメント:

コメントを投稿