Using spotlight in iOS app with deployment target iOS 8 - swift

I have iOS app with minimal deployment target set to iOS 8.0 and I want to enable spotlight search in it. I understand that spotlight search can be used only in iOS 9 and higher. That is why in my AppDelegate I am using available
#available(iOS 9.0, *)
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
if userActivity.activityType == CSSearchableItemActionType {
if let uniqIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
print(uniqIdentifier)
}
}
return true
}
And Xcode gives me this error
Protocol 'UIApplicationDelegate' requires
'application(_:continue:restorationHandler:)' to be available on iOS
8.0 and newer
What can I do with it

I found solution so I decide to post it as it may be useful to other developers. After import of CoreSpotlight
import CoreSpotlight
You should add application(_:continue:restorationHandler:) method if you are targeting iOS 8.0 and newer. But as 'CSSearchableItemActionType' is only available on iOS 9.0 or newer you should add version check inside method.
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
if #available(iOS 9.0, *) {
if userActivity.activityType == CSSearchableItemActionType {
if let uniqIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
print(uniqIdentifier)
}
}
}
return true
}

Related

Still receives INStartAudioCallIntent when drop support for iOS 12(INStartCallIntent)

I've meet a strange issue when dropping the support for iOS 12. When handle the user activity from AppDelegate continue userActivity, Although we drop INStartAudioCallIntent and INStartVideoCallIntent for it is deprecated in iOS 13, we still receive the above 2 intents from native contact card. But actually we want to handle INStartCallIntent instead.
Anyone knows why this happens for my debug version is 14.6, thanks.
Adding Intent as an app extension to handle INStartCallIntentHandling made my AppDelegate receive INStartCallIntent!
It can be implemented like this:
File > New -> Target -> Intent
Add INStartCallIntent to Supported Intents
Implement the IntentHandler (In the intent extension) like this:
class IntentHandler: INExtension, INStartCallIntentHandling {
override func handler(for intent: INIntent) -> Any {
return self
}
func handle(intent: INStartCallIntent, completion: #escaping (INStartCallIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INStartCallIntent.self))
let response = INStartCallIntentResponse(code: .continueInApp, userActivity: userActivity)
completion(response)
}
}
The INStartCallIntent will now be called in the AppDelegate:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if let intent = userActivity.interaction?.intent {
if let intent = intent as? INStartCallIntent {
// The correct INStartCallIntent
}
}
}

How I can write this method in Objective-C - (application: continue userActivity: restorationHandler:)?

I am implementing Universal links in my Objective-C app.
Please, help me to translate this Swift method into Objective-C:
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
This is a built-in method, which is documented both in Swift and Objective-C. The Obj-C function signature is
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler;

SwiftUI swizzling disabled by default, phone auth not working

I am building a screen with phone number login. I checked over and over again and the project is newly created, however, I am getting this log:
7.2.0 - [Firebase/Auth][I-AUT000015] The UIApplicationDelegate must handle remote notification for phone number authentication to work.
If app delegate swizzling is disabled, remote notifications received by UIApplicationDelegate need to be forwarded to FIRAuth's canHandleNotificaton: method.
I did read in the documentation about swizzling and I don't know why it seems to be disabled, I did not disabled it. I have added GoogleServices-Info.plist into the app, I added in firebase panel the app apn auth key.
My entry point in the app looks like this:
#main
struct partidulverdeApp: App {
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
MainView()
.onOpenURL { url in
Auth.auth().canHandle(url.absoluteURL)
}
}
}
}
My URL Types property has an entry with the RESERVED_CLIENT_ID
I am very desperate about this problem. Any idea is highly appreciated.
Edit1:
I did read the documentation and tried to handle notification with swizzling disabled, but I get the same error:
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didReceiveRemoteNotification notification: [AnyHashable : Any],
fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if Auth.auth().canHandleNotification(notification) {
completionHandler(.noData)
return
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("Your code here")
return true
}
}
#main
struct partidulverdeApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
init() {
FirebaseApp.configure()
}
var body: some Scene {
WindowGroup {
MainView()
.onOpenURL { url in
Auth.auth().canHandle(url.absoluteURL)
}
}
}
}
Here's how to implement Phone Number Auth using the new SwiftUI 2 life cycle:
Create a Firebase project and set up PhoneNumber Auth
Add your iOS app to the Firebase project, download and add GoogleService-Info.plist to your project
In Xcode, select the application target and enable the following capabilities:
Push notifications
Background modes > Remote notifications
Create and register an APNS authentication key on the Apple developer portal
Upload the key to Firebase (under Project Settings > Cloud messaging in the Firebase Console)
Add the Firebase project's reversed client ID to your app's URL schemes
In your Info.plist, set FirebaseAppDelegateProxyEnabled to NO
Implement the AppDelegate as follows:
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
print("SwiftUI_2_Lifecycle_PhoneNumber_AuthApp application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.")
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("\(#function)")
Auth.auth().setAPNSToken(deviceToken, type: .sandbox)
}
func application(_ application: UIApplication, didReceiveRemoteNotification notification: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("\(#function)")
if Auth.auth().canHandleNotification(notification) {
completionHandler(.noData)
return
}
}
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
print("\(#function)")
if Auth.auth().canHandle(url) {
return true
}
return false
}
}
#main
struct SwiftUI_2_Lifecycle_PhoneNumber_AuthApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
print("Received URL: \(url)")
Auth.auth().canHandle(url) // <- just for information purposes
}
}
}
}
For further reading, I suggest these two articles I wrote:
Firebase and the new SwiftUI 2 Application Life Cycle
The Ultimate Guide to the SwiftUI 2 Application Life Cycle

How to implement PKPaymentAuthorizationViewControllerDelegate in Xcode 9 for iOS 8 backwards compatibility?

I'm trying to build an app that uses Apple Pay on Xcode 9. The deployment target is iOS 8.
Up until Xcode 8.3.3 (and the iOS 10 SDK) this worked perfectly:
extension MyViewController: PKPaymentAuthorizationViewControllerDelegate {
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didSelectShippingAddress address: ABRecord, completion: #escaping (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Void) {
// handle the ABRecord for iOS 8
}
#available(iOS 9.0, *)
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didSelectShippingContact contact: PKContact, completion: #escaping (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Void) {
// handle the PKContact on iOS 9 and later
}
Now, with Xcode 9 I get the following build error:
Protocol 'PKPaymentAuthorizationViewControllerDelegate' requires 'paymentAuthorizationViewController(_:didSelectShippingContact:completion:)' to be available on iOS 8.0 and newer
Which means, that I am supposed to change the #available(iOS 9, *) to #available(iOS 8, *).
Looking into the definition of PKPaymentAuthorizationViewControllerDelegate I see the following:
#available(iOS, introduced: 8.0, deprecated: 11.0, message: "Use paymentAuthorizationViewController:didSelectShippingContact:handler: instead to provide more granular errors")
optional public func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didSelectShippingContact contact: PKContact, completion: #escaping (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Swift.Void)
So the method is defined as being available from iOS 8 on.
Here comes the catch: Looking into the definition of PKContact (which is part of the method signature of the above method) you can see the following:
#available(iOS 9.0, *)
open class PKContact : NSObject {
...
}
So, according to the current SDK, the method is available on iOS 8 (which leads to the build error) but one of its parameter types is only available on iOS 9. This seems to be mutual exclusive.
I know that the method I'm trying to implement is deprecated but the replacement is only available on iOS 11 so for now it seems that I need to implement the deprecated methods anyways (or am I mistaken here?).
Does anybody have the same issue? Any thoughts on this? I appreciate every thought :)
Thank you!
Protocol 'PKPaymentAuthorizationViewControllerDelegate' requires
'paymentAuthorizationViewController(_:didSelectShippingContact:completion:)'
to be available on iOS 8.0 and newer
doesn't mean that method require iOS 8.0 and newer. It means what that method is released in class what should support iOS 8.0. You can easily check it by putting #available(iOS 9.0, *) before class declaration
#available(iOS 9.0, *)
class MyViewController: UIViewController, PKPaymentAuthorizationViewControllerDelegate {
//...
}
error will gone
But how to deal with your issue. Create two classes what will release PKPaymentAuthorizationViewControllerDelegate protocol. One of them should release iOS 8 support and another one - iOS 9 and new.
class DeprecatedPayment: NSObject, PKPaymentAuthorizationViewControllerDelegate {
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
}
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didSelectShippingAddress address: AddressBook.ABRecord, completion: #escaping (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Swift.Void) {
}
}
#available(iOS 9.0, *)
class Payment: NSObject, PKPaymentAuthorizationViewControllerDelegate {
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
}
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didSelectShippingContact contact: PKContact, completion: #escaping (PKPaymentAuthorizationStatus, [PKShippingMethod], [PKPaymentSummaryItem]) -> Void) {
// handle the PKContact on iOS 9 and later
}
}
Now you will be able to use appropriative class according to installed iOS
if #available(iOS 9.0, *) {
//use Payment
} else {
//use DeprecatedPayment
}
Also it will be easy to drop supporting of deprecated iOS when time will came.

PFFacebookUtils.logInInBackgroundWithReadPermissions block returns user = nil and error = nil

I have an iOS 9 app (Xcode 7.1, Swift 2.0) with the following pods, using the latest versions at the time of this post:
pod 'Parse' (using latest version 1.12.0)
pod 'ParseFacebookUtilsV4' (using latest version 1.11.0)
I am attempting to run the following code:
let permissions = ["public_profile", "email"]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) {
(user, error) in
// Both user and error are nil here
}
But I always get a nil user and a nil error. The login worked a couple months ago, but all of a sudden (I believe after a pod update), the login stopped working.
I have added all the proper plist entries as described on the Facebook developers site, and I have added all the appropriate functions in the app delegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
PFFacebookUtils.initializeFacebookWithApplicationLaunchOptions(launchOptions)
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
let sourceApplication = options[UIApplicationOpenURLOptionsSourceApplicationKey] as? String
return FBSDKApplicationDelegate.sharedInstance().application(app, openURL: url, sourceApplication: sourceApplication, annotation: nil)
}
func applicationDidBecomeActive(application: UIApplication) {
FBSDKAppEvents.activateApp()
}
in AppDelegate, under didFinishLaunchingWithOptions initialize Parse before initializing PFFacebookUtils. I was making the same mistake