2015年2月26日木曜日

Swiftでパラメータ付きPOSTリクエストを行う

SwiftでWebAPIのリクエストを行う際、POSTメソッドを使う必要がありました。
次は非同期でリクエストを行う、NSURLConnectionクラスのsendAsynchronousRequestメソッドを用いたサンプルソースです。

        // URLセット
        let url = NSURL(string: "https://test.url.jp")
        var request : NSMutableURLRequest = NSMutableURLRequest(URL: url!)

        // POSTメソッド指定
        request.HTTPMethod = "POST"
        
        // POSTパラメータ
        var bodyData: String = "key1=value1&key2=value2"
        request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
        // ヘッダの指定
        request.setValue("HeaderValue", forHTTPHeaderField: "HeaderName")
        
        //asyncで実行
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: responseHandler)

2015年2月24日火曜日

SwiftなCocoa Touch FrameworkでCommonCryptoを使う

Swiftを使ってCocoa Touch Frameworkを作成しています。
どうしても、CommonCryptoを使う必要があり手段を探していました。

以下、参考になったサイトです。
Importing CommonCrypto in a Swift framework
CommonHMAC in Swift

手順は次のようになります。

  1. CommonCryptoというディレクトリを作成
  2. module.mapというファイルを作成
  3. プロジェクト設定のBuild Settings -> Swift Compiler - Search Paths -> Import Pathsに、上記のCommonCryptoディレクトリを指定



次はサンプルです。 module.mapにSDK内のヘッダファイルのPathを記述します。

module CommonCrypto [system] {
    header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/CommonCrypto/CommonCrypto.h"
    link "CommonCrypto"
    export *
}

このあたり、公式Developerサイトに書いてるのかな?

2015年2月21日土曜日

Unit testで非同期処理のWaitをする

XcodeでiOS開発の勉強をはじめました。
いきなりですが、UnitTestネタです。テストしないもの、エンジニアにあらずです。

参考:Writing Test Classes and Methods

waitForExpectationsWithTimeoutで非同期処理の待ち状態を指定

標準テストフレームワークのXCTestExpectationとXCTestCase.waitForExpectationsWithTimeoutを組み合わせて実装します。
手順は次のようになります。

  1. XCTestCase.expectationWithDescriptionをコールして、XCTestExpectationを取得 
  2. XCTestCase.waitForExpectationsWithTimeoutで、waitを指定 
  3. 非同期処理終了のタイミングで、XCTestExpectation.fulfill()。もし、waitForExpectationsWithTimeoutで指定した時間内にコールしなければfailとなる

サンプルプログラムです。
    func testPerformAsyncRequest(){
        // XCTestExpectationの取得
        let expectation = self.expectationWithDescription("client key")

        // 非同期処理のコールバック処理完了後、expectation.fulfill()をコール
        expectation.fulfill()
        
        //waitの時間指定
        self.waitForExpectationsWithTimeout(5, handler: nil)
        
    }

2015年2月8日日曜日

コードカバレッジを使って、視覚的/統計的にテスト状況を確認する

build.gradleの設定

Code Coverage機能を使用するために、app直下のbuild.gradleに追加します。

android {
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }

+        debug {
+            testCoverageEnabled = true
+        }
    }
}

複数のProjectをImportしている場合、各モジュールのbuild.gradleにtestCoverageEnabledを追加します。

Code Coverageの実行

Terminalから次のコマンドを実行します。

./gradlew createDebugCoverageReport

createDebugCoverageReportを実行すると、自動的にandroidTestを行います。
デバイス(or エミュレータ)が起動していない場合、次のようなエラーが表示されます。

:app:connectedAndroidTest FAILED          
              
FAILURE: Build failed with an exception.
              
* What went wrong:
Execution failed for task ':app:connectedAndroidTest'.
> com.android.builder.testing.api.DeviceException: java.lang.RuntimeException: No connected devices!

必ずデバイスかエミュレータを起動して、androidTestを実行できる環境にしておきましょう。

レポートの観覧

createDebugCoverageReportが成功すると、各モジュールのOutputにレポートを保存します。
[PROJECT]/app/build/outputs/reports/coverage/debug/index.html

次のような表が出力されます。コードの全ライン数に対して、テストが実行されたラインが統計的に確認する事ができます。



次のように視覚的に、どのコードがテストされていないか確認することも可能です。

2015年2月1日日曜日

LocalBroadcastManagerを使って、アプリ内部にのみブロードキャストを行う

LocalBroadcastManager

LocalBroadcastManagerはアプリ内の同プロセスに限定して、Broadcastを行う事ができます。
通常のsendBroadcastでブロードキャストと異なり、次のような利点があります。

プライベートなデータをアプリ外に残す心配がない。
他アプリがこのBroadcastを使う事ができない。(セキュリティホールの心配はしなくていい)
通常のBroadcastに比べ、より効率的アプリ内部へブロードキャストが行える。

注意点としては「同プロセス」に限定してブロードキャストを行います。言い換えれば、他プロセスにはブロードキャストは行えません。


用途

データ管理を行うModleクラスを作成する際、多くの人はServiceで実装すると思います。
Serviceを使うまでもない軽微なModleなら、ObserverとしてLocalBroadcastManagerを利用してデータの更新通知を行うのもありかと思います。


サンプルソース

こちらにJUnit付きのソースをアップしています。
https://github.com/baroqueworksdev/MyApiDemo_AndroidStudio/commit/cf08208cfd84b916480a08acf6f43fd791ca7da0

public class LocalBroadcastController {
    private LocalBroadcastManager mLocalBroadcastManager;
    private IntentFilter mIntentFilter;
    private OnLocalBroadcastController mOnLocalBroadcastController;

    public interface OnLocalBroadcastController {
        public void onReceive(Context context, Intent intent);
    }

    /**
     * Constructor
     *
     * @param context  Context
     * @param filter   Intent Filter which need to receive an action
     * @param listener onReceive
     */
    public LocalBroadcastController(Context context, IntentFilter filter, OnLocalBroadcastController listener) {
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(context);
        mIntentFilter = filter;
        mOnLocalBroadcastController = listener;
    }

    /**
     * Register Receiver
     */
    public void registerReceiver() {
        mLocalBroadcastManager.registerReceiver(mReceiver, mIntentFilter);
    }

    /**
     * Unregister Receiver
     */
    public void unregisterReceiver() {
        mLocalBroadcastManager.unregisterReceiver(mReceiver);
    }


    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mOnLocalBroadcastController.onReceive(context, intent);

        }
    };

}