Beacappを使って簡単なアプリを作ってみる（開発編 -iOS-）


[前回](http://qiita.com/JMASystems/items/b57c2689d22a42cbe36e)に引き続き、Beacappを使った簡単なアプリ開発について、ご紹介します。


##iOSアプリへの組み込み

アプリを組み込むプロジェクトを用意します。
既存のプロジェクトでも新規プロジェクトでも問題ありません。

今回は、新規のプロジェクトを作成します。

さて、これ以降の手順で実施して作成したアプリが下記にあります。
とりあえずソースコードを見たい！
という人はこちらからどうぞ
[Beacapp iOS Sample - Github](https://github.com/JMASystems/Beacapp-iOS-Sample)

### プロジェクトの作成
MacでXcodeを起動します。

[Create a new Xcode Project]を選択しましょう。

[Choose a template for your new project:]
は、好きなものを選択してください。
今回は[iOS]-[Applications]-[Single View Application]を選択して「Next」を押下します。

次の画面で、プロジェクトに必要な情報を入力していきます。
Languageは [Swift]でも[Objective-C]でも大丈夫です。
今回は[Objective-C]を選んでみました。
※Swiftの場合は、Bridging-Headerを用意する必要があります。

全て入力したら、[Next]を押下して、プロジェクトの保存場所を選択したら、[Create]を押下してください。

### CocoaPodsを使ってインストール

BeacappSDK for iOS は CocoaPods を使ってインストールできようにしています。
開発環境で CocoaPods を使えるようにしましょう。

Beacappを使うには、Podfileに ``pod "BeacappSDKforiOS`` を記述すればOKです。
以下は、Podfileの参考。

```Podfile 
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'

target 'BeacappiOSSample' do
  # Uncomment this line if you're using Swift or would like to use dynamic frameworks

  # ここに、必要なライブラリ名称を書く
  pod "BeacappSDKforiOS"

  # Pods for BeacappiOSSample

  target 'BeacappiOSSampleTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'BeacappiOSSampleUITests' do
    inherit! :search_paths
    # Pods for testing
  end

end
```

Podfileを修正したら、ターミナルから、installを実行します。

```
 $ pod install
```

プロジェクトディレクトリに、拡張子が .xcworkspace のファイルが作成されていることが確認できたら、ライブラリの取り込みは完了です。


#### BeacappSDKを呼び出す
cocoapods でのインストールが完了していると、.xcworkspace が作成されていますので、こちらをダブルクリックして、Xcodeを立ち上げます。
※ .xcodeproj ファイルを立ち上げると、ライブラリの依存関係を解決できないため、ビルドエラーになります。

##### ヘッダーをインポート
今回は、AppDelegateで呼び出していきたいと思います。
まずは、ヘッダーをインポートします。

```AppDelegate.h
#import <BeacappSDKforiOS/JBCPCore.h>
```

##### デリゲートを採用
続けて、JBCPManagerDelegateを採用します。

```AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate,JBCPManagerDelegate>
```

##### デリゲートメソッドを記述
JBCPManagerDelegateのメソッドを記述します
デリゲートメソッドには、以下のものがあります。

1. manager:shouldUpdateEvents:
	- CMSに登録されているイベント情報に、アップデートがあった場合に、更新するかどうかを返却します。
1. manager:didFinishUpdateEvents:
	- イベントの更新が完了すると呼ばれます。
	- このタイミングでスキャンを開始するのが良いです。
1. manager:fireEvent:
	- CMSに登録したイベントに合致するBeaconを検知した場合に、呼ばれます。

1. manager:didUpdateMonitoringStatus:peripheralState:
	- 位置情報サービスの許可状態、端末Bluetooth状態を検知し場合、このdelegateメソッドが呼ばれます。

1. manager:didFailWithError:
	- CLLocationManagerを利用した位置情報サービスの利用、Beaconの監視・レンジング実行時にエラーが発生した場合に、このdelegateメソッドが呼ばれます。

より詳細な情報は、以下を参照してください。
[JBCPManagerDelegate プロトコルリファレンス](https://github.com/JMASystems/beacapp-sdk-ios/blob/master/Documents/JBCPManagerDelegate.md)

今回記載するのは、とりあえず以下の2つだけ。
manager:didFinishUpdateEvents:
manager:fireEvent:


```AppDelegate.m
#pragma mark - JBCPManagerDelegate

- (void)manager:(JBCPManager *)manager didFinishUpdateEvents:(NSError *)error{
    if(error){
        
    }else{
        
    }
}

- (void)manager:(JBCPManager *)manager fireEvent:(NSDictionary *)event{
    
}

```

##### JBCPManagerを呼ぶ
とりあえず、起動処理の中で、呼んでみましょう。
ちなみに、[yourRequestToken]、[yourSecretKey]の部分は、CMSで確認した、「アクティベーションキー」と「シークレットキー」に書き換えてください。


```AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // JBCPManagerインスタンスを取得する
    JBCPManager *manager = [JBCPManager sharedManager];
    manager.delegate = self;
    
    // アクティベーションをする
    NSError *activateError = nil;
    [manager initializeWithRequestToken:@"yourRequestToken" secretKey:@"yourSecretKey" options:nil error:&activateError];
    
    if (!activateError) {
        // イベントをアップデートをする
        NSError *eventError = nil;
        [manager startUpdateEvents:&eventError];
    }

    return YES;
}
```

ただし、上記の記載だと、アクティベーション処理が、同期で通信処理を行いますので、アプリの立ち上げに時間がかかってしまいます。

念のため、簡易的に、別スレッドで処理をさせておきます。

```AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self performSelectorInBackground:@selector(initJBCPManager) withObject:nil];
    return YES;
}

- (void)initJBCPManager{
    // JBCPManagerインスタンスを取得する
    JBCPManager *manager = [JBCPManager sharedManager];
    manager.delegate = self;
    
    // アクティベーションをする
    NSError *activateError = nil;
    [manager initializeWithRequestToken:@"yourRequestToken" secretKey:@"yourSecretKey" options:nil error:&activateError];
    
    if (!activateError) {
        // イベントをアップデートをする
        NSError *eventError = nil;
        [manager startUpdateEvents:&eventError];
    } else {
        NSLog(@"%@",activateError);
    }

    
}
```

エラーハンドリングがイマイチですが、今回はこれでいきましょう！
上記の処理の中で、イベントの取得までを行います。

イベント取得が完了すると、先に記載したデリゲートが呼ばれます。

```AppDelegate.m
- (void)manager:(JBCPManager *)manager didFinishUpdateEvents:(NSError *)error;
```

この中で、スキャンの開始をします。
こんな感じで書いてみます。

```AppDelegate.m
- (void)manager:(JBCPManager *)manager didFinishUpdateEvents:(NSError *)error{
    if(error){
        // 一旦何もしない
    }else{
        JBCPManager *manager = [JBCPManager sharedManager];
        NSError *scanError = nil;
        [manager startScan:&scanError];
    }
}
```

あとは、イベントを検知した時の制御を書けばOKです。
とりあえず、ログに吐き出す処理を書いておきます。

```AppDelegate.m
- (void)manager:(JBCPManager *)manager fireEvent:(NSDictionary *)event{
    NSDictionary *actionContentDic = [event objectForKey:@"action_data"];
    NSString *type = actionContentDic[@"action"];
    
    // typeがtextの場合は、textキーでコンテンツの内容を取得することができます。
    if([type isEqualToString:@"jbcp_open_text"]){
        NSString *contentText = actionContentDic[@"text"];
        NSLog(@"event_text:%@",contentText);
    }
}
```


##### info.plist に項目を追加する

```info.plist
NSLocationAlwaysUsageDescription
```
位置情報を要求するため、plistに上記の項目を追加して、説明を記載します。


```info.plist
NSAppTransportSecurity
```

また、CMSと通信を行うため、AWS SDKが説明する以下を適用してください。
[ATS(Apple Transport Security)の設定](https://github.com/aws/aws-sdk-ios/tree/2.3.6#preparing-your-apps-for-ios-9)


ここまでできたら、実機で実行してみましょう。
手元に、登録したBeaconがあれば、ログに、設定したテキストが表示されることを確認できると思います。
あとは、受け取ったイベントをもとに、なんでもやってみてください。

「手元にBeaconなんてないよ」という方のために、Beacon発信用（受信もできちゃう）のアプリを用意しています。
[Beacheck on the App Store - iTunes - Apple](https://itunes.apple.com/us/app/beacheck/id941020155?mt=8)

### よくあるエラー

#### アクティベーションに失敗する！
アクティベーション時に発生するよくあるエラーに、以下のものがあります。

```
Error Domain=BeacappDomain Code=1000 "ネットワークエラー(401)"
```

これが発生した場合は、以下のケースが想定されます。

・BundleIdentifierが間違っている
・アクティベーションキーが間違っている
・シークレットキーが間違っている

CMSに登録した内容とあっているか、確認してみてください。

#### Beaconを検知しない

Beaconを検知しないという場合は、VerboseModeを用意していますので、そちらでログを見て確認してみてください。

##### スキャンの開始に失敗している
iPhoneの設定を確認してください。

・位置情報はONになっているか
・位置情報は許可されているか
・Bluetoothは許可されているか

##### BeaconのUUIDが出力されていない

以下の可能性があります。
・Beacappに登録したBeaconのUUIDに誤りがある
・端末の故障などで、電波が出ていない


##### BeaconのUUIDなどが出力されているのに、イベントを検知しない
設定は正しくできています。
端末から距離を離れてみたり、動いてみたり、少し待ってみてください。



```AppDelegate.m
- (void)manager:(JBCPManager *)manager didFinishUpdateEvents:(NSError *)error{
    if(error){
        // 一旦何もしない
    }else{
        JBCPManager *manager = [JBCPManager sharedManager];
        
        //VerboseModeをYESにする
        [manager setVerboseMode:YES];
        NSError *scanError = nil;
        [manager startScan:&scanError];
    }
}
```

#### ビルドに失敗する
Project設定で、ENABLE_BITCODEがYESになっているとエラーになりますので、NOを指定してください。

#### その他
その他、お困りごとがあれば、お気軽にお問い合わせください。
[Beacapp サポートチーム](https://cms.beacapp.com/contact/index)


次回はAndroid版での開発について、ご紹介させていただきます。