summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Schmertmann <Lars.Schmertmann@governikus.de>2020-08-03 12:01:09 +0200
committerLars Schmertmann <Lars.Schmertmann@governikus.de>2021-04-26 21:59:48 +0200
commitfab1aef025590c4d32ee03caf92400ae8d0a3e1e (patch)
treea0e1ef400693c58fda9ce129cbf2fdab7939820d
parent16ba360f210fb481aafea03af3232321fb21a9e5 (diff)
Implement Qt NFC on iOS starting with TagTypeSpecificAccess
NdefAccess will follow later. Fixes: QTBUG-81824 Change-Id: I87bc1b2af4b6c4aedb847c38700d766a7f755479 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
-rw-r--r--src/nfc/CMakeLists.txt16
-rw-r--r--src/nfc/doc/src/nfc-index.qdoc2
-rw-r--r--src/nfc/ios/qiostagreaderdelegate.mm152
-rw-r--r--src/nfc/ios/qiostagreaderdelegate_p.h84
-rw-r--r--src/nfc/qnearfieldmanager.cpp2
-rw-r--r--src/nfc/qnearfieldmanager_ios.mm189
-rw-r--r--src/nfc/qnearfieldmanager_ios_p.h109
-rw-r--r--src/nfc/qnearfieldtarget_ios.mm234
-rw-r--r--src/nfc/qnearfieldtarget_ios_p.h104
9 files changed, 891 insertions, 1 deletions
diff --git a/src/nfc/CMakeLists.txt b/src/nfc/CMakeLists.txt
index c9d9aeed..6f851046 100644
--- a/src/nfc/CMakeLists.txt
+++ b/src/nfc/CMakeLists.txt
@@ -54,6 +54,22 @@ qt_internal_extend_target(Nfc CONDITION ANDROID AND NOT ANDROID_EMBEDDED
Qt::Gui
)
+if(IOS)
+ # special case begin
+ set(NFC_BACKEND_AVAILABLE ON)
+ qt_disable_apple_app_extension_api_only(Nfc)
+ # special case end
+endif()
+
+qt_extend_target(Nfc CONDITION IOS
+ SOURCES
+ ios/qiostagreaderdelegate.mm ios/qiostagreaderdelegate_p.h
+ qnearfieldmanager_ios.mm qnearfieldmanager_ios_p.h
+ qnearfieldtarget_ios.mm qnearfieldtarget_ios_p.h
+ DEFINES
+ IOS_NFC
+)
+
#### Keys ignored in scope 2:.:.:nfc.pro:ANDROID AND NOT ANDROID_EMBEDDED:
# NFC_BACKEND_AVAILABLE = "yes"
diff --git a/src/nfc/doc/src/nfc-index.qdoc b/src/nfc/doc/src/nfc-index.qdoc
index 25a07c4b..84ac642d 100644
--- a/src/nfc/doc/src/nfc-index.qdoc
+++ b/src/nfc/doc/src/nfc-index.qdoc
@@ -34,7 +34,7 @@
The NFC API provides connectivity between NFC enabled devices.
-Currently the API is supported on \l{Qt for Android}{Android}.
+Currently the API is supported on \l{Qt for Android}{Android} and \l{Qt for iOS}{iOS}.
\section1 Overview
diff --git a/src/nfc/ios/qiostagreaderdelegate.mm b/src/nfc/ios/qiostagreaderdelegate.mm
new file mode 100644
index 00000000..35956567
--- /dev/null
+++ b/src/nfc/ios/qiostagreaderdelegate.mm
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiostagreaderdelegate_p.h"
+
+#include "qnearfieldmanager_ios_p.h"
+
+#import <CoreNFC/NFCError.h>
+#import <CoreNFC/NFCTag.h>
+
+QT_USE_NAMESPACE
+
+@implementation QT_MANGLE_NAMESPACE(QIosTagReaderDelegate)
+
+- (instancetype)initWithListener:(QNearFieldManagerPrivateImpl *)listener
+{
+ self = [super init];
+ if (self) {
+ self.listener = listener;
+ self.sessionStoppedByApplication = false;
+ self.message = nil;
+ self.session = nil;
+ }
+
+ return self;
+}
+
+- (void)startSession
+{
+ if (self.sessionStoppedByApplication) {
+ Q_EMIT self.listener->didInvalidateWithError(true);
+ return;
+ }
+
+ if (self.session) {
+ [self.session restartPolling];
+ return;
+ }
+
+ self.session = [[NFCTagReaderSession alloc] autorelease];
+ self.session = [self.session initWithPollingOption:NFCPollingISO14443 delegate:self queue:nil];
+ if (self.session) {
+ if (self.message)
+ self.session.alertMessage = self.message;
+ [self.session beginSession];
+ } else {
+ Q_EMIT self.listener->didInvalidateWithError(true);
+ }
+}
+
+- (void)stopSession:(QString)message
+{
+ if (self.session) {
+ if (self.session.ready) {
+ if (message.isNull())
+ [self.session invalidateSession];
+ else
+ [self.session invalidateSessionWithErrorMessage:message.toNSString()];
+ self.sessionStoppedByApplication = true;
+ } else {
+ self.session = nil;
+ }
+ }
+}
+
+- (void)alertMessage:(QString)message
+{
+ if (self.session && !self.sessionStoppedByApplication)
+ self.session.alertMessage = message.toNSString();
+ else
+ self.message = message.toNSString();
+}
+
+- (void)tagReaderSessionDidBecomeActive:(NFCTagReaderSession*)session
+{
+ if (session != self.session)
+ [session invalidateSession];
+}
+
+- (void)tagReaderSession:(NFCTagReaderSession*)session didInvalidateWithError:(NSError*)error
+{
+ if (session != self.session)
+ return;
+
+ self.session = nil;
+ if (self.sessionStoppedByApplication) {
+ self.sessionStoppedByApplication = false;
+ return;
+ }
+
+ const bool doRestart =
+ !(error.code == NFCReaderError::NFCReaderSessionInvalidationErrorUserCanceled
+ || error.code == NFCReaderError::NFCReaderErrorUnsupportedFeature);
+ Q_EMIT self.listener->didInvalidateWithError(doRestart);
+}
+
+- (void)tagReaderSession:(NFCTagReaderSession*)session didDetectTags:(NSArray<__kindof id<NFCTag>>*)tags
+{
+ if (session != self.session)
+ return;
+
+ bool foundTag = false;
+ for (id<NFCTag> tag in tags) {
+ if (tag.type == NFCTagTypeISO7816Compatible) {
+ foundTag = true;
+ [tag retain];
+ Q_EMIT self.listener->tagDiscovered(tag);
+ }
+ }
+
+ if (!foundTag) {
+ [session restartPolling];
+ }
+}
+
+@end
diff --git a/src/nfc/ios/qiostagreaderdelegate_p.h b/src/nfc/ios/qiostagreaderdelegate_p.h
new file mode 100644
index 00000000..cb456643
--- /dev/null
+++ b/src/nfc/ios/qiostagreaderdelegate_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIOSTAGREADERDELEGATE_P_H
+#define QIOSTAGREADERDELEGATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QString>
+
+#import <CoreNFC/NFCReaderSession.h>
+#import <CoreNFC/NFCTagReaderSession.h>
+#import <Foundation/Foundation.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNearFieldManagerPrivateImpl;
+
+QT_END_NAMESPACE
+
+API_AVAILABLE(ios(13.0))
+@interface QT_MANGLE_NAMESPACE(QIosTagReaderDelegate)
+ : NSObject<NFCTagReaderSessionDelegate>
+
+@property QNearFieldManagerPrivateImpl *listener;
+@property bool sessionStoppedByApplication;
+@property (nonatomic, strong) NSString *message;
+@property (nonatomic, strong) NFCTagReaderSession *session;
+
+- (instancetype)initWithListener:(QNearFieldManagerPrivateImpl *)listener;
+
+- (void)startSession;
+- (void)stopSession:(QString)message;
+
+- (void)alertMessage:(QString)message;
+
+@end
+
+#endif // QIOSTAGREADERDELEGATE_P_H
diff --git a/src/nfc/qnearfieldmanager.cpp b/src/nfc/qnearfieldmanager.cpp
index 15394c1d..4796b492 100644
--- a/src/nfc/qnearfieldmanager.cpp
+++ b/src/nfc/qnearfieldmanager.cpp
@@ -44,6 +44,8 @@
#include "qnearfieldmanager_simulator_p.h"
#elif defined(ANDROID_NFC)
#include "qnearfieldmanager_android_p.h"
+#elif defined(IOS_NFC)
+#include "qnearfieldmanager_ios_p.h"
#else
#include "qnearfieldmanager_generic_p.h"
#endif
diff --git a/src/nfc/qnearfieldmanager_ios.mm b/src/nfc/qnearfieldmanager_ios.mm
new file mode 100644
index 00000000..dcae40dc
--- /dev/null
+++ b/src/nfc/qnearfieldmanager_ios.mm
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldmanager_ios_p.h"
+
+#include "ios/qiostagreaderdelegate_p.h"
+#include "qnearfieldtarget_ios_p.h"
+
+#include <QDateTime>
+
+#import <CoreNFC/NFCReaderSession.h>
+#import <CoreNFC/NFCNDEFReaderSession.h>
+#import <CoreNFC/NFCTagReaderSession.h>
+
+QT_BEGIN_NAMESPACE
+
+QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl()
+{
+ if (@available(iOS 13, *))
+ delegate = [[QT_MANGLE_NAMESPACE(QIosTagReaderDelegate) alloc] initWithListener:this];
+
+ connect(this, &QNearFieldManagerPrivateImpl::tagDiscovered,
+ this, &QNearFieldManagerPrivateImpl::onTagDiscovered,
+ Qt::QueuedConnection);
+ connect(this, &QNearFieldManagerPrivateImpl::didInvalidateWithError,
+ this, &QNearFieldManagerPrivateImpl::onDidInvalidateWithError,
+ Qt::QueuedConnection);
+}
+
+QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl()
+{
+ if (@available(iOS 13, *))
+ [delegate release];
+}
+
+bool QNearFieldManagerPrivateImpl::isSupported(QNearFieldTarget::AccessMethod accessMethod) const
+{
+ switch (accessMethod) {
+ case QNearFieldTarget::AnyAccess:
+ return NFCNDEFReaderSession.readingAvailable;
+ case QNearFieldTarget::TagTypeSpecificAccess:
+ if (@available(iOS 13, *))
+ return NFCTagReaderSession.readingAvailable;
+ Q_FALLTHROUGH();
+ case QNearFieldTarget::UnknownAccess:
+ case QNearFieldTarget::NdefAccess:
+ return false;
+ }
+}
+
+bool QNearFieldManagerPrivateImpl::startTargetDetection(QNearFieldTarget::AccessMethod accessMethod)
+{
+ if (detectionRunning)
+ return false;
+
+ switch (accessMethod) {
+ case QNearFieldTarget::UnknownAccess:
+ case QNearFieldTarget::NdefAccess:
+ case QNearFieldTarget::AnyAccess:
+ return false;
+ case QNearFieldTarget::TagTypeSpecificAccess:
+ if (@available(iOS 13, *))
+ if (NFCTagReaderSession.readingAvailable) {
+ detectionRunning = true;
+ startSession();
+ return true;
+ }
+ return false;
+ }
+}
+
+void QNearFieldManagerPrivateImpl::stopTargetDetection(const QString &errorMessage)
+{
+ if (detectionRunning) {
+ stopSession(errorMessage);
+ detectionRunning = false;
+ Q_EMIT targetDetectionStopped();
+ }
+}
+
+
+void QNearFieldManagerPrivateImpl::startSession()
+{
+ if (detectionRunning)
+ if (@available(iOS 13, *))
+ [delegate startSession];
+}
+
+void QNearFieldManagerPrivateImpl::stopSession(const QString &error)
+{
+ clearTargets();
+
+ if (@available(iOS 13, *))
+ [delegate stopSession:error];
+}
+
+void QNearFieldManagerPrivateImpl::clearTargets()
+{
+ auto i = detectedTargets.begin();
+ while (i != detectedTargets.end()) {
+ (*i)->invalidate();
+ Q_EMIT targetLost((*i)->q_ptr);
+ i = detectedTargets.erase(i);
+ }
+}
+
+
+void QNearFieldManagerPrivateImpl::setUserInformation(const QString &message)
+{
+ if (@available(iOS 13, *))
+ [delegate alertMessage:message];
+}
+
+void QNearFieldManagerPrivateImpl::onTagDiscovered(void *tag)
+{
+ QNearFieldTargetPrivateImpl *target = new QNearFieldTargetPrivateImpl(tag);
+ detectedTargets += target;
+ connect(target, &QNearFieldTargetPrivateImpl::targetLost,
+ this, &QNearFieldManagerPrivateImpl::onTargetLost);
+ Q_EMIT targetDetected(new NearFieldTarget(target, this));
+}
+
+void QNearFieldManagerPrivateImpl::onTargetLost(QNearFieldTargetPrivateImpl *target)
+{
+ detectedTargets.removeOne(target);
+ Q_EMIT targetLost(target->q_ptr);
+
+ if (detectionRunning && detectedTargets.isEmpty())
+ onDidInvalidateWithError(true);
+}
+
+void QNearFieldManagerPrivateImpl::onDidInvalidateWithError(bool doRestart)
+{
+ clearTargets();
+
+ if (detectionRunning && doRestart)
+ {
+ if (!isRestarting) {
+ isRestarting = true;
+ using namespace std::chrono_literals;
+ QTimer::singleShot(2s, this, [this](){
+ isRestarting = false;
+ startSession();
+ });
+ }
+ return;
+ }
+
+ detectionRunning = false;
+ Q_EMIT targetDetectionStopped();
+}
+
+QT_END_NAMESPACE
diff --git a/src/nfc/qnearfieldmanager_ios_p.h b/src/nfc/qnearfieldmanager_ios_p.h
new file mode 100644
index 00000000..e5d995d5
--- /dev/null
+++ b/src/nfc/qnearfieldmanager_ios_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNEARFIELDMANAGER_IOS_P_H
+#define QNEARFIELDMANAGER_IOS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnearfieldmanager_p.h"
+
+#include <QTimer>
+
+#import <os/availability.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QIosTagReaderDelegate));
+
+QT_BEGIN_NAMESPACE
+
+class QNearFieldTargetPrivateImpl;
+
+class QNearFieldManagerPrivateImpl : public QNearFieldManagerPrivate
+{
+ Q_OBJECT
+
+public:
+ QNearFieldManagerPrivateImpl();
+ ~QNearFieldManagerPrivateImpl();
+
+ bool isEnabled() const override
+ {
+ return true;
+ }
+
+ bool isSupported(QNearFieldTarget::AccessMethod accessMethod) const override;
+
+ bool startTargetDetection(QNearFieldTarget::AccessMethod accessMethod) override;
+ void stopTargetDetection(const QString &errorMessage) override;
+
+ void setUserInformation(const QString &message) override;
+
+Q_SIGNALS:
+ void tagDiscovered(void *tag);
+ void didInvalidateWithError(bool doRestart);
+
+private:
+ QT_MANGLE_NAMESPACE(QIosTagReaderDelegate) *delegate API_AVAILABLE(ios(13.0)) = nullptr;
+ bool detectionRunning = false;
+ bool isRestarting = false;
+ QList<QNearFieldTargetPrivateImpl *> detectedTargets;
+
+ void startSession();
+ void stopSession(const QString &error);
+ void clearTargets();
+
+private Q_SLOTS:
+ void onTagDiscovered(void *target);
+ void onTargetLost(QNearFieldTargetPrivateImpl *target);
+ void onDidInvalidateWithError(bool doRestart);
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QNEARFIELDMANAGER_IOS_P_H
diff --git a/src/nfc/qnearfieldtarget_ios.mm b/src/nfc/qnearfieldtarget_ios.mm
new file mode 100644
index 00000000..5b280389
--- /dev/null
+++ b/src/nfc/qnearfieldtarget_ios.mm
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldtarget_ios_p.h"
+
+#import <CoreNFC/NFCReaderSession.h>
+#import <CoreNFC/NFCTagReaderSession.h>
+#import <CoreNFC/NFCISO7816Tag.h>
+#import <CoreNFC/NFCTag.h>
+
+QT_BEGIN_NAMESPACE
+
+void NfcTagDeleter::operator()(void *tag)
+{
+ [static_cast<id<NFCTag>>(tag) release];
+}
+
+QNearFieldTargetPrivateImpl:: QNearFieldTargetPrivateImpl(void *tag, QObject *parent) :
+ QNearFieldTargetPrivate(parent),
+ nfcTag(tag)
+{
+ Q_ASSERT(nfcTag);
+
+ QObject::connect(&targetCheckTimer, &QTimer::timeout, this, &QNearFieldTargetPrivateImpl::onTargetCheck);
+ targetCheckTimer.start(500);
+}
+
+QNearFieldTargetPrivateImpl::~QNearFieldTargetPrivateImpl()
+{
+}
+
+void QNearFieldTargetPrivateImpl::invalidate()
+{
+ queue.clear();
+ nfcTag.reset();
+ targetCheckTimer.stop();
+}
+
+QByteArray QNearFieldTargetPrivateImpl::uid() const
+{
+ if (!nfcTag)
+ return QByteArray();
+
+ if (@available(iOS 13, *)) {
+ id<NFCTag> tag = static_cast<id<NFCTag>>(nfcTag.get());
+ id<NFCISO7816Tag> iso7816Tag = tag.asNFCISO7816Tag;
+ if (iso7816Tag)
+ return QByteArray::fromNSData(iso7816Tag.identifier);
+ }
+
+ return QByteArray();
+}
+
+QNearFieldTarget::Type QNearFieldTargetPrivateImpl::type() const
+{
+ if (!nfcTag)
+ return QNearFieldTarget::ProprietaryTag;
+
+ if (@available(iOS 13, *)) {
+ id<NFCTag> tag = static_cast<id<NFCTag>>(nfcTag.get());
+ id<NFCISO7816Tag> iso7816Tag = tag.asNFCISO7816Tag;
+
+ if (tag.type != NFCTagTypeISO7816Compatible || iso7816Tag == nil)
+ return QNearFieldTarget::ProprietaryTag;
+
+ if (iso7816Tag.historicalBytes != nil && iso7816Tag.applicationData == nil)
+ return QNearFieldTarget::NfcTagType4A;
+
+ if (iso7816Tag.historicalBytes == nil && iso7816Tag.applicationData != nil)
+ return QNearFieldTarget::NfcTagType4B;
+
+ return QNearFieldTarget::NfcTagType4;
+ }
+
+ return QNearFieldTarget::ProprietaryTag;
+}
+
+QNearFieldTarget::AccessMethods QNearFieldTargetPrivateImpl::accessMethods() const
+{
+ if (@available(iOS 13, *)) {
+ id<NFCTag> tag = static_cast<id<NFCTag>>(nfcTag.get());
+ if (tag && [tag conformsToProtocol:@protocol(NFCISO7816Tag)])
+ return QNearFieldTarget::TagTypeSpecificAccess;
+ }
+
+ return QNearFieldTarget::UnknownAccess;
+}
+
+int QNearFieldTargetPrivateImpl::maxCommandLength() const
+{
+ if (accessMethods() & QNearFieldTarget::TagTypeSpecificAccess)
+ return 0xFEFF;
+
+ return 0;
+}
+
+QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::sendCommand(const QByteArray &command)
+{
+ QNearFieldTarget::RequestId requestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate());
+
+ if (!(accessMethods() & QNearFieldTarget::TagTypeSpecificAccess)) {
+ reportError(QNearFieldTarget::UnsupportedError, requestId);
+ return requestId;
+ }
+
+ queue.enqueue(std::pair(requestId, command));
+
+ if (!connect()) {
+ reportError(QNearFieldTarget::TargetOutOfRangeError, requestId);
+ return requestId;
+ }
+
+ onExecuteRequest();
+ return requestId;
+}
+
+bool QNearFieldTargetPrivateImpl::isAvailable() const
+{
+ if (@available(iOS 13, *)) {
+ id<NFCTag> tag = static_cast<id<NFCTag>>(nfcTag.get());
+ return tag && (!connected || tag.available);
+ }
+
+ return false;
+}
+
+bool QNearFieldTargetPrivateImpl::connect()
+{
+ if (connected || requestInProgress)
+ return true;
+
+ if (!isAvailable())
+ return false;
+
+ if (@available(iOS 13, *)) {
+ requestInProgress = true;
+ id<NFCTag> tag = static_cast<id<NFCTag>>(nfcTag.get());
+ NFCTagReaderSession* session = tag.session;
+ [session connectToTag: tag completionHandler: ^(NSError* error){
+ const bool success = error == nil;
+ QMetaObject::invokeMethod(this, [this, success] {
+ requestInProgress = false;
+ if (success) {
+ connected = true;
+ onExecuteRequest();
+ } else {
+ invalidate();
+ Q_EMIT targetLost(this);
+ reportError(QNearFieldTarget::ConnectionError, queue.head().first);
+ }
+ });
+ }];
+ return true;
+ }
+
+ return false;
+}
+
+void QNearFieldTargetPrivateImpl::onTargetCheck()
+{
+ if (!isAvailable()) {
+ invalidate();
+ Q_EMIT targetLost(this);
+ }
+}
+
+void QNearFieldTargetPrivateImpl::onExecuteRequest()
+{
+ if (!nfcTag || requestInProgress || queue.isEmpty())
+ return;
+
+ if (@available(iOS 13, *)) {
+ requestInProgress = true;
+ const auto request = queue.dequeue();
+ const auto tag = static_cast<id<NFCISO7816Tag>>(nfcTag.get());
+ auto* apdu = [[NFCISO7816APDU alloc] initWithData: request.second.toNSData()];
+ [tag sendCommandAPDU: apdu completionHandler: ^(NSData* responseData, uint8_t sw1, uint8_t sw2, NSError* error){
+ QByteArray recvBuffer = QByteArray::fromNSData(responseData);
+ recvBuffer += static_cast<char>(sw1);
+ recvBuffer += static_cast<char>(sw2);
+ const bool success = error == nil;
+ const auto requestId = request.first;
+ QMetaObject::invokeMethod(this, [this, success, requestId, recvBuffer] {
+ requestInProgress = false;
+ if (success) {
+ setResponseForRequest(requestId, recvBuffer, true);
+ onExecuteRequest();
+ } else {
+ invalidate();
+ Q_EMIT targetLost(this);
+ reportError(QNearFieldTarget::CommandError, requestId);
+ }
+ });
+ }];
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/nfc/qnearfieldtarget_ios_p.h b/src/nfc/qnearfieldtarget_ios_p.h
new file mode 100644
index 00000000..93c7c098
--- /dev/null
+++ b/src/nfc/qnearfieldtarget_ios_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Governikus GmbH & Co. KG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNEARFIELDTARGET_IOS_P_H
+#define QNEARFIELDTARGET_IOS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnearfieldtarget_p.h"
+
+#include <QQueue>
+#include <QTimer>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+struct NfcTagDeleter
+{
+ void operator()(void *tag);
+};
+
+class QNearFieldTargetPrivateImpl : public QNearFieldTargetPrivate
+{
+ Q_OBJECT
+
+public:
+ QNearFieldTargetPrivateImpl(void *tag, QObject *parent = nullptr);
+ ~QNearFieldTargetPrivateImpl() override;
+ void invalidate();
+
+ QByteArray uid() const override;
+ QNearFieldTarget::Type type() const override;
+ QNearFieldTarget::AccessMethods accessMethods() const override;
+
+ int maxCommandLength() const override;
+ QNearFieldTarget::RequestId sendCommand(const QByteArray &command) override;
+
+ bool isAvailable() const;
+
+Q_SIGNALS:
+ void targetLost(QNearFieldTargetPrivateImpl *target);
+
+private:
+ std::unique_ptr<void, NfcTagDeleter> nfcTag;
+ bool connected = false;
+ QTimer targetCheckTimer;
+ bool requestInProgress = false;
+ QQueue<std::pair<QNearFieldTarget::RequestId, QByteArray>> queue;
+
+ bool connect();
+
+private Q_SLOTS:
+ void onTargetCheck();
+ void onExecuteRequest();
+};
+
+QT_END_NAMESPACE
+
+#endif // QNEARFIELDTARGET_IOS_P_H