Apple Storeに14時間並んでiPhone 5sを手に入れたら、 同期の @saya_223n が発売日にたった3時間で手に入れていて少し切なくなりました.  株式会社ジェネシックスのiOSエンジニア 坂田(@huin)です.


さて、iPhone 5s(とiPhone 5c)の発売に合わせてついにiOS 7がリリースされました. iOS 7の目玉はなんといってもUIの刷新ですが、UI以外にもフレームワークの変更や追加が数多く行われています.

というわけで、今回はiOS 7で個人的に注目している MultipeerConnectivity.frameworkについてご紹介したいと思います.

MultipeerConnectivity.framework とは

MultipeerConnectivity.framework (長いので以後MC Frameworkと表記します) はiOS 7で新規に追加されたフレームワークの一つです. Bluetooth or/and WiFiを利用して端末同士(3台以上も可能)で直接通信を行うためのフレームワークで、iOS 7で追加されたAirDropのAPI版だと考えると分かりやすいかと思います. 
 

WWDC 2013では、#708 Nearby Networking with Multipeer Connectivityというセッションで紹介されていました. 9/25現在、プログラミングガイドは存在しないのですが、MultipeerGroupChatというサンプルコードが配布されています. サンプルコード以上の例が作れなかったため非常に良いサンプルのため、今回はこのサンプルコードをベースにご紹介したいと思います.

注意 なお、このフレームワークはiPhone 5など、比較的新しい端末のみでサポートされています. AirDropが利用できないiPhone 4ではうまく動作しなかったのでご注意下さい.また、複数端末で利用する機能であるため、iOS 7が動作する端末が2台以上必要になります.

MultipeerGroupChat について

MC FrameworkのサンプルコードMultipeerGroupChatは、名前の通りグループチャット機能を実現するサンプルで、テキストメッセージの送信や写真の送受信ができます.

IMG_0002    IMG_0004


MC Frameworkの概要

MC Frameworkによる通信は、基本的にはMCSessionクラスとMCSessionDelegateプロトコルのメソッドを通じて行うだけです.サンプルでは、MCSessionの他に接続開始までのプロセスでMCAdvertiserAssistantMCBrowserViewControllerクラスを利用していますが、最低限これらのクラスだけで実現できます. 以下これらのクラスについて説明します.

デバイス同士の接続

端末同士で通信を行うためにはまず端末間の接続を確立する必要があります. MC FrameworkではAdvertiserとBrowserという2つの役割がこれを行います. Advertiserが自身を周囲に知らせ、Browserがこれを見つけて接続を確立するという流れです.

Advertiserの動作

Advertiserは周囲に対して自身の告知を行います. MC Frameworkでは MCAdvertiserAssistantクラスのオブジェクトを生成し、startメソッドを呼ぶことでこれを行います. サンプルでは SessionContainerクラスの初期化メソッドinitWithDisplayName:serviceType:の中で行っています.
 

// Session container designated initializer
- (id)initWithDisplayName:(NSString *)displayName serviceType:(NSString *)serviceType
{
    if (self = [super init]) {
        MCPeerID *peerID = [[MCPeerID alloc] initWithDisplayName:displayName];

        _session = [[MCSession alloc] initWithPeer:peerID securityIdentity:nil encryptionPreference:MCEncryptionRequired];
        _session.delegate = self;

        _advertiserAssistant = [[MCAdvertiserAssistant alloc] initWithServiceType:serviceType discoveryInfo:nil session:_session];
        [_advertiserAssistant start];
    }
    return self;
}

Browserの動作

Advertiserを探索し、接続を開始する役割はBrowserと呼ばれます. MC FrameworkではMCBrowserViewControllerクラスがAdvertiserの探索、接続およびUIの提供までを行ってくれます.

サンプルでは MainViewControllerのメソッドbrowseForPeers:でこれらの処理を行っています. といってもやっているのはMCBrowserViewControllerの初期化と、サービス提供に必要な最小/最大人数の設定、そして表示のみです.

セッションへの端末の参加/離脱イベントは、MCSessionDelegateプロトコルのsession:peer:didChangeState:メソッドで受け取れます. これらはSessionContainerクラスで実装されています.

デバイス間の通信

端末間の接続を確立したら、あとはMCSessionオブジェクトに対してデータを送るだけです.

MC Frameworkでは、データ、リソース、ストリームの3種類のデータ送信がサポートされており、それぞれ

  • -(BOOL)sendData:toPeers:withMode:error
  • -(void)sendResourceAtURL:toPeer:withTimeout:completionHandler:
  • -(NSOutputStream *)startStreamWithName:toPeer:error:

を呼ぶことで各種データの送信が可能です.

また、受信側は、MCSessionDelegateプロトコルの各メソッド、

  • - (void)session:didReceiveData:fromPeer:
  • - (void)session:didReceiveResourceAtURL:fromPeer:
  • - (void)session:didReceiveStream:withName:fromPeer:

そ実装することで、受信できます.

サンプルでは、SessionContainersendMessage:およびsendImage:メソッドでそれぞれテキスト(をNSDataにしたもの)と画像(のURL)を送信しています.


// 文字列の送信
- (Transcript *)sendMessage:(NSString *)message
{
    NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding];

    NSError *error;
    [self.session sendData:messageData toPeers:self.session.connectedPeers withMode:MCSessionSendDataReliable error:&error];

    if (error) {
        return nil;
    } else {
        return [[Transcript alloc] initWithPeerID:_session.myPeerID message:message direction:TRANSCRIPT_DIRECTION_SEND];
    }
}

// 画像URLの送信
- (Transcript *)sendImage:(NSURL *)imageUrl
{
    NSProgress *progress;
    for (MCPeerID *peerID in _session.connectedPeers) {    
        progress = [self.session sendResourceAtURL:imageUrl withName:[imageUrl lastPathComponent] toPeer:peerID withCompletionHandler:^(NSError *error) {
            if (error) {
                NSLog(@"Send resource to peer [%@] completed with Error [%@]", peerID.displayName, error);
            } else {
                Transcript *transcript = [[Transcript alloc] initWithPeerID:_session.myPeerID imageUrl:imageUrl direction:TRANSCRIPT_DIRECTION_SEND];
                [self.delegate updateTranscript:transcript];
            }
        }];
    }

    Transcript *transcript = [[Transcript alloc] initWithPeerID:_session.myPeerID imageName:[imageUrl lastPathComponent] progress:progress direction:TRANSCRIPT_DIRECTION_SEND];
    return transcript;
}

まとめ

今回はMultipeerConnectivity.frameworkによる端末間通信について紹介しました. 接続の確立までが少し手間ですが、登場するクラスは少なく簡単に端末間通信を行うことが可能です.

端末間で直接通信を行うアプリはこれまでもありましたが、サーバーを介したものなど大掛かりなものが多かったように思います. MC Frameworkの登場で、身近にいる人同士で直接楽しめるアプリが増えるのではないかと思います. ジェネシックスでもこれらの新しいフレームワークを積極的に利用していくつもりです.
 

なお、VOYAGE GROUPでは現在、新規サービス立ち上げに向けてiOSエンジニアを募集しております. ご興味のある方は下記URLからお問い合わせ下さい.

採用情報 :  http://voyagegroup.com/crew/recruit/career/ios/

応募までは行かないけど…という方は@huinにご連絡頂けると社内バーのAJITOで飲みながらのお話などご招待させて頂きます. ご連絡お待ちしております!