Our application has had ApplePay implemented for a number of years. Just recently I hit the button to trigger it to only find out the pay sheet from PKPaymentAuthorizationViewController doesn't appear. It won't slide up in a sandbox (i.e. simulator or device connected to Xcode) environment, but putting a breakpoint shows that it is being created successfully. It's been awhile since I've tested this, but I am suspecting something change with Xcode11 or iOS 13.
My code is pretty standard Apple Pay, but posted below.
let item = PKPaymentSummaryItem()
item.label = "Our Label"
let price = NSDecimalNumber(mantissa: UInt64(totalPrice), exponent: -2, isNegative: false)
item.amount = price
items.append(item)
request.paymentSummaryItems = items
if let applePayController = PKPaymentAuthorizationViewController(paymentRequest: request) {
applePayController.delegate = self
present(applePayController, animated: true)
}
This will happen if the merchantID used by your PKPaymentRequest is not listed in mobile.entitlements. Or if the MerchantID is not enabled by your app's provisioning profile.
View Controller
PKPaymentRequest *request = [[PKPaymentRequest alloc] init];
request.merchantIdentifier = "com.your.app.merchandId"
mobile.entitlements
<dict>
<key>com.apple.developer.in-app-payments</key>
<array>
<string>com.your.app.merchandId</string>
</array>
</dict>
I was facing the same issue, where Apple Pay suddenly stopped working in our app. We've also been supporting this for at least halve a year now.
Using the latest Xcode (Xcode 11.2, Beta 2 (11B44)), it seems to work again.
I'm guessing it was a Xcode bug then. Even though it's not listed in Xcode's release notes for Xcode 11.1 or Xcode 11.2 beta 2.
I have pasted your snippet into my app, which uses ApplePay and I have run it on a test device which is sandboxed and has ApplePay enabled and the ApplePay button didn't work.
What I found it quite surprising.
When you look at the docs for PKPaymentSummaryItem it has only two initialisers:
init(label: String, amount: NSDecimalNumber)
init(label: String, amount: NSDecimalNumber, type:PKPaymentSummaryItemType)
And neither has default values, but you are initialising one with an empty initialiser. Surprisingly there are no errors or warnings and it even comes up in code completion.
So I changed the initialiser:
let item = PKPaymentSummaryItem(label: "Our Label",
amount: NSDecimalNumber(mantissa: UInt64(totalPrice),
exponent: -2,
isNegative: false))
request.paymentSummaryItems = [items]
if let applePayController = PKPaymentAuthorizationViewController(paymentRequest: request) {
applePayController.delegate = self
present(applePayController, animated: true)
}
And it works (on device only - not simulator)!
That made my wonder so I just changed your item from let to var and it works as well. I would still go with the documented initialiser.
Related
My app keeps getting rejected by the App Review team due to the fact that the Apple Pay button in my app is unresponsive on their device when they tap it. The Apple Pay sheet shows up perfectly fine on my device but never completes the payment (error says my debit card number is invalid, my card works perfectly fine everywhere else, not sure how to fix this, but not the main issue here).
I am not worried about paying with Apple Pay on my app simply because I will not be the one using my app but I do want to ensure it works for everybody else since Apple is bringing it up as a reoccurring issue.
Here is my function for presenting the Apple Pay sheet:
{
guard let cost = actualCostOfEvent.text else { return }
guard let name = nameOfEvent else { return }
let text = cost
let textWithout$ = text.replacingOccurrences(of: "$", with: "")
guard let finalCost = Double(textWithout$) else { return }
let fee = finalCost / Double(12.0)
let roundedFee = floor(fee * 100) / 100.0
let total = finalCost + roundedFee
let paymentRequest = StripeAPI.paymentRequest(withMerchantIdentifier: "merchant.xxxxx.xxxxx.xxxxx",
country: "CA",
currency: "CAD")
paymentRequest.supportedNetworks = [.visa, .masterCard, .interac, .amex, .discover]
paymentRequest.paymentSummaryItems = [
PKPaymentSummaryItem(label: name , amount: NSDecimalNumber(value: finalCost)),
PKPaymentSummaryItem(label: "Service Fee", amount: NSDecimalNumber(value: roundedFee)),
PKPaymentSummaryItem(label: "XXXXXX", amount: NSDecimalNumber(value: total))
]
guard let applePayController = STPApplePayContext(paymentRequest: paymentRequest, delegate: self) else { return }
if StripeAPI.canSubmitPaymentRequest(paymentRequest) {
applePayController.presentApplePay {
self.startApplePayCheckout()
}
}
}
I can't seem to figure out why the Apple Pay sheet doesn't show up for the App reviewer, any suggestions or things needed to be changed? If not, I will just have to submit an appeal.
I have reproduced your code, and it works perfectly fine; the ApplePay sheet is displayed for me. You may want to confirm with Apple what iOS version they are testing and submit an appeal.
A few notes just in case:
You don’t need to check if StripeAPI.canSubmitPaymentRequest(paymentRequest). When you initialized STPApplePayContext it automatically did the check for you: https://github.com/stripe/stripe-ios/blob/master/Stripe/STPApplePayContext.swift#L87
If you are using a test key, you can complete the payment as long as your real card information is correct. You won’t be charged while using a test key.
I'm trying to implement Apple Pay payment throught Stripe and PassKit. I've done the following to create an STPAppleyPayContext, and use its presentApplePay(on:completion:) method to display the Apple Pay payment sheet.
let merchantId = "A_VALID_MERCHANT_ID_HERE"
let currency = "usd"
let country = "US"
let paymentReq = Stripe.paymentRequest(withMerchantIdentifier: merchantId, country: country, currency: currency)
// Configure the line items on the payment request
let charge = NSDecimalNumber(value: 1000)
let summaryItems = [PKPaymentSummaryItem(label: "Service", amount: charge)]
paymentReq.paymentSummaryItems = summaryItems
if let applePayContext = STPApplePayContext(paymentRequest: paymentReq, delegate: self) {
print("--- presenting apple pay sheet")
applePayContext.presentApplePay(on: self, completion: nil)
} else {
print("--- failed to create STPApplePayContext, may be a problem with the Apple Pay config")
}
In the logs, "--- presenting apple pay sheet" was printed, so I believe applePayContext.presentApplePay(on:completion:) was successfully called. However, no sheet was presented, and I can't find any error logs.
The exact same code worked when we build the App on iOS 13, but it doesn't work on iOS 14. I am wondering if it is related some update to the Apple Pay system in iOS 14, or something else went wrong.
I'm using stripe apple pay for payment. I have integrated stripe apple pay and it is working fine with test stripe key. But when I use live stripe key it shows an error:
Apple pay not completed "total" was not able to complete the payment.
I'm following stripe doc and this blog to integrate apple pay. Done with these steps:
1: Register for an Apple Merchant ID
2: Create a new Apple Pay certificate
3: Integrate with Xcode
When click on apple pay button, this code will run:
let paymentNetworks:[PKPaymentNetwork] = [.amex, .discover, .masterCard, .visa]
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: paymentNetworks) {
let request = PKPaymentRequest()
request.merchantIdentifier = constants.merchantIdentifier
//request.supportedCountries = ["GB"]
request.countryCode = "GB"
request.currencyCode = "GBP"
request.supportedNetworks = paymentNetworks
//request.requiredShippingContactFields = [.name, .postalAddress]
// This is based on using Stripe
request.merchantCapabilities = .capability3DS
let total = PKPaymentSummaryItem(label: "Total", amount: NSDecimalNumber(decimal: Decimal(floatLiteral: Double(totalWithDeliveryCharges))), type: .final)
request.paymentSummaryItems = [total]
let authorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: request)
if let viewController = authorizationViewController {
viewController.delegate = self
present(viewController, animated: true, completion: nil)
}
}
else {
self.showAlert("Alert", "Device does not support apple pay.")
}
My output is :-
Apple pay not completed "total" was not able to complete the payment.
But actual output is
Processing complete
Done.
I forget to refresh apple pay merchant id in XCode after stripe apple pay certificate added.
Follow the image and just click on refresh merchant id button to get rid of this error.
Make sure you have added stripe apple pay certificate by following this step.
In order to open instagram app with certain post I'm using following code:
func instaOpen(_ postId: String, _ postUrl: String){
let appURL = URL(string: "instagram://media?id=\(postId)")!
if UIApplication.shared.canOpenURL(appURL) {
UIApplication.shared.open(appURL, options: [:], completionHandler: nil)
} else {
// if Instagram app is not installed, open URL inside Safari
let webURL = URL(string: postUrl)!
let svc = SFSafariViewController(url: webURL)
present(svc, animated: true, completion: nil)
}
}
When instaOpen function called – instagram app opens, but login prompt forcefully pops over. Not matter what you do - close it or proceed with login, the queried post simply won't open(see gif).
This started happening recently, after I've updated my app and pushed deployment target to iOS12.
I do have instagram listed in my LSApplicationQueriesSchemes as well as I'm 100% positive that correct mediaID is being passed to instaOpen func (the code worked previously).
Let me know if there's any suggestions on how to fix this and actually open instagram post in instagram app.
Updated - Facebook developer fixed the issue.
Its instagram bug you can follow its progress from https://developers.facebook.com/support/bugs/290173615155052/?disable_redirect=0
Probably a bug, as that feature works on Android.
i manage to "fix" the problem on a pwa app using the Instagram web app.
let appURL = URL(string: "https://www.instagram.com/p/\(postId)")!
//https://www.instagram.com/p/insert here media id
I am writing a test framework for an ios app that requires importing image from photos/gallery app for validation. I am using XCTest Framework for testing. I have looked over the Internet for some resources but couldn't find any. Can anyone help me how to approach the problem. Again, I have pick the image not from inside the app but from image but from Photos library.
Yes
You can access the photo library but it require XCUITest and the recorder doesn't work inside of Apple's UIRemoteView's like the photo picker. What you have to do is get to the photo picker inside an XCUITest, set a breakpoint, then inspect the view hierarchy to find the elements that are able to be navigated with XCUITest. The example below works with the pictures that come with the simulator.
let app = XCUIApplication()
// get to the photo library
// set a breakpoint, po [[UIWindow keyWindow] recursiveDescription]
let tablesQuery = app.tables
app.sheets.buttons["Choose From Library"].tap()
app.cells["Camera Roll"].tap()
app.cells["Photo, Landscape, March 12, 2011, 7:17 PM"].tap()
let photosApp = XCUIApplication(bundleIdentifier: "com.apple.mobileslideshow")
photosApp.launch()
let continueButton = photosApp.buttons["Continue"]
if continueButton.waitForExistence(timeout: 2) {
continueButton.tap()
}
photosApp.collectionViews["PhotosGridView"].cells.firstMatch.tap()
This works reliably for me, derived from Steve Moser's helpful answer:
/*
LIKE: The left hand expression equals the right-hand expression: ? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters.
Resource: https://nshipster.com/nspredicate/
*/
let yellowLeafGreenBackground = NSPredicate(format: "label LIKE 'Photo, October 09, 2009, *:09*'")
let waterfallCloseUpOverRocks = NSPredicate(format: "label LIKE 'Photo, August 08, 2012, *:29*'")
let treeWithWaterfallBackground = NSPredicate(format: "label LIKE 'Photo, August 08, 2012, *:52*'")
let yellowFlowerSucculentBeach = NSPredicate(format: "label LIKE 'Photo, March 12, 2011, *:17*'")
let amazingWaterfall = NSPredicate(format: "label LIKE 'Photo, August 08, 2012, *:55*'")
// take note with this one, it is an HDR image
let beautyFlowers = NSPredicate(format: "label LIKE 'Photo, March 30, 2018, *:14*'")
func testTappingOnSpecificImage() {
// ... test setup and navigation to get to presented Camera Roll (PHPicker or UIImagePickerController)
// trigger presentation of camera roll
app.buttons["Choose from Photos"].tap()
XCTAssertTrue(app.buttons["Cancel"].waitForExistence(timeout: 3))
let activeQuery = treeWithWaterfallBackground
app.scrollViews.otherElements.images.matching(activeQuery).element.tap()
}
You can see the dates for the individual photos by going into the Photos app on the simulator and tapping into the detail view for a specific image. Like Mikkel Selsøe pointed out, the timestamps are localized for the current time zone.
Available in a gist here: https://gist.github.com/bitops/9182b5ee96c682aba57d9fa16ca6b987
XCTest provides special method just for that case.
let galleryAccessMonitor = addUIInterruptionMonitor(withDescription: "Intercept Gallery Access") { alert -> Bool in
alert.buttons.element(boundBy: 1).tap() /// tap accept
return true /// mark as handled
}
see https://useyourloaf.com/blog/handling-system-alerts-in-ui-tests/
Xcode 14.1 with iOS 16.1 simulator solution:
let app = XCUIApplication()
app.scrollViews.otherElements.images.containing(NSPredicate(format: "label BEGINSWITH 'Photo'")).element(boundBy: 0).tap()
Here's my solution that is independent on the photos being added to the library:
let app = XCUIApplication()
app.launch()
app.buttons["add.photo.button"].tap()
let photosNavBar = app.navigationBars["Photos"]
if photosNavBar.waitForExistence(timeout: 2) {
XCTAssert(app.navigationBars["Photos"].exists)
} else {
XCTFail()
}
If you do not care which image you want to choose then this worked for me:
let image = app.scrollViews.images.matching(NSPredicate(format: "label LIKE '*2012*'")).firstMatch
if image.waitForExistence(timeout: 5) {
image.tap()
}
Note that similar answers which use full name of one of the default photos tend to fail in many cases because for example device/simulator language can be different than english and then image names do not start with "Photo" word, also according to other answers it seems that the time in those names can also be different so that is why I used only year in above code.
You can't interact with an app outside your own app using XCTest. The tests have a reference to your app's bundle identifier, and that is the only app they are able to interact with.
XCTest requires a certain amount of access to the internals of your app in order to give you information about it to allow you to interact with it through XCTest, and this is not something that is available to access in apps that you did not make yourself.