LoginSignup
18
19

More than 5 years have passed since last update.

x-callback-url を使ったアプリ間連携実装のサンプル

Last updated at Posted at 2015-06-25

はじめに

x-callback-url を用いた別アプリ呼び出し処理の一例です。

流れや概要についてはcookpad様の丁寧な解説があります。
必読。

iOSアプリ間連携の実装に x-callback-url を使う

以下は主に「これを実装してみた!」内容です。

概要

別アプリを起動する場合、2パターンあると思います。
 a. 別アプリを呼び、すぐに制御を元のアプリに戻す
 b. 別アプリを呼び、元アプリに制御は戻らずそのまま操作できる

今回は前者のケース。

UUIDを生成し保持しているアプリAがあるとして、
アプリBからアプリAを起動しUUIDを貰ってきます。

工夫・調査した点

一瞬見える、呼び出されたアプリの画面表示

元のアプリに制御を返す時に一瞬だけ、呼び出したアプリの画面が見えてしまいます。
それが嫌だったので隠すようにしました。

元アプリに制御を戻す際の遅さへの対応

こちらを参考にさせて頂きました。
iOS7で起動時にsafariへ飛ばすときにはGCDを使う

カスタムURLスキームでの呼び出し時の画像(保留)

カスタムURLスキームでのアプリ呼び出し時の画像は変更可能でした。
カスタムURLスキームでのアプリケーション切替時の画像の設定

しかしiOS8.3ではできないようです。
調査中です。

呼び出す側アプリ実装

InterAppCommunication
こちらをプロジェクトに追加しておきます。

あとは自分のURLスキームの登録をしておきます。
カスタムURLスキームを利用する (3)

例では自分のURLスキームは「jp.co.sample」としました。
呼び出される側のURLスキームは「jp.co.sample-parent」としました。

以下、主な部分の実装です。

AppDelegate.m

//
//  AppDelegate.m
//  CustomSchemeSample
//
//  Created by Satoshi Hattori on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import "AppDelegate.h"
#import "IACManager.h"

#define SELF_SCHEME_NAME @"jp.co.sample"

@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [IACManager sharedManager].callbackURLScheme = SELF_SCHEME_NAME;

    return YES;
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    return [[IACManager sharedManager] handleOpenURL:url];
}

@end

ViewController.m

//
//  ViewController.m
//  CustomSchemeSample
//
//  Created by Satoshi Hattori on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import "ViewController.h"
#import "IACClient.h"

#define PARENT_SCHEME_NAME @"jp.co.sample-parent"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 取得処理を呼ぶためだけのボタン
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    btn.frame = CGRectMake(100, 100, 120, 35);
    [btn setTitle:@"UUID取得" forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(uuidBtn_touchUpInside:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}

- (void)uuidBtn_touchUpInside:(id)sender
{
    NSString *parentScheme = [NSString stringWithFormat:@"%@://", PARENT_SCHEME_NAME];

    if (![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:parentScheme]]) {
        return;
    }

    IACClient *client = [IACClient clientWithURLScheme:PARENT_SCHEME_NAME];

    // コールバックありの呼び出し
    [client performAction:@"getUuid" parameters:nil onSuccess:^(NSDictionary *params) {

        NSString *uuid = params[@"uuid"];

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"UUID" message:uuid delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];

    } onFailure:^(NSError *error) {

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];

    }];
}

@end

呼び出される側アプリ実装

こちらのプロジェクトでもInterAppCommunicationを追加しておきます。

自分のURLスキームの登録を忘れずに。
こちらのURLスキームは「jp.co.sample-parent」としました。

AppDelegate.m

//
//  AppDelegate.m
//  CustomSchemeSampleParent
//
//  Created by GCSMAC01 on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import "AppDelegate.h"

#import "IACManager.h"
#import "MyIACLogic.h"
#import "SchemeBackgroundView.h"

#define PARENT_SCHEME_NAME @"jp.co.sample-parent"

@interface AppDelegate ()
{
    MyIACLogic *_myIACLogic;
}

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    _myIACLogic = [[MyIACLogic alloc] init];

    [IACManager sharedManager].callbackURLScheme = PARENT_SCHEME_NAME;
    [IACManager sharedManager].delegate = _myIACLogic;

    return YES;
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // アプリがBackgroundに移行するタイミングで一時的な背景Viewの除去を行っています
    [SchemeBackgroundView dismiss];
}

#pragma mark - Custom URL Scheme

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    return [[IACManager sharedManager] handleOpenURL:url];
}

@end

IACDelegateを実装したロジッククラス。

MyIACLogic.h
//
//  MyIACLogic.h
//  CustomSchemeSampleParent
//
//  Created by Satoshi Hattori on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "IACManager.h"

@interface MyIACLogic : NSObject <IACDelegate>

@end

MyIACLogic.m
//
//  MyIACLogic.m
//  CustomSchemeSampleParent
//
//  Created by Satoshi Hattori on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import "MyIACLogic.h"
#import "SchemeBackgroundView.h"

@implementation MyIACLogic

- (BOOL)supportsIACAction:(NSString *)action
{
    NSArray *supportedActions = @[@"getUuid"];
    return [supportedActions containsObject:action];
}

- (void)performIACAction:(NSString *)action
              parameters:(NSDictionary *)parameters
               onSuccess:(IACSuccessBlock)success
               onFailure:(IACFailureBlock)failure
{
    if ([action isEqualToString:@"getUuid"]) {

        if (success) {

            // 一時的な背景Viewを最善面に追加
            [SchemeBackgroundView show];

            // ここがロジック部分
            // ここではダミーUuid値を生成
            NSString *uuid = @"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";

            // 数秒(3〜5秒)待たされる現象があったため対応としてdispatch_asyncで実行
            dispatch_async(dispatch_get_main_queue(), ^{
                success(@{@"uuid":uuid}, NO);
            });

        }
    }
    else {
        // その他のアクションの場合
    }

}

@end

一時的な背景View。
最前面にaddSubviewしています。

SchemeBackgroundView.h

//
//  SchemeBackgroundView.h
//  CustomSchemeSampleParent
//
//  Created by GCSMAC01 on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface SchemeBackgroundView : UIView

+ (void)show;
+ (void)dismiss;

@end

SchemeBackgroundView.m

//
//  SchemeBackgroundView.m
//  CustomSchemeSampleParent
//
//  Created by Satoshi Hattori on 2015/06/24.
//  Copyright (c) 2015年 Satoshi Hattori. All rights reserved.
//

#import "SchemeBackgroundView.h"

@implementation SchemeBackgroundView

+ (SchemeBackgroundView *)sharedView
{
    static dispatch_once_t once;
    static SchemeBackgroundView *sharedView;
    dispatch_once(&once, ^ { sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });

    sharedView.backgroundColor = [UIColor blackColor];

    return sharedView;
}

+ (void)show
{
    NSEnumerator *frontToBackWindows = [[[UIApplication sharedApplication] windows] reverseObjectEnumerator];

    for (UIWindow *window in frontToBackWindows) {
        if (window.windowLevel == UIWindowLevelNormal) {
            [window addSubview:[SchemeBackgroundView sharedView]];
            break;
        }
    }
}

+ (void)dismiss
{
    NSEnumerator *frontToBackWindows = [[[UIApplication sharedApplication] windows] reverseObjectEnumerator];

    for (UIWindow *window in frontToBackWindows) {
        if (window.windowLevel == UIWindowLevelNormal) {
            for (UIView *view in window.subviews) {
                if ([view isKindOfClass:[SchemeBackgroundView class]]) {
                    [view removeFromSuperview];
                }
            }
            break;
        }
    }
}

@end

気を付ける箇所

・カスタムURLスキームには「_」アンダースコアは使用できない。(なんという罠!)

・InterAppCommunication使用時、localizedAppNameがnilだとエラー。
 → info.plistに「Bundle display name」を追加し設定しましょう。

おわりに

実際にやってみると罠や気になる挙動が出てきますよね。
それに対応することでじわじわと貯まるノウハウ。

細かい点でまだ課題ありですが
同じ事をやろうとした方の参考になりましたら幸いです。

関連

x-callback-url
InterAppCommunication

18
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
19