loganathan
loganathan

Reputation: 2086

How to launch a parent iOS App from its App Extension

Does any one know how to launch the parent app from the app extension's view controller?

I just want to launch the main app from its app extension.

Upvotes: 25

Views: 20552

Answers (10)

sagarthecoder
sagarthecoder

Reputation: 193

Tested in Xcode-16.1, iOS-18.
go to parent app's target -> info. Scroll down and add URL Types info.plist

Here, the Identifier should be your parent app's bundle identifier.

Now add the following code to open the parent app from the App extension

func openParentApp() {
    if let url = URL(string: "openmybestappfromkeyboard://") {
        var responder: UIResponder? = self
        while responder != nil {
            if let application = responder as? UIApplication {
                application.open(url)
                break
            }
            responder = responder?.next
        }
    }
}

Now, go to the parent app's AppDelegate and add the following code.

func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
   if url.scheme == "openmybestappfromkeyboard" {
       return true
   } else {
       return false
   }
}

Upvotes: 2

Ely
Ely

Reputation: 9121

I use this code in my extension to open the main app with a custom scheme url:

fileprivate func openUrl(url: URL?) {
    let selector = sel_registerName("openURL:")
    var responder = self as UIResponder?
    while let r = responder, !r.responds(to: selector) {
        responder = r.next
    }
    _ = responder?.perform(selector, with: url)
}

Make sure that the extension gets enough time to load before calling this function.

Upvotes: 1

Muhammad Ahmad
Muhammad Ahmad

Reputation: 259

Here is Valentin Shergin's solution for ios 14 & xcode 13

extension KeyboardViewController{
    func openURL(url: NSURL) -> Bool {
            do {
                let application = try self.sharedApplication()
                return (application.perform("openURL:", with: url) != nil)
            }
            catch {
                return false
            }
        }

        func sharedApplication() throws -> UIApplication {
            var responder: UIResponder? = self
            while responder != nil {
                if let application = responder as? UIApplication {
                    return application
                }

                responder = responder?.next
            }

            throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
        }
}

Upvotes: 3

Paul B
Paul B

Reputation: 5117

Here is another way to do it:

  1. Step 1 from here
  2. Select extension target and go to its Build Settings. Set Require Only App-Extension Safe API to NO.
  3. Use UIApplication.shared.openURL(URL(string:"openPdf://")!) as you normally would do it outside extension to open urls.

Note, that you'll get 'openURL' was deprecated in iOS 10.0 warning. So this looks like it is not guaranteed to work in future.

Consider using local Notifications to wake up host app instead of this.

Upvotes: 2

Awais Mobeen
Awais Mobeen

Reputation: 852

it is working in my current working app using action extension

1- in parent app plist add custom URL like enter image description here

2- add both functions in your extension view controller

func openURL(url: NSURL) -> Bool {
    do {
        let application = try self.sharedApplication()
        return application.performSelector(inBackground: "openURL:", with: url) != nil
    }
    catch {
        return false
    }
}

func sharedApplication() throws -> UIApplication {
    var responder: UIResponder? = self
    while responder != nil {
        if let application = responder as? UIApplication {
            return application
        }

        responder = responder?.next
    }

    throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
}

3- call this function in your button action or somewhere you want to do

self.openURL(url: NSURL(string:"openPdf://HomeVC")!)

here homevc is the class name of your View controller which should be presented

4- in your app delegate implement the method like

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let urlPath : String = url.absoluteString
    print(urlPath)
    if urlPath.contains("HomeVC"){
        //here go to firstViewController view controller
        self.window = UIWindow(frame: UIScreen.main.bounds)

        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        let initialViewController = storyboard.instantiateViewController(withIdentifier: "homeVC")

        self.window?.rootViewController = initialViewController
        self.window?.makeKeyAndVisible()
    }
}

I hope will work fine, can also share data between extension app and parent app

Upvotes: 12

coyer
coyer

Reputation: 4367

Working solution in Swift 3.1 (tested in iOS10):

You need to create your own URL Scheme for your host app, then add this function to your ViewController and call it with openURL("myScheme://myIdentifier")

//  Function must be named exactly like this so a selector can be found by the compiler!
//  Anyway - it's another selector in another instance that would be "performed" instead.
func openURL(_ url: URL) -> Bool {
    var responder: UIResponder? = self
    while responder != nil {
        if let application = responder as? UIApplication {
            return application.perform(#selector(openURL(_:)), with: url) != nil
        }
        responder = responder?.next
    }
    return false
}

Upvotes: 13

Valentin Shergin
Valentin Shergin

Reputation: 7334

Here is working solution (tested on iOS 9.2) at least for Keyboard Extension. This category adds special method for access to hidden sharedApplication object and then call openURL: on it. (Of course then you have to use openURL: method with your app scheme.)

extension UIInputViewController {

    func openURL(url: NSURL) -> Bool {
        do {
            let application = try self.sharedApplication()
            return application.performSelector("openURL:", withObject: url) != nil
        }
        catch {
            return false
        }
    }

    func sharedApplication() throws -> UIApplication {
        var responder: UIResponder? = self
        while responder != nil {
            if let application = responder as? UIApplication {
                return application
            }

            responder = responder?.nextResponder()
        }

        throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
    }

}

Upvotes: 2

Dan Loughney
Dan Loughney

Reputation: 4677

The documentation is pretty clear on saying that you can use extensionContext openURL within a Today extension. By implication, openURL can only be used in a Today exception and our experiences are proving this to be true--I also can't get it working from within a Share extension.

I would be interested to see if any of these work arounds are accepted by Apple's review process. I doubt it. If Apple wanted it to work, it would have been easy enough for them to allow it.

Apple allows any Today widget to use the openURL:completionHandler: method to open the widget’s own containing app.

If you employ this method to open other apps from your Today widget, your App Store submission might entail additional review to ensure compliance with the intent of Today widgets.

I'm certain this second paragraph was added after Launcher was accepted, rejected, and then later approved for sale.

Upvotes: 2

Karl Monaghan
Karl Monaghan

Reputation: 910

In the WWDC session Creating Extensions for iOS and OS X, Part 1 around the 22 minute mark the say to use the openURL:completionHandler: method from the UIViewController's extensionContext to open a custom URL scheme

[self.extensionContext openURL:[NSURL URLWithString:@"your-app-custom-url-scheme://your-internal-url"]
             completionHandler:nil];

Upvotes: 13

arcticmatt
arcticmatt

Reputation: 2004

I asked a similar question here: Communicating with/opening Containing app from Share extension. Basically, you can do a couple things.

  1. Use a UIWebView's loadRequest webView to load an NSURL request with your containing app's url. For example,

    NSURL *url = [NSURL URLWithString:@"myurl"];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    [self.webView loadRequest:request];
    
  2. Use a UIDocumentInteractionController and a custom file extension type to provide a link to your containing app (and your containing app only)

  3. Start a "fake" NSURL session to get the following functionality: In iOS, if your extension isn’t running when a background task completes, the system launches your containing app in the background and calls the application:handleEventsForBackgroundURLSession:completionHandler: app delegate method.

The first one is probably your best bet.

Upvotes: 2

Related Questions