Hi folks,
I have trouble with reading NFC tag in background while my application is running.
I still receives a message in my iPhone 13, NFC tag of web Open "donator.cz" in application Safari.
But I would like to open the app Donator which is here: https://apps.apple.com/dk/app/don%C3%A1tor/id6473955033
My NFC chip scanned by "NFC Tools" is ISO 14443-3A (NXP-NTAG213)
In the tag is mentioned https://donator.cz/<8digits-number>**text
In my Info.plist of application is:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https://donator.cz</string>
</array>
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
<string>12FC</string>
</array>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>D2760000850101</string>
</array>
Apple CDN looks like: https://app-site-association.cdn-apple.com/a/v1/donator.cz
I have read in Apple documentation https://developer.apple.com/documentation/corenfc/adding-support-for-background-tag-reading
that application should be in progress A Core NFC reader session is in progress.
How to do it in AppDelegate or SceneDelegate.
One note is that everything works properly in case of reading NFC based on button click is pressed.
My NFCHandler looks like:
//
// NFCReader.swift
// Donator
//
// Created by Petr Hracek on 22.11.2024.
//
import Foundation
import UIKit
import CoreNFC
class NFCHandler: NSObject, NFCNDEFReaderSessionDelegate {
var readerSession: NFCNDEFReaderSession?
func startBackgroundScanning() {
guard NFCNDEFReaderSession.readingAvailable else {
return
}
readerSession = NFCNDEFReaderSession(delegate: self, queue: DispatchQueue.main, invalidateAfterFirstRead: true)
readerSession?.alertMessage = "Prilozte NFC tag"
readerSession?.begin()
}
func readTag(session: NFCNDEFReaderSession , tags: [NFCNDEFTag] ) {
if tags.count > 1 {
// Restart polling in 500ms
let retryInterval = DispatchTimeInterval.milliseconds(500)
session.alertMessage = "More than 1 tag is detected, please remove all tags and try again."
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
session.restartPolling()
})
return
}
// Connect to the found tag and perform NDEF message reading
let tag = tags.first!
session.connect(to: tag, completionHandler: { (error: Error?) in
if nil != error {
session.alertMessage = "Unable to connect to tag."
session.invalidate()
return
}
tag.queryNDEFStatus(completionHandler: { (ndefStatus: NFCNDEFStatus, capacity: Int, error: Error?) in
if .notSupported == ndefStatus {
session.alertMessage = "Tag is not NDEF compliant"
session.invalidate()
return
} else if nil != error {
session.alertMessage = "Unable to query NDEF status of tag"
session.invalidate()
return
}
tag.readNDEF(completionHandler: { (message: NFCNDEFMessage?, error: Error?) in
var statusMessage: String
if nil != error || nil == message {
statusMessage = "Fail to read NDEF from tag"
} else {
statusMessage = "Found 1 NDEF message"
DispatchQueue.main.async {
// Process detected NFCNDEFMessage objects.
debugPrint(message!)
//self.tableView.reloadData()
}
}
session.alertMessage = statusMessage
session.invalidate()
})
})
})
}
/// - Tag: sessionBecomeActive
/// This method will called when NFC session become active
/// Tells the delegate that the session detected NFC tags with NDEF messages.
func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
debugPrint("NFC session become active======")
}
func readerSessionDidInvalidate(_ session: NFCNDEFReaderSession) {
debugPrint("NFC session invalidate======")
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
print(error)
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
print("Type name format: \(record.typeNameFormat)")
print("Payload: \(record.payload)")
print("Type: \(record.type)")
print("Identifier: \(record.identifier)")
}
}
}
/// - Tag: processingNDEFTag
/// if This method is not implement then only func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) will call
/// Tells the delegate that the session detected NFC tags with NDEF messages and enables read-write capability for the session.
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
self.readTag(session: session, tags: tags)
}
}
Post
Replies
Boosts
Views
Activity
I have an application which should use Apple Pay.
As soon as user accept payment and Apple Pay works well, like Face ID or PIN,
I would like to send Apple Pay Token to PayU.
Unfortunatelly I did not find and support in PayU (https://czech.payu.com/en/)
In my ApplePayViewController I have method like:
class ApplePayViewController: {
func submitToken(payment: PKPayment, completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
let result = PKPaymentAuthorizationResult(status: .failure, errors: nil)
let jsonPayment = try? JSONSerialization.jsonObject(with: payment.token.paymentData, options: []) as? Dictionary<String, AnyObject>
if let jsonData = jsonPayment {
let jsonTokenString = String(decoding: payment.token.paymentData, as: UTF8.self)
print("Json Data String: \(jsonTokenString)")
}
// What should be sent to PayU?
// paymentData? https://developer.apple.com/documentation/passkit/pkpaymenttoken/1617000-paymentdata
// Or data from Payment Token Structure https://developer.apple.com/documentation/passkit/apple_pay/payment_token_format_reference#//apple_ref/doc/uid/TP40014929
// Data are sent to my server over API like
// https://<URL>/api/send_payment&user=<username>&token=<token>&payment_data=<ApplePayToken>
do {
send_payment(data: paymentData, completion: { apiResponse in
switch apiResponse {
case .success(let zapisStatus):
result.status = PKPaymentAuthorizationStatus.success
completion(result)
case .failure(let error):
result.status = PKPaymentAuthorizationStatus.failure
completion(result)
}
})
}
}
extension ApplePayViewController: PKPaymentAuthorizationViewControllerDelegate {
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController!, didAuthorizePayment payment: PKPayment!, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
let status = PKPaymentAuthorizationStatus(rawValue: 0)!
switch status.rawValue {
case 0:
self.paymentStatus = PKPaymentAuthorizationStatus.success
completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.success, errors: nil))
case 1:
self.paymentStatus = PKPaymentAuthorizationStatus.failure
completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.failure, errors: nil))
default:
self.paymentStatus = PKPaymentAuthorizationStatus.failure
completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.failure, errors: nil))
}
if payment.token.paymentData.count > 0 {
self.submitToken(payment: payment)
}
}
The data are send over by this code:
public func getURLRequest(endpoint: String, data: Data) -> URLRequest{
guard let url = URL(string: RestAPIHelper.RestAPIKeys.url_pub + endpoint) else {
fatalError()
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
urlRequest.httpBody = data
return urlRequest
}
func send_payment(data: Data, completion: @escaping(Result<[UserPlatbyInfo], APIError>) -> Void) {
let urlRequest = getURLRequest(endpoint: APIInterface.user_zapisplatbu.rawValue, data: data)
let dataTask = URLSession.shared.dataTask(with: urlRequest) { respData, resp, _ in
// Handle return status
dataTask.resume()
}
Basically I don't know what PayU expects in order to accept ApplePayToken.
PayU expects Apple Pay Token, but it is https://developer.apple.com/documentation/passkit/pkpaymenttoken/1617000-paymentdata or https://developer.apple.com/documentation/passkit/apple_pay/payment_token_format_reference#//apple_ref/doc/uid/TP40014929