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

Viewed 919

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!

1 Answers

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.

Related