I created a quick sample app that is just a page with a button in it that when clicked launches SFSafariViewController with a URL pointing at my localhost page that I created. The localhost page just has a single link on it pointing at mytestapp://hello. I registered the mytestapp url scheme in my app settings in Xcode by adding it to the "URL Types" section. The plan was to make sure the URL scheme is working before implementing it into my main app that I am building, but nothing is happening when I click the link in the localhost page. The SFSafariViewController loads perfectly, the localhost page loads properly, I click the link and nothing happens.
I have added a simple print statement in the application(:url:options) method in app delegate, but that never gets run.
Here is the ViewController code...
import UIKit
import SafariServices
class ViewController: UIViewController, SFSafariViewControllerDelegate {
#IBOutlet weak var launchButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func launchTap(_ sender: UIButton) {
guard let url = URL(string: "http://localhost.dcom/test/proof.php") else {
print("Unable to create the URL")
return
}
let authorizationController = SFSafariViewController(url: url)
authorizationController.delegate = self
present(authorizationController, animated: true, completion: nil)
}
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
print("safari completed")
}
}
and the app delegate method here...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("app received url: \(url)")
guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true), let message = components.path else {
print("Invalid URL")
return false
}
print("The message received was \(message)")
return true
}
I can't figure out why this isn't working, seems like I did everything I was supposed to, but the print lines in app delegate never get called.
Not sure if you need it or not, but just in case, my localhost page is literally just...
<?php
echo 'Here goes nothing';
Is there something that might not be working in an emulator in xcode and it has to be on an actual device for URL schemes to work? Did I miss a step somewhere? somethings else I have no idea? Any help would be really appreciated. Thank you
Here is a test project
Only change I have done is update webpage to following. Nothing wrong with PHP you have it's because I don't have PHP setup.
Also, you get callbacks in Scene Delegate's
func scene(_ scene: UIScene, openURLContexts URLContexts: Set)
<html>
<body>
Here goes nothing
</body>
</html>
Related
I'm trying to set up DeepLinks on my webview app and it's almost done but I've ran into a final obstacle.
At the moment my code is working as follows:
I have a function declared in SceneDelegate that is triggered when the user clicks the deep link and then proceeds to "clean" the link which is only removing the "myappname://" from the start of it.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let firstUrl = URLContexts.first?.url else {
return
}
let originalString = firstUrl.absoluteString
let desiredString = originalString.components(separatedBy: "sports://").last
g_url = desiredString
let notificationName = NSNotification.Name(rawValue: "deepLink")
NotificationCenter.default.post(name: notificationName, object: nil)
}
}
It then proceeds to store the String containing the link in a variable that is declared at the top as: var g_url: String?
And sends a notification alert that my ViewController catches and calls the function to handle the deepLink
#objc func updateUrl(){
let new_url = SceneDelegate.shared?.g_url
if(new_url != nil){
let request = URLRequest(url: URL(string: new_url!)!)
self.webView.load(request)
}
}
Where it simply loads that link to the webview.
This is where my problem lies, when it tries to load the link it throws this error:
WebPageProxy::didFailProvisionalLoadForFrame: frameID=3, isMainFrame=1, domain=WebKitErrorDomain, code=101, isMainFrame=1
The problem I think has something to do with the way the String is being encoded or handled, especially since if I use the exact same url but hard code it into the request it works with no issue.
I'm not sure what I'm doing wrong and would really appreciate some help, thank you :)
Found out it was due to an error from the deeplink simulator, RocketSim, that for some reason removed the double quotes from "https://" sending it as "https//" which when used in the webView.load() method throws an error because the URL wasn't valid.
I've followed specific steps from the official docs and have implemented SCSDK to login with Snapchat. I added this code to my SceneDelegate to redirect back to my app, but it doesn't work --
the print(url) works and prints the url, but SCSDKLoginClient.application(UIApplication.shared, open: url, options: options) doesn't work at all i even added a debug pointer to check, but nothing .
Is there any solution for this or something i'm missing , and do i have to use SceneDelegate?
Also, I have checked all the data in the info.plist, including the scheme, and everything is in place.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for urlContext in URLContexts {
let url = urlContext.url
print(url)
var options: [UIApplication.OpenURLOptionsKey : Any] = [:]
options[.openInPlace] = urlContext.options.openInPlace
options[.sourceApplication] = urlContext.options.sourceApplication
options[.annotation] = urlContext.options.annotation
SCSDKLoginClient.application(UIApplication.shared, open: url, options: options)
}
}
and this is the login function in my ViewController:
SCSDKLoginClient.login(from: self) { (success, error) in
if success == true {
print("OK")
} else {
print("NO")
}
}
Turns Out I was using a wrong account to login in snapchat , I forgot I had to use the same developer account or at least add the other account to this one
I have a Today extension and let say I just want to open a url let url = "https://google.com" when I press the button goButton in my widget to open it in my app with safari ( inside my app using SafariServices )
I have defined a custom url scheme in my Info.plist and I wrote those lines of code in my button (class TodayViewController) :
#IBAction func goButton(_ sender: Any) {
let myAppUrl = URL(string: "Easy-tools://")!
extensionContext?.open(myAppUrl, completionHandler: { (success) in
if (!success) {
print("error: failed to open app from Today Extension")
}
})
}
I have sucssesfuly open my main app from my widget but i don't know how to open the url let url = "https://google.com" when i press goButton I have added this func in my AppDelegate file :
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
but i don't know how to open it , by the way I'm new in swift
So this is actually a question about SafariServices rather than about Today extensions?
By saying that you are "new in swift" do you mean that you have worked with iOS before but used Objective-C instead?
I assume that you have read the SafariServices documentation. I have not used it yet, but I think it should work like this:
Implement application(_:open:options:) (the one you mention above) and initialize an SFSafariViewController using your URL.
Present your safari view controller or push it onto a navigation stack, just like any other view controller.
Apps created from Xcode templates have some kind of root view controller set up for you, so you can use that for presentation before you figure out where exactly you want to present your safari view.
I'm adding an iMessage extension target to my app. The extension is supposed to send a message that has a url attribute. The behaviour I'm expecting when a user touches the message is to open the browser using the url attribute of the message.
I have a button in my messageView which executes this code:
#IBAction func labelButton(_ sender: Any) {
let layout = MSMessageTemplateLayout()
layout.imageTitle = "iMessage Extension"
layout.caption = "Hello world!"
layout.subcaption = "Test sub"
guard let url: URL = URL(string: "https://google.com") else { return }
let message = MSMessage()
message.layout = layout
message.summaryText = "Sent Hello World message"
message.url = url
activeConversation?.insert(message, completionHandler: nil)
}
If I touch the message, it expands the MessageViewController
I have then added this:
override func didSelect(_ message: MSMessage, conversation: MSConversation) {
if let message = conversation.selectedMessage {
// message selected
// Eg. open your app:
self.extensionContext?.open(message.url!, completionHandler: nil)
}
}
And now, when I touch the message, it opens my main app but still not my browser.
I have seen on another post (where I cannot comment, thus I opened this post) that it is impossible to open in Safari but I have a news app which inserts links to articles and allows with a click on the message to open the article in a browser window, while the app is installed.
So, can someone please tell how I can proceed to force opening the link in a browser window?
Thank you very much.
Here is a trick to insert a link in a message. It does not allow to create an object that has an url attribute but just to insert a link directly which will open in the default web browser.
activeConversation?.insertText("https://google.com", completionHandler: nil)
I have published a sample on github showing how to launch a URL from inside an iMessage extension. It just uses a fixed URL but the launching code is what you need.
Copying from my readme
The obvious thing to try is self.extensionContext.open which is documented as Asks the system to open a URL on behalf of the currently running app extension.
That doesn't work. However, you can iterate back up the responder chain to find a suitable handler for the open method (actually the iMessage instance) and invoke open with that object.
This approach works for URLs which will open a local app, like settings for a camera, or for web URLs.
The main code
#IBAction public func onOpenWeb(_ sender: UIButton) {
guard let url = testUrl else {return}
// technique that works rather than self.extensionContext.open
var responder = self as UIResponder?
let handler = { (success:Bool) -> () in
if success {
os_log("Finished opening URL")
} else {
os_log("Failed to open URL")
}
}
let openSel = #selector(UIApplication.open(_:options:completionHandler:))
while (responder != nil){
if responder?.responds(to: openSel ) == true{
// cannot package up multiple args to openSel so we explicitly call it on the iMessage application instance
// found by iterating up the chain
(responder as? UIApplication)?.open(url, completionHandler:handler) // perform(openSel, with: url)
return
}
responder = responder!.next
}
}
I have a Safari share extension where I want the ability to open the main app from within the extension. The user is presented with an alert where they have the option to open the app.
func openAppHandler() {
self.extensionContext?.completeRequest(returningItems: []) { (success) in
if let url = URL(string: "myapp://...") {
self.extensionContext?.open(url, completionHandler: nil)
}
}
}
The alert appears after the method didSelectPost() is called, and as you can see it occurs in the background priority completion block for the extension. The open method says in it's docs "In iOS 8, only the Today extension point (used for creating widgets) supports this method." I'm guessing it's still the case that it's still not supported in the Safari Share Extension.
Does anyone know of a way to open my main app from a share extension?
I found a solution here. I'm not sure if this is technically ok with Apple, but it works just as I need it to.
#objc func openURL(_ url: URL) {
return
}
func openContainerApp() {
var responder: UIResponder? = self as UIResponder
let selector = #selector(MyViewController.openURL(_:))
while responder != nil {
if responder!.responds(to: selector) && responder != self {
responder!.perform(selector, with: URL(string: "myapp://url")!)
return
}
responder = responder?.next
}
}