Objective-C
iOS
CocoaPods
Parse.com
NiftyCloud

parse.comからNIFTY Cloud mobile backendへ移行した話(Part1)

More than 1 year has passed since last update.

個人で開発しているアプリ、keyaki2のバックエンドをparse.comからNIFTY Cloudへ移行した話です。
keyaki2は欅坂46のブログリーダーです。メンバーごとに新着ブログの通知をプッシュで送ってくれます。

ついでにCocoaPodsの導入、Gitの導入と、開発と本番で叩くAPIを変えるように実装したいと思っています。
クソプロジェクトを保守しやすいマシなものにしよう(メインはmBaaSの切り替え)
っていう大きなテーマを掲げていきたいと思います。

以降、NIFTY Cloud Mobile BackendをNCMBと称します。

現在の状態

【Parse版 keyaki2】
 CocoaPods未導入。手作業の温かみでライブラリを追加していました。

具体的な移行手順

いきなりParse版からNCMB版に切り替えてしまうと、いろいろ大変なことが起きそうなので
移行期間を作ってゆっくり移行していきたいと思います。

1. Parse版にNCMBのSDKをインストール

こちらも手作業の温かみが良さそうなのでGitHubからSDKを落としてきて入れようとしたのですが、
なぜかうまく行かなかったので、結局CocoaPodsをこちらにも導入することにしました。

ターミナル上で.xcodeprojがあるディレクトリに移動して
$ pod init
生成されたPodfileに

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

target 'keyaki2' do
  # Comment this line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for keyaki2
  pod 'NCMB', :git => 'https://github.com/NIFTYCloud-mbaas/ncmb_ios.git'
end

pod NCMB...を追記して保存します。
$ pod install を実行してCocoaPodsのプロジェクトを作成します。

生成された.xcworkspaceをXcodeで開いてビルドが通ればとりあえずOKです。

2. DeviceTokenの設定を行う

必要とする場所に #import <NCMB/NCMB.h> を記述してください。
僕の場合はPrefixHeader.hにライブラリ関係を入れているので、そこに記述しました。(楽ちん

まずはDeviceTokenをParseにも、NCMBにも登録するためにAppDelegate.mにいろいろ書いていきます。
アプリのキーや設定関係をコードに直接書くのがイヤなので、僕はConst.hというファイルに定数を置いてます。
Parseの設定ができているのであれば、一行追加するだけでOKです。

AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Parseの初期化
    [Parse setApplicationId:K2ParseAppId clientKey:K2ParseClientKey];

    //NCMBの設定を行う
    [NCMB setApplicationKey:K2NiftyAppId clientKey:K2NiftyClientKey];

    //通知の設定(デバイストークンの取得などを行う)
    UIUserNotificationType userNotificationTypes = (UIUserNotificationTypeAlert |
                                                    UIUserNotificationTypeBadge |
                                                    UIUserNotificationTypeSound);
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:userNotificationTypes
                                                                             categories:nil];
    [application registerUserNotificationSettings:settings];
    [application registerForRemoteNotifications];

    //略
}
Const.h
//Parse関連
static NSString *const K2ParseAppId = @"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
static NSString *const K2ParseClientKey = @"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
//Parse関連
static NSString *const K2NiftyAppId = @"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
static NSString *const K2NiftyClientKey = @"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";

次に、DeviceTokenが返ってきた時にNiftyにも送るようにコードを書いていきます。
同じくAppDelegate.mに記述していきます。基本的には追記していくだけです。

AppDelegate.m
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    //Parse版で元々記述されていた処理
    PFInstallation *currentInstallation = [PFInstallation currentInstallation];
    [currentInstallation setDeviceTokenFromData:deviceToken];
    [currentInstallation saveInBackground];

    //以下を追記
    //端末情報を扱うNCMBInstallationのインスタンスを作成
    NCMBInstallation *installation = [NCMBInstallation currentInstallation];

    //Device Tokenを設定
    [installation setDeviceTokenFromData:deviceToken];

    //端末情報をデータストアに登録
    [installation saveInBackgroundWithBlock:^(NSError *error) {
        if(!error){
            //端末情報の登録が成功した場合の処理
        } else {
            //端末情報の登録が失敗した場合の処理
            if (error.code == 409001){
                //失敗した原因がdeviceTokenの重複だった場合
                [self updateExistInstallation:installation];
            } else if (error.code == 404001) {
                //deviceTokenの重複以外のエラーが返ってきた場合
                [self reRegistInstallation:installation];
            }
        }
    }];
}

//deviceTokenの重複で端末情報の登録に失敗した場合に上書き処理を行う
- (void)updateExistInstallation:(NCMBInstallation*)currentInstallation{
    NCMBQuery *installationQuery = [NCMBInstallation query];
    [installationQuery whereKey:@"deviceToken" equalTo:currentInstallation.deviceToken];

    NSError *searchErr = nil;
    NCMBInstallation *searchDevice = [installationQuery getFirstObject:&searchErr];

    if (!searchErr){
        //上書き保存する
        currentInstallation.objectId = searchDevice.objectId;
        [currentInstallation saveInBackgroundWithBlock:^(NSError *error) {
            if (!error){
                //端末情報更新に成功したときの処理
            } else {
                //端末情報更新に失敗したときの処理
            }
        }];
    } else {
        //端末情報の検索に失敗した場合の処理
    }
}

- (void) reRegistInstallation:(NCMBInstallation *) installation {
    installation.objectId = nil;
    [installation saveInBackgroundWithBlock:^(NSError *error){
        if (!error) {
            NSLog(@"NCMB installation : successfully re-regist.");
        } else {
            NSLog(@"NCMB installation : failed to re-regist. %@",error);
        }
    }];
}

これでDeviceTokenがParseとNiftyの両方に向くようになりました。
基本的な移行版の作成はこれだけでいいのですが、keyaki2ではメンバーごとにブログの通知を送るため、
ユーザはそれぞれチャンネルに登録を行っています。今回はその設定もNiftyに移行したいため、
こちらのコードも書いていきます。

3.Channelsの設定をNiftyへ移行する

通知チャンネルの移行は、「Parseで登録しているチャンネルリストをNiftyにも送る」という方法で
いきたいと思います。
以下に移行するためのメソッドを示します。僕はどこからでも呼べるようにUtilsクラス内に入れました。

Utils.h
+(void)syncNotificationSettings;
Utils.m
+(void)syncNotificationSettings{
    NSArray *subscribedChannels = [PFInstallation currentInstallation].channels;
    NCMBInstallation *installation = [NCMBInstallation currentInstallation];
    [installation setObject:subscribedChannels forKey:@"channels"];
    //端末情報をデータストアに保存
    [installation saveInBackgroundWithBlock:^(NSError *error) {
        NSLog(@"installationIdInBlock:%@", installation.objectId);
        if(!error){
            //端末情報の登録が成功した場合の処理
            NSLog(@"Sync successful!");
        } else {
            //端末情報の登録が失敗した場合の処理
            NSLog(@"Sync failed...");
        }
    }];
}

とりあえずこれ呼んでおけば同期できるっぽいのでいいのかな…と思ってたりします。
パフォーマンス的にどうかとは思いますが(未検証)、とりあえずAppDelegate内でそういう処理を書いちゃえば
とりあえず移行はできるだろうということで…。
再びAppDelegate.mに戻って

AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //どこか適当な位置に(return直前とかいいかもしれない)
    [Utils syncNotificationSettings];

    return;
}

って置いておけばコールドスタート時にはとりあえず設定は同期できるはずです。
あとはユーザがchannelの設定を変更した時にもこのメソッドを呼べば完璧ですね。

一旦この状態でアプリをビルド、リリースしてユーザには移行期間版を使ってもらうことにします。

次回は新アプリをCocoaPodsで作成して、コードを移行したり整理したりしたいと思います。