iOSでTestFlight SDKを使うときのメモ


**この内容はAppleに買収されiTunes Connectに統合される前のTestFlightについての情報です、testflightapp.comは2015/2/26に終了です。**

**TestFlightのAndroidサポートは2014/3/21までなので代替サービスを検討してください。**
http://help.testflightapp.com/customer/portal/articles/1450414

自分なりの使い方の要約。
https://testflightapp.com/sdk/ios/doc/

## インストール

CocoaPodsでインストールする。基本的に安定バージョンを明示的に指定。

```ruby:Podfile
pod 'TestFlightSDK', '2.0.2'
```

## Take Off

こんな感じの関数を用意しておくAppStoreに提出するときは```#define USE_TESTFLIGHT_SDK 0```にしてコードに含めないようにしている。

```objectivec

#if USE_TESTFLIGHT_SDK
#import <TestFlightSDK/TestFlight.h>
#endif

/*
 My Apps Custom uncaught exception catcher, we do special stuff here, and TestFlight takes care of the rest
 */
void HandleExceptions(NSException *exception)
{
    NSLog(@"This is where we save the application data during a exception");
    // Save application data on crash
}
/*
 My Apps Custom signal catcher, we do special stuff here, and TestFlight takes care of the rest
 */
void SignalHandler(int sig)
{
    NSLog(@"This is where we save the application data during a signal");
    // Save application data on crash
}

- (void)initializeTestFlightWithApplicationToken:(NSString *)token
{
    // installs HandleExceptions as the Uncaught Exception Handler
    NSSetUncaughtExceptionHandler(&HandleExceptions);
    // create the signal action structure
    struct sigaction newSignalAction;
    // initialize the signal action structure
    memset(&newSignalAction, 0, sizeof(newSignalAction));
    // set SignalHandler as the handler in the signal action structure
    newSignalAction.sa_handler = &SignalHandler;
    // set SignalHandler as the handlers for SIGABRT, SIGILL and SIGBUS
    sigaction(SIGABRT, &newSignalAction, NULL);
    sigaction(SIGILL, &newSignalAction, NULL);
    sigaction(SIGBUS, &newSignalAction, NULL);
#if USE_TESTFLIGHT_SDK
#ifndef __IPHONE_7_0
    [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];
#endif
    [TestFlight takeOff:token];
#endif
}
```

あとはAppDelegateのdidFinishLaunchingWithOptionsあたりで呼び出す。さっきの関数もAppDelegateに入れて良い。

```objectivec:AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{    
    [self initializeTestFlightWithApplicationToken@"Your App Token"];
    return YES;
}
```


## Remote Logging

TFLogを使う。ドキュメントでは以下のようにNSLogと切り替える例がある。

```
#define NSLog TFLog
```

や

```
#define NSLog(__FORMAT__, ...) TFLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
```

それぞれのログは非同期で記録され、セッションが終わる際にTestFlightサーバに送られる。

実際は、AppStoreに提出することも想定してTARGET_APP_STOREなどの定義で処理を切りわけるんじゃないだろうか。自分の場合はUSE_TESTFLIGHT_SDKとTARGET_APP_STOREの定義は明示的に使い分け以下の様な感じにする。

```objective-c:
#if TARGET_APP_STORE
#define MyLog(...) ;
#else
#if USE_TESTFLIGHT_SDK
#define MyLog(__FORMAT__, ...) TFLog((@"%s [Line %d] " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
#define MyLog(...) NSLog(__VA_ARGS__)
#endif // USE_TESTFLIGHT_SDK
#endif // TARGET_APP_STORE

```

### ログに関するオプション

|オプション名|型|デフォルト|説明|
|---|---|---|---|
|logToConsole|BOOL|YES|Apple System Logにも出力するかどうか|
|logToSTDERR|BOOL|YES|STDERRにも出力するかどうか|


```例)
[TestFlight setOptions:@{@"logToConsole:}[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"logToConsole"]];
// iOS SDK 6 or later
[TestFlight setOptions:@{@"logToConsole: @NO}];
```

## Feedback API

openFeedbackViewを実行するとModalでフィードバックを送るフォーム(TextArea、Cancel Button, Submit Button)が表示される。

```
[TestFlight openFeedbackView];
```

submitFeedbackを使えばTestFlightのフォームを使わなくても文字列だけ送信できる。

```
NSString *feedback = @"my feedback";
[TestFlight submitFeedback:feedback];
```

FeedbackはTestFlight上からビルドごとに閲覧でき、TestFlightサイトから返信もできる。(メールで通知される)

## リリース時

USE_TESTFLIGHT_SDK 0にすれば実行しなくなるが、Podfileをコメントアウトしてpod updateするのは面倒なので、スタティックライブラリは```Pods.xcconfig```のOTHER_LDFLAGSから一時的に``-lTestFlight``を取り除いてビルドしている。ただ人為ミスが起きそうなのであまり良いやり方だと思わない。