7

I am trying to open specific view controller on widgets click , but can not able to open it , i am able to open app using url schema but i want to open specific view controller how can i do this, here is code for open app using url schema :

@IBAction func open_app(_ sender: Any) { extensionContext?.open(URL(string: "open://")! , completionHandler: nil) } on button click i am opeing app sucessfully using url schema. but now i want to open specific view controller on that click how can i do this?

Asmita
  • 477
  • 8
  • 20

4 Answers4

14

According to your requirement, I have created a sample to get this working correctly.

1. First of all in TodayViewController interface, create 3 different UIButtons and give their tag values to uniquely identify them. enter image description here

Here I have given tags as: 1, 2, 3 to First, Second and Third UIButton.

2. Next you need to write the code to open your Containing App from Today Extension. In TodayViewController create an @IBAction for and connect it to all three UIButtons.

@IBAction func openAppController(_ sender: UIButton)
{
    if let url = URL(string: "open://\(sender.tag)")
    {
        self.extensionContext?.open(url, completionHandler: nil)
    }
}

In the above code, tag will be added to the url scheme to identify which UIViewController needs to be opened on UIButton press. So the url will look something like: open://1

3. In the Containing App's URL Types need to make an entry for URL Scheme, i.e

enter image description here

As evident from the above screenshot, there is no need to make entry for each url that you want to open from your extensions. URLs having same url scheme have only a single entry.

4. When the containing app is opened from extension, you can get the handle in AppDelegate’s application(_ : url: sourceApplication: annotation: ) method. Here, you can handle which controller to open i.e.

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
{
    if url.scheme == "open"
    {
        switch url.host
        {
        case "1":
            //Open First View Controller

        case "2":
            //Open Second View Controller

        case "3":
            //Open Third View Controller

        default:
            break
        }
    }
    return true
}

url.scheme identifies the scheme of URL i.e. open and url.host identifies the host component in the URL which is currently set to the UIButton's tag value which you can use to uniquely identify which UIButton is pressed and what to de next accordingly.

For more on Today Extensions, you can refer to: https://hackernoon.com/app-extensions-and-today-extensions-widget-in-ios-10-e2d9fd9957a8

Let me know if you still face any issues regarding this.

PGDev
  • 23,751
  • 6
  • 34
  • 88
  • Sure. Accept and upvote the answer if it works for you. Happy Coding.. – PGDev Mar 18 '18 at 15:33
  • So I implemented this and kept getting: Thread 7: EXC_BAD_ACCESS (code=1, address=0x10). Finally got it to work by just simplifying everything to let url = URL(string: "open")! self.extensionContext.open(url) and just using "open" as the scheme with no identifier. It worked. Then I changed open to something more unique in all the same places and tried again. It broke. So I changed it back to open and now it's broke again. – metamonkey Sep 17 '21 at 20:22
2

add a new scheme for your App

enter image description here

as Shown above image...

then, write a code below on IBAction of your Today Extension

@IBAction func btnFirstWidgetAction() {
    let url: URL? = URL(string: "schemename://secondViewController")!
    if let appurl = url { self.extensionContext!.open(appurl, completionHandler: nil) }
}

@IBAction func btnSecondWidgetAction() {
    let url: URL? = URL(string: "schemename://secondViewController")!
    if let appurl = url { self.extensionContext!.open(appurl, completionHandler: nil) }
}

@IBAction func btnThirdWidgetAction() {
    let url: URL? = URL(string: "schemename://thirdViewController")!
    if let appurl = url { self.extensionContext!.open(appurl, completionHandler: nil) }
}

than, add method application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) in AppDelegate file and write code to redirect in specific ViewController in this method.

//call when tap on Extension and get url that is set into a ToadyExtension swift file...
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    let urlPath : String = url.absoluteString
    print(urlPath)

    if self.isContainString(urlPath, subString: "firstViewController") {
        //here go to firstViewController view controller
    }
    else if self.isContainString(urlPath, subString: "firstViewController") {
        //here go to secondViewController view controller
    }
    else {
        //here go to thirdViewController view controller
    }

    return true
} 

this method used for check your string is contains as sub string that are given in widget button action. if contain than true otherwise false

 func isContainString(_ string: String, subString: String) -> Bool {
    if (string as NSString).range(of: subString).location != NSNotFound { return true }
    else { return false }
}
Mak K
  • 83
  • 1
  • 10
  • thanks for replay but i have 3 view controller in application on widgets click open specific view controller , is i need three seperate url schemas for each view controller ?? – Asmita Mar 13 '18 at 07:06
  • No. There is no need to create a separate url scheme. Generally, url scheme are used for open application from widget. b'coz Apple consider that widget is other app that you try to open your app from widget that you want to open from it. – Mak K Mar 15 '18 at 09:07
  • can you give me e.g of if there is 3 view controller in application & i want to open each view controller on 3 different buttons in widgets – Asmita Mar 15 '18 at 09:16
  • Pls check the answer and refer below link for more information about App Extension. https://grokswift.com/notification-center-widget/ – Mak K Mar 15 '18 at 11:49
  • one more issue how i check user is login or not in today extenion?? – Asmita Mar 19 '18 at 09:25
  • you have to store user login detail and when user tap on Today extension at that time you have to check that user is login nor not. – Mak K Mar 20 '18 at 03:57
  • This should be the accepted answer. because I find it quite well explained in a step-by-step manner. I was very much confused about the URL scheme. – Tejas Sep 06 '19 at 06:24
1

In xCode 11 if you are using sceneDelegate, follow the same logic as described by Malik and Mahesh but use the following function in the SceneDelegate instead:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {

  if let url = URLContexts.first?.url {
      //Do stuff with the url
  }

}

(instead of application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool)

In details:

First:

Add a url scheme in your project -> info -> url Types -> add url Scheme. Here you can get started by filling the 'URL Schemes" field only (with your app name for instance).

Second:

In your extension, use the following function (called by a button for instance):

let urlString = "MyAppName://host/path"
if let url = URL(string: urlString)
{

   self?.extensionContext?.open(url, completionHandler: nil)

 }

Third:

Implement your logic in Scene Delegate with:

 func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {

    if let url = URLContexts.first?.url {
      //Do stuff with the url
    }

 }
Gautier
  • 103
  • 1
  • 4
0

Swift5

Step1: select project>info>url types>add url scheme

step2: go to the button action method and use this code

let tag = 1
        if let url = URL(string: "open://\(tag)")
        {
            self.extensionContext?.open(url, completionHandler: nil)
        }

step 3: welcome you get the control of your host app, jus add this in app delegate method

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
    {
        if url.scheme == "open"
        {
            switch url.host
            {
            case "1":
              let storyboard = UIStoryboard(name: "Main", bundle: nil)
                  let vc = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
                  self.window?.rootViewController = vc
                  self.window?.makeKeyAndVisible()
            default:
                break
            }
        }
        return true
    }

Congrats! you open the controller.