1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

iOS 7 で自身のカスタムスキームを呼び出した時にアプリケーションイベントが異なる

Last updated at Posted at 2015-09-04

問題

iOS 7 では、自身のカスタムスキームを呼び出した時、
UIApplicationWillResignActiveNotification / UIApplicationDidBecomeActiveNotification が呼び出されない・通知されない。

ルーティングを実装して遷移するために呼び出したり、WebView で認証させてる場合に実装によっては影響することがあるのではなかろうかと思ってます。

自身を呼び出す (iOS 7)

-[AppDelegate application:openURL:sourceApplication:annotation:] [Line 28] url: openurl://, app: (null), state: 0, annotation: (null)

自身を呼び出す (iOS 8 later)

-[AppDelegate applicationWillResignActive:] [Line 36] 
-[AppDelegate application:openURL:sourceApplication:annotation:] [Line 28] url: openurl://, app: example.OpenURLTest, state: 1, annotation: (null)
-[AppDelegate applicationDidBecomeActive:] [Line 52] 

他アプリケーションが呼び出す (iOS 7)

-[AppDelegate applicationWillEnterForeground:] [Line 47]
-[AppDelegate application:openURL:sourceApplication:annotation:] [Line 28] url: openurl://, app: com.apple.mobilesafari, state: 1, annotation: (null)
-[AppDelegate applicationDidBecomeActive:] [Line 52] 

他アプリケーションが呼び出す (iOS 8 later)

-[AppDelegate applicationWillEnterForeground:] [Line 47]
-[AppDelegate application:openURL:sourceApplication:annotation:] [Line 28] url: openurl://, app: com.apple.mobilesafari, state: 1, annotation: (null)
-[AppDelegate applicationDidBecomeActive:] [Line 52] 

対応

Method Swizzling で、iOS 8 以降と同じ処理になるよう UIApplicationWillResignActiveNotification / UIApplicationDidBecomeActiveNotification を補完してみました。

AppDelegate.m
+ (void)initialize
{
    [super initialize];

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (NSOrderedAscending == [[UIDevice currentDevice].systemVersion compare:@"8.0"
                                                                          options:NSNumericSearch]) {
            Method fromMethod = class_getInstanceMethod([self class],
                                                        @selector(application:openURL:sourceApplication:annotation:));
            Method toMethod = class_getInstanceMethod([self class],
                                                      @selector(iOS7_application:openURL:sourceApplication:annotation:));
            method_exchangeImplementations(fromMethod, toMethod);
        }
    });
}

/**
 iOS 7 では自身のスキームを呼び出した際に UIApplicationWillResignActiveNotification / UIApplicationDidBecomeActiveNotification が呼び出されないため補完する
 */
- (BOOL)iOS7_application:(UIApplication *)application
                 openURL:(NSURL *)url
       sourceApplication:(NSString *)sourceApplication
              annotation:(id)annotation
{
    BOOL result = NO;
    BOOL oneself = NO;
    NSString *bundleIdentifier = sourceApplication;
    
    // 自身のスキーム呼び出し時、sourceApplication が nil になっている
    if (!sourceApplication || 0 == [sourceApplication length]) {
        oneself = YES;
        bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
    }
    
    // UIApplicationWillResignActiveNotification
    if(oneself) {
        // perform delegate
        if([self respondsToSelector:@selector(applicationWillResignActive:)]) {
            [self applicationWillResignActive:application];
        }
        
        // post notification
        [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillResignActiveNotification
                                                            object:nil];
    }
    
    // perform original
    result = [self iOS7_application:application
                            openURL:url
                  sourceApplication:bundleIdentifier
                         annotation:annotation];
    
    // UIApplicationDidBecomeActiveNotification
    if (oneself) {
        // perform delegate
        if([self respondsToSelector:@selector(applicationDidBecomeActive:)]) {
            [self applicationDidBecomeActive:application];
        }
        
        // post notification
        [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidBecomeActiveNotification
                                                            object:nil];
    }
    
    return result;
}

課題

application.applicationState が iOS 8 以降とは異なる

iOS バージョン applicationState
iOS 7 UIApplicationStateActive
iOS 8 later UIApplicationStateInactive

自身を呼び出した時なので気にしなくていいはず…。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?