Unable to compile Siri extension in Swift 2.3 - swift

I want to integrate the Siri/Intent extension in my App, currently based on Swift 2.3. I am using the Xcode 8 beta 6 build.
The app fails to even compile, giving out errors as
Type 'IntentHandler' does not conform to protocol 'INSendMessageIntentHandling'
Protocol requires function 'handle(sendMessage:completion:)' with type '(sendMessage: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) -> Void'
Objective-C method 'handleWithSendMessage:completion:' provided by method 'handle(sendMessage:completion:)' does not match the requirement's selector ('handleSendMessage:completion:')
Even the sample app from Apple, UnicornChat fails to compile with the same errors. I did change the SWIFT_VERSION flag to Yes, to support Swift 2.3
Sample Code:
func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
let response = INSendMessageIntentResponse(code: .Success, userActivity: userActivity)
completion(response)
}
This fails with the error above.
Anyone has an idea on how to make Siri work in Swift 2.3?

So, this helped
https://forums.developer.apple.com/message/174455#174455
We need to explicitly define the #objc method signatures with the Swift ones. As in the following works.
#objc (handleSendMessage:completion:)
func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
//..
}

Apparently Xcode 8 beta 6 has been updated to Swift 3 and the call back needs #escaping added to the method:
func handle(sendMessage intent: INSendMessageIntent, completion: #escaping (INSendMessageIntentResponse) -> Swift.Void) {
//..
}
If the Siri delegate methods have been updated to Swift 3 (as appears to be the case) then I am afraid you will have to use Swift 3 as well, or an older version of the SDK.

While compiling your code with Swift 2.3, you need to set Use Legacy Swift Language Version to YES for Siri and SiriUI targets.
Also, when IntentHandler and IntentViewController are created, methods might have been created in Swift 3 (as happened to me). So convert the classes to Swift 2.3, and use #objc method signatures along with the delegates.
For IntentHandler,
#objc (handleSendMessage:completion:)
func handle(sendMessage intent: INSendMessageIntent, completion: (INSendMessageIntentResponse) -> Void) {
}
#objc (handleSearchForMessages:completion:)
func handle(searchForMessages intent: INSearchForMessagesIntent, completion: (INSearchForMessagesIntentResponse) -> Void) {
}
#objc (handleSetMessageAttribute:completion:)
func handle(setMessageAttribute intent: INSetMessageAttributeIntent, completion: (INSetMessageAttributeIntentResponse) -> Void) {
}
For IntentViewController,
#objc (configureWithInteraction:context:completion:)
func configureWithInteraction(interaction: INInteraction!, context: INUIHostedViewContext, completion: ((CGSize) -> Void)!) {
}
Hope this helped.

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
}
}
}

Urban Airship receivedBackgroundNotification never called

After reading all the guides, and after checking hundred of articles on the internet, I'm quite sure that the method receivedBackgroundNotification is never called.
Everything works perfect, but when the app is in background, a notification is shown and this method never is called. Seems to be impossible to get it working.
Assuming all the normal operations and the basic configuration is well done and is working, what can I do to intercept and manage background push notifications with this library?
I will appreciate a lot any help.
Make sure you have the following configured:
Remote notifications background mode enabled in the target's capabilities
Background app refreshed is enabled on your test device
Assuming you are trying to use the UAPushNotificationDelegate, make sure you either have automatic setup enabled or you are forwarding all the proper methods to UA SDK.
Apple will only wake up your application if you send the push notification with content-available=1 in the payload. The option is exposed in the composer as "background processing" or you can set it in the iOS overrides when using the push api.
In Urban Airship iOS SDK v.13.4.0 if func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping (UIBackgroundFetchResult) -> Void) is not called you can handle notifications in func receivedNotificationResponse(_ notificationResponse: UANotificationResponse, completionHandler: #escaping () -> Void) if you display notifications as alerts. Remember that you should only handle the notifications with actionIdentifier == UNNotificationDefaultActionIdentifier (the user opened the app from the notification interface).
This is an example of UAPushNotificationDelegate implementation:
extension MyPushNotificationDelegate: UAPushNotificationDelegate {
public func receivedForegroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping () -> Void) {
completionHandler()
}
public func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(.newData)
}
public func receivedNotificationResponse(_ notificationResponse: UANotificationResponse, completionHandler: #escaping () -> Void) {
guard notificationResponse.actionIdentifier == UNNotificationDefaultActionIdentifier else {
completionHandler()
return
}
someFunc() {
completionHandler()
}
}
public func extend(_ options: UNNotificationPresentationOptions = [], notification: UNNotification) -> UNNotificationPresentationOptions {
[.alert, .badge, .sound]
}
}

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.

Error: Generic parameter 'T' could not be inferred.

I have a problem with calling this method:
func setUpFeedbackForm<T:UIViewController>(viewController: T,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ())
where T:FeedbackFormDelegate { ... }
inside this wrapper function:
public class func setUpFeedbackFormWrapper(viewController: UIViewController,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ()) {
setUpFeedbackForm(viewController: viewController,
viewForScreenshot: viewForScreenshot,
completionHandler: completionHandler)
}
I am getting error: Generic parameter 'T' could not be inferred. I do understand what the error means, but I don't know how to implement this call correctly. Also the reason why I have this wrapper is that I want to expose func setUpFeedbackForm to obj-c code and I can't import it directly to obj-c because of swift's generics.
Could you please show me the correct way of calling it?
You have two constraints on the viewController parameter that need to be satisfied when calling setUpFeedbackForm:
inheritance from UIViewController
conformance to FeedbackFormDelegate
, and setUpFeedbackFormWrapper satisfies only one, thus the compiler doesn't know what to do with the other one.
The problem is caused by a limitation of Swift which can't directly express variables/parameters that satisfy both class inheritance and protocol conformance, unless using generics, which breaks the Objective-C compatibility.
Thus a valid UIViewController<FeedbackFormDelegate> construct in Objective-C doesn't have a direct equivalent in Swift.
A workaround to this limitation is to declare a 3rd method that exposes the class inheritance and protocol conformance arguments as two distinct parameters, and call that method from both the Objective-C compatible and the Swift-only versions.
func setUpFeedbackForm<T:UIViewController>(viewController: T,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ())
where T:FeedbackFormDelegate {
setupFeedbackFormImpl(viewController: viewController,
feedbackFormDelegate: viewController,
viewForScreenshot: viewForScreenshot, completionHandler: completionHandler)
}
func setupFeedbackFormImpl(viewController: UIViewController,
feedbackFormDelegate: FeedbackFormDelegate,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ()) {
// actual code here
}
public func setUpFeedbackFormWrapper(viewController: UIViewController,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ()) {
guard let feedbackFormDelegate = viewController as? FeedbackFormDelegate else {
// you can also report errors here, if you want to
// forbid runtime calls with controllers that are not FeedbackFormDelegate
return
}
setupFeedbackFormImpl(viewController: viewController,
feedbackFormDelegate: feedbackFormDelegate,
viewForScreenshot: viewForScreenshot,
completionHandler: completionHandler)
}
If we think in terms of SOLID programming, then this workaround follows the Interface Segregation Principle, as we receive one argument for the view controller stuff, and another one for the delegate stuff, even if they point to the same object behind.

Swift 3: fix issue Type "className" does not conform to protocol 'UIDocumentPickerDelegate'

I'm migrating my code from Swift 2 to Swift 3 but my code throws this error: Type "className" does not conform to protocol 'UIDocumentPickerDelegate'. I've migrate many parts of my document as you can see but error stills,
extension className: UIDocumentMenuDelegate {
//Opening the Document Menu
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController) {
documentPicker.delegate = self
print ("documentMenu")
self.present(documentPicker, animated: true, completion: nil)
}
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController) {
}
}
//Using UIDocumentPickerDelegate, error persists here.
extension className: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
if(controller.documentPickerMode == UIDocumentPickerMode.import){
print ("success")
}
}
}
class className: BaseViewController{
...Tons of code here...
#IBAction func importKeyButtonPressed(_ sender: UIButton) {
let importMenu = UIDocumentMenuViewController(documentTypes: ["public.data","public.text","public.content"], in: UIDocumentPickerMode.import)
var documentPicker = UIDocumentPickerViewController(documentTypes: ["public.txt"], in: UIDocumentPickerMode.import)
documentPicker.delegate = self
documentPicker.modalPresentationStyle = UIModalPresentationStyle.fullScreen
self.present(documentPicker, animated: true, completion: nil)
}
How can I solve it?, I've use all methods that procotol requires. Thanks
I revised your information on this link that has a similar problem.
Cannot conform to STPAddCardViewControllerDelegate since Xcode 8 GM on Swift 3
The problem is the swift compiler than canĀ“t recognize automatically some information. So in this case:
didPickDocumentAt url: URL
Then i follow the problem to this link:
https://bugs.swift.org/browse/SR-2596
In this link the information is that Swift 3 mistake data types, so i research some more and get to this page. The mistaken type in this case is "URL".
Then the solution is in the same page. I write it bellow:
weak var delegate : UIDocumentPickerDelegate?
#available(iOS 8.0, *)
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: Foundation.URL ){
//
print("something")
}
To that matter i reproduce the error in my computer, it is caused when diferent imported libraries has the same definition for data types, in this case URL type, which Swift 3 doesnt recognize automatically neither tells correctly the error. So it has to be defined directly.