2015年2月26日木曜日

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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を記述します。

1
2
3
4
5
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となる

サンプルプログラムです。
1
2
3
4
5
6
7
8
9
10
11
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から次のコマンドを実行します。

1
./gradlew createDebugCoverageReport

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

1
2
3
4
5
6
7
: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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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);
 
        }
    };
 
}