URL not being passed to appdelegate - swift

I can't get dynamic links or universal links working. My app won't read in a URL. I've started with a fresh project, added a URL Scheme of com.company.AppLoginView and the below code into AppDelegate. I added a url in notes of: com.company.AppLoginView://Register?User=Name. Clicking the link requests to open the app which then opens. However, nothing seems to be passed to the app and the below isn't being called. What am I doing wrong? This is a completely empty app and should work.
func application(_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if let scheme = url.scheme,
scheme.localizedCaseInsensitiveCompare("com.myApp") == .orderedSame,
let view = url.host {
var parameters: [String: String] = [:]
URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.forEach {
parameters[$0.name] = $0.value
}
print("View:\(view) Params:\(parameters) Scheme:\(scheme)")
// redirect(to: view, with: parameters)
}
return true
}

This is because target is iOS 13 and SceneDelegate takes over some functions from AppDelegate. In particular, open:url is replaced by SceneDelegate scene:openURLContexts.
More here: Apple openURLContexts
3 days of my life I won't get back!

You should add ULR scheme and URL identifier to plist. You can use this:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.company.AppLoginView</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.company.AppLoginView</string>
</array>
</dict>
</array>
and check it with print in method in AppDelegate
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print(url)
return true
}
open Safari and type your address
com.company.AppLoginView://Register?User=Name

Related

In iOS, how can I get the contact shared from "Recent" calls list to my app in Flutter?

Please excuse me if I sound stupid, I'm new to flutter.
I have started learning flutter recently and wanted to create an app where anyone can share a contact from the "Recent" calls list to my app. I'm following this blog post which allows text share from any other app to my app.
What I have done so far:
This is my plist file, added the public.vcard to allow my app to appear on the tap of "Share Contact".
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<string>
SUBQUERY (
extensionItems, $extensionItem,
SUBQUERY (
$extensionItem.attachments, $attachment,
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.vcard"
).#count >= 1
).#count > 0
</string>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
Here's my ShareViewController.swift
import Social
import MobileCoreServices
class ShareViewController: SLComposeServiceViewController {
override func isContentValid() -> Bool {
// Do validation of contentText and/or NSExtensionContext attachments here
print("Something is not right")
return true
}
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
let sharedSuiteName: String = "group.com.thelogicalbeing.whatsappshare"
let sharedDataKey: String = "SharedData"
let extensionItem = extensionContext?.inputItems[0] as! NSExtensionItem
let contentTypeText = kUTTypeText as String // Note, you need to import 'MobileCoreServices' for this
for attachment in extensionItem.attachments! {
print(attachment)
if attachment.hasItemConformingToTypeIdentifier(contentTypeText) {
attachment.loadItem(forTypeIdentifier: contentTypeText, options: nil, completionHandler: {(results, error) in
if let sharedText = results as! String? {
if let userDefaults = UserDefaults(suiteName: sharedSuiteName) {
userDefaults.set(sharedText, forKey: sharedDataKey)
}
}
})
}
}
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
}
Here's my AppDelegate.swift
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let sharedSuiteName: String = "group.com.thelogicalbeing.whatsappshare"
let sharedDataKey: String = "SharedData"
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel(name: "com.thelogicalbeing.whatsappshare", binaryMessenger: controller.binaryMessenger)
methodChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
if call.method == "getSharedData" {
if let prefs = UserDefaults(suiteName: sharedSuiteName) {
if let sharedText = prefs.string(forKey: sharedDataKey) {
result(sharedText);
}
// clear out the cached data
prefs.set("", forKey: sharedDataKey);
}
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
What I'm trying to achieve is that I need to receive the phone number and display it in my app.
Don't know how to proceed. Any help will be appreciated.
1- Apple does not allow fetching call logs on iOS!
You can fetch all contacts with all their information. But not the calls log.
2- On Android you can use the pub.dev dependency plugin call_log to do that.
Have a look at this package called receive_sharing_intent, it allows you to to receive sharing photos, videos, text, urls or any other file types from another app. And it also supports iOS Share extension and launching the host app automatically.

Can't open a link when the default browser has been changed

After updating to iOS 14 I cannot open urls in the browser if it has been changed from default Safari to Chrome.
I did some investigation and found that UIApplication.shared.canOpenURL(url) always returns false.
Please help.
For iOS 14+
I suggest to add this to your Info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
</array>
By adding this you can continue to use method canOpenURL(_ url: URL) -> Bool
guard let url = URL(string: "https://example.com") else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}

Is Uber's API still stable? I can't get basic authentication working IOS 13 swift 5.0

Was trying to do basic integration of Uber's IOS Ride Request SDK, I followed the instructions here https://developer.uber.com/docs/riders/ride-requests/tutorials/api/ios
When trying to authenticate a user it opens the Uber app then immediately returns to my app. The console prints "code=25 'User cancelled the login process.'" I'm using a real device with IOS 13 and Swift 5.0. It looks like the Uber Developers social media hasn't been updated since 2017, and it doesn't appear very active.
Info.plist
<key>UberClientID</key>
<string>907YWpr4cTwH9-TWVP0Fq8DX-_HuCxN3</string>
<key>UberDisplayName</key>
<string>Wandr</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>uber</string>
<string>uberauth</string>
</array>
<key>UberCallbackURIs</key>
<array>
<dict>
<key>UberCallbackURIType</key>
<string>General</string>
<key>URIString</key>
<string>com.wandrinc.Wandr://oauth/consumer</string>
</dict>
</array>
login function
func loginToUber() {
let loginManager = LoginManager()
loginManager.login(requestedScopes:[.request], presentingViewController: UIApplication.shared.keyWindow?.rootViewController!, completion: { accessToken, error in
if let error = error {
print(error)
return
}
self.requestRide()
})
}
App Delegate
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
let handledUberURL = UberAppDelegate.shared.application(app, open: url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String, annotation: options[UIApplication.OpenURLOptionsKey.annotation] as Any)
return handledUberURL
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let handledUberURL = UberAppDelegate.shared.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
return handledUberURL
}
Output
Error Domain=com.uber.rides-ios-sdk.ridesAuthenticationError Code=25 "User cancelled the login process." UserInfo={NSLocalizedDescription=User cancelled the login process.}

iOS Today Widget Universal Links From Lock Screen and Notification Center

We have a today widget to deep link into the app. The deep links work just fine when the user accesses the widget from the home screen. However, when a user accesses the widget when the device is locked, or when the user slides down from the top of the screen, the links open in Safari.
I was wondering if anyone else has come across this issue, and if so, how they solved it.
Here was the solution we came upon (Swift 4.1). We needed to support a custom URL scheme to tell iOS that we can open links from the today widget. This uses a different UIApplication delegate function. Along with implementing func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool, we also need to implement func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool
First, in Info.plist, we have our supported schemes under CFBUndleURLTypes.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>todayWidgetScheme</string>
</array>
</dict>
</array>
Then, also in Info.plist, we also listed the scheme under LSApplicationQueriesSchemes.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>todayWidgetScheme</string>
</array>
Next, when opening the link from the today widget, set the url scheme to the iOS recognized todayWidgetScheme.
func openAppFromTodayWidget() {
if let url = URL(string: "https://url.com") {
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)
components?.scheme = "todayWidgetScheme"
if let todayWidgetUrl = components?.url {
extensionContext?.open(todayWidgetUrl)
}
}
}
Finally, in AppDelegate.swift, when iOS asks the application to handle the universal link, set the original url scheme
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if url.scheme == "todayWidgetScheme" {
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)
components?.scheme = "https"
if let todayWidgetUrl = components?.url {
// do your thing
return true
}
}
return false
}

How to share a Note including Markup with Share Extensions Swift

I've already figured out how to share the general content of a Note (text and pictures). But the way I currently approach the problem the markup of the note is not kept (Titles, Lists, etc.). I just receive the pure text of the note. When you share a note with Mail for instance, you can see that the markup is transferred. Is there a way to do that for your own apps?
My current solution where I only receive the pure text:
class ShareViewController: UIViewController{
override func viewDidLoad() {
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
// Verify the provider is valid
if let contents = content.attachments as? [NSItemProvider] {
// look for images
for attachment in contents {
print(attachment.registeredTypeIdentifiers)
if attachment.hasItemConformingToTypeIdentifier("public.plain-text"){
attachment.loadItem(forTypeIdentifier: "public.plain-text", options: nil) { data, error in
let string = data as! String
print(string)
}
}
}
}
}
}
}
EDIT:
My current NSExtensionActivationRules:
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationDictionaryVersion</key>
<integer>2</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>100</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
</dict>
</dict>
Use the attributedContentText property of your NSExtensionItem (content):
override func viewDidLoad() {
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
// move your content validation to `func isContentValid() -> Bool`
let attributedString = content.attributedContentText // yay NSAttributedString!
}
}