I'm working on an app that needs to get an authorization token from an external provider.
So, I need a custom URL scheme for the redirection callback.
The redirection callback is: chirper://success.
I registered the URL Scheme in my Info.plist:
I also added the following method in my AppDelegate.swift:
func handleGetURLEvent(event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) {
if let aeEventDescriptor = event?.paramDescriptor(forKeyword: AEKeyword(keyDirectObject)) {
if let urlStr = aeEventDescriptor.stringValue {
let url = URL(string: urlStr)
print(url)
// do something with the URL
}
}
}
But when I open the redirection callback URL with Safari, this is what I get:
Safari can't open this URL because macOS doesn't recognize URLs that start with chirper:
Try to "Clean Build Folder" and rebuild. Did help for me. Looks like this is required in some cases.
According to the answer to this question, the problem is the inclusion of :// in the callback URL. If you remove those, the URL should be opened. I found this was correct on macOS 10.15.5.
Related
I am using ASWebAuthenticationSession to try and authenticate with Google's OAuth 2.0 API in a native Mac app. I registered my app in the Google Developer Console as a Desktop type app like this:
I construct my URL like this:
func createGmailURL() -> String{
var url = String.init()
url.append("https://accounts.google.com/o/oauth2/v2/auth")
url.append("?client_id=*****.apps.googleusercontent.com")
url.append("&redirect_uri=cova://oauth/gmail")
url.append("&response_type=code")
url.append("&scope=https://mail.google.com/")
return url
}
I then initiate the request like this:
let url = createGmailURL()
guard let authURL = URL(string: url) else { return }
let scheme = "cova"
let session = ASWebAuthenticationSession(url: authURL, callbackURLScheme: scheme){ callbackURL, error in
...
}
Google seems to reject my request because of something related to the redirect_uri.
I can't for the life of me see what I've done wrong. It seems Google doesn't want to allow me to use a custom URI. Do I have to change the app type to something else? Does Desktop not apply to Mac apps?
In it's FAQ, WhatsApp explains how to create a new, prefilled message where the user can choose the contact to send the message to:
"To create a link with just a pre-filled message" from the FAQ Page here: https://faq.whatsapp.com/iphone/how-to-link-to-whatsapp-from-a-different-app
The link looks like this: https://wa.me/?text=I%27m%20inquiring%20about%20the%20apartment%20listing (example from the FAQ Page).
However, since a few days this seems not to work anymore on iOS and Desktop (Android Phones seems not to be affected yet).
The link with a phone number (https://wa.me/123456789?text=The%20message%20to%20send) is working just fine. The workaround with just adding "0" as the phone number doesn't work either as whatsApp will throw an error that the contact could not be found.
Has anyone else run into the same problem? And found a soution / workaround?
Your URL seems to be wrong. You need to append URL path which is /send in your URL string. You seem to be adding the URLQueries right after the base URL. Also, use addingPercentEncoding for encoding.
guard
let urlString = "whatsapp://send?text=\(text)"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let unwrappedUrl = URL(string: urlString) else {
return nil
}
I have successfully saved a CKShare URL to CloudKit, and I can see that the user is INVITED in the CloudKit Dashboard. My Mac app emailed the URL to that person, but when they click it, all they see it this screen on icloud.com:
Clicking OK makes everything disappear so all you see is the background on the web page.
My understanding is that the URL is supposed to open my Mac app where it will fire userDidAcceptCloudKitShareWith in my app delegate. But it does nothing.
Could this be because my app is in development and not in the Mac App Store yet? Do I need a custom URL scheme to get it to open my app?
Documentation on this stuff is pretty sparse. I'd love any help someone can provide.
I have since learned that you must specify a fallback URL for your CloudKit container. In cases where the app isn't installed (or isn't recognized, which seems to be the case when doing dev builds in Xcode like I am), CloudKit will forward share URL to somewhere you specify. They append the unique share ID to the URL so that you can process it on your own web page.
In the CloudKit dashboard, go to Environment Settings... and you'll see this popup:
I have it redirect to https://myapp.com/share/?id= and on my web page where it redirects to, I do a $_GET['id'] to grab the id. I then do another redirect to my application using a custom URL scheme and pass the share ID (e.g. myapp://abc123 where abc123 is the share ID).
In my app delegate, I receive the URL like this:
func application(_ application: NSApplication, open urls: [URL]) {
if let url = urls.first, let shareId = url.host{
fetchShare(shareId) //<-- sharedId = abc123
}
}
I then use CKFetchShareMetadataOperation to look up the URL of the share and CKAcceptSharesOperation to accept it like this:
func fetchShare(shareId: String){
if let url = URL(string: "https://www.icloud.com/share/\(shareId)"){
let operation = CKFetchShareMetadataOperation(shareURLs: [url])
operation.perShareMetadataBlock = { url, metadata, error in
if let metadata = metadata{
//:::
acceptShare(metadata: metadata)
}
}
operation.fetchShareMetadataCompletionBlock = { error in
if let error = error{
print("fetch Share error: \(error)")
}
}
CKContainer.default().add(operation)
}
}
func acceptShare(metadata: CKShareMetadata){
let operation = CKAcceptSharesOperation(shareMetadatas: [metadata])
operation.acceptSharesCompletionBlock = { error in
if let error = error{
print("accept share error: \(error)")
}else{
//Share accepted!
}
}
CKContainer.default().add(operation)
}
I think there are easier ways to work through this using NSItemProvider and NSSharingService, but I'm doing a lot of custom UI and wanted to have full control of the share workflow.
I hope this helps someone. :)
I am trying to navigate to the Microsoft Live oauth authentication page so that I can authorize my user and get a token for use by the app. When I use the following NSURL string, I am able to navigate to the site and authorize my app, retrieving the token.
let stringUrl = "https://login.live.com/oauth20_authorize.srf?client_id=\(clientId)&scope=\(scope)&response_type=code"
let url = NSURL(string: stringUrl)!
However, I want to redirect back to my app (using SFSafariViewController). In order to do so, I added a URL Scheme to my app, and passed that in as the redirect_uri in the URL.
let stringUrl = "https://login.live.com/oauth20_authorize.srf?client_id=\(clientId)&scope=\(scope)&response_type=code&redirect_uri=Lifestream://onedrive-oauth-callback"
let url = NSURL(string: stringUrl)!
However the Live login site gives me an error saying something went wrong and it couldn't continue. This error occurs before the oauth login page is displayed. It happens immediately upon navigating to the URL.
Am I creating my URL scheme incorrectly, or passing the scheme in to the redirect uri improperly? I'm confused as to why it works fine without the redirect_uri, but the site can't load when I provide it.
Can someone point me in the right direction on how I am supposed to pass a redirect url for my app, into an oauth redirect?
Update
It seems that Microsoft does not allow you to register a redirect URL that is a App URL scheme. I dont know how to get this info back into my app then, other than just paying for a site I can point MSFT to, which would then do nothing but redirects into the app for me.
I had a similar OAuth problem and didn't want to mess around with a web server so instead what I did was kinda cheeky. Make a UIWebView and put the request on the web view. Then delegate it to yourself and pass the redirect URL to be http://localhost:8000 (it can be anything like that it really doesn't matter). Then inside the delegate do this:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool
{
if let URL = request.URL?.absoluteString
{
// This should be the redirect URL that you pass it can be anything like local host mentioned above.
if URL.hasPrefix(redirectURL)
{
// Now you can simply do some string manipulation to pull out the relevant components.
// I'm not sure what sort of token or how you get it back but assuming the redirect URL is
// YourRedirectURL&code=ACCESS_TOKEN and you want access token heres how you would get it.
var code : String?
if let URLParams = request.URL?.query?.componentsSeparatedByString("&")
{
for param in URLParams
{
let keyValue = param.componentsSeparatedByString("=")
let key = keyValue.first
if key == "code"
{
code = keyValue.last
}
}
}
// Here if code != nil then it has the ACCESS_TOKEN and you are done! If its nil something went wrong.
return false // So that the webview doesnt redirect to the dummy URL you passed.
}
}
return true
}
This is sorta hacky but it works great and you don't need any server nor do you need a redirect URI on your app, its an awesome way to do it in my opinion. You could optimize this for swift 2 to reduce the indentation by using guards but I wrote this before it came out so...
Hope this helps!
I am working on a project where I share video on Vimeo. In this My app open a video where user needs to press authorize button to authorize the app at Vimeo and to get access tokens. So, for this, my app opens safari and open Vimeo's site there. The user needs to press allow button then it has to come back again to the app. But I am not able to know what should be the call back url to make the Safari/Vimeo to come back to my app.
Please suggest your views regarding this.
You need to set a custom URL scheme for your app by editing your app's Info.plist. There's plenty of documentation about this on Apple's developer website. Here's an article that goes into detail: http://iosdevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html
Then your website just needs to open a url that uses your app's url scheme (eg: myappscheme://do/something/cool?foo=bar). If your app cares about any data passed in to it via your website then implement the "application:openURL:sourceApplication:annotation:" method and inspect the NSURL passed in. You can read more about this in Apple's documentation: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html
You need to implement something called 'URL Scheme' to your app, which means register your app to a certain url, so it can be opened from.
1) You should add a row to your info.plist file.
2) You need to listen to the url in your app, and do what needed.
Google for more info...
To support a custom URL scheme:
Define the format for your app's URLs.
Register your scheme so that the system directs appropriate URLs to your app.
Handle the URLs that your app receives.
URLs must start with your custom scheme name. Add parameters for any options your app supports. For example, a photo library app might define a URL format that includes the name or index of a photo album to display.
an example is :
myphotoapp:albumname?name="foods"
myphotoapp:albumname?index=1
Register Your URL Scheme
click on project target and goto info page
in the info page expand URL Types section and hit the + button
fill fields with appropriate values.
Handle Incoming URLs
The system delivers the URL to your app by calling your app delegate's application(_:open:options:)method. you can useNSURLComponents` APIs to extract the components. Obtain additional information about the URL, such as which app opened it, from the system-provided options dictionary.
func application(_ application: UIApplication,
open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any] = [:] ) -> Bool {
// Determine who sent the URL.
let sendingAppID = options[.sourceApplication]
print("source application = \(sendingAppID ?? "Unknown")")
// Process the URL.
guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
let albumPath = components.path,
let params = components.queryItems else {
print("Invalid URL or album path missing")
return false
}
if let photoIndex = params.first(where: { $0.name == "index" })?.value {
print("albumPath = \(albumPath)")
print("photoIndex = \(photoIndex)")
return true
} else {
print("Photo index missing")
return false
}
}
If your app has opted into Scenes, and your app is not running, the system delivers the URL to the scene(_:willConnectTo:options:) delegate method after launch, and to scene(_:openURLContexts:) when your app opens a URL while running or suspended in memory.
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
// Determine who sent the URL.
if let urlContext = connectionOptions.urlContexts.first {
let sendingAppID = urlContext.options.sourceApplication
let url = urlContext.url
print("source application = \(sendingAppID ?? "Unknown")")
print("url = \(url)")
// Process the URL similarly to the UIApplicationDelegate example.
}
}