Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qiosnfcndefsessiondelegate.mm
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include "qndefmessage.h"
8
9#include <QtCore/qscopeguard.h>
10#include <QtCore/qbytearray.h>
11#include <QtCore/qstring.h>
12#include <QtCore/qdebug.h>
13
14#include <memory>
15
16QT_BEGIN_NAMESPACE
17
18dispatch_queue_t qt_Nfc_Queue()
19{
20 static dispatch_queue_t nfcQueue = []{
21 auto queue = dispatch_queue_create("qt-NFC-queue", DISPATCH_QUEUE_SERIAL);
22 if (!queue)
23 qCWarning(QT_IOS_NFC, "Failed to create the QtNfc's dispatch queue");
24 return queue;
25 }();
26 static const auto queueGuard = qScopeGuard([]{
27 if (nfcQueue)
28 dispatch_release(nfcQueue);
29 });
30 return nfcQueue;
31}
32
33QT_END_NAMESPACE
34
35QT_USE_NAMESPACE
36
37@implementation QIosNfcNdefSessionDelegate
38{
39 std::unique_ptr<QNfcNdefNotifier> notifier;
40 QString alertMessage;
41 NFCNDEFStatus tagStatus;
42 NSUInteger capacity;
43 QNearFieldTarget::RequestId requestId;
44}
45
46-(instancetype)initWithNotifier:(QNfcNdefNotifier *)aNotifier
47{
48 Q_ASSERT(aNotifier);
49
50 if (self = [super init]) {
51 auto queue = qt_Nfc_Queue();
52 if (!queue)
53 return self;
54
55 tagStatus = NFCNDEFStatusNotSupported;
56 capacity = 0;
57 notifier.reset(aNotifier);
58 }
59
60 return self;
61}
62
63-(void)dealloc
64{
65 [self abort];
66 [super dealloc];
67}
68
69-(QNfcNdefNotifier *)ndefNotifier
70{
71 return notifier.get();
72}
73
74-(void)abort
75{
76 notifier.reset(nullptr);
77 [self reset];
78}
79
80-(bool)startSession
81{
82 if (self.session)
83 return true;
84
85 auto queue = qt_Nfc_Queue();
86 Q_ASSERT(queue);
87 self.session = [[NFCNDEFReaderSession alloc] initWithDelegate:self queue:queue invalidateAfterFirstRead:NO];
88 if (alertMessage.size())
89 self.session.alertMessage = alertMessage.toNSString();
90
91 if (!self.session)
92 return false;
93
94 qCDebug(QT_IOS_NFC, "Starting NFC NDEF reader session");
95 [self.session beginSession];
96 return true;
97}
98
99-(void)reset
100{
101 self.session = nil; // Strong property, releases.
102 self.ndefTag = nil; // Strong property, releases.
103 requestId = {};
104 tagStatus = NFCNDEFStatusNotSupported;
105 capacity = 0;
106}
107
108-(void)stopSession:(const QString &)message
109{
110 if (!self.session)
111 return;
112
113 if (self.ndefTag && notifier.get())
114 emit notifier->tagLost(self.ndefTag);
115
116 if (message.size())
117 [self.session invalidateSessionWithErrorMessage:message.toNSString()];
118 else
119 [self.session invalidateSession];
120
121 [self reset];
122}
123
124-(void)setAlertMessage:(const QString &)message
125{
126 alertMessage = message;
127}
128
129-(void)readerSession:(NFCNDEFReaderSession *)session
130 didInvalidateWithError:(NSError *)error
131{
132 if (session != self.session) // If we stopped the session, this maybe the case.
133 return;
134
135 if (!notifier.get()) // Aborted.
136 return;
137
138 NSLog(@"session did invalidate with error %@", error);
139
140 if (error.code != NFCReaderSessionInvalidationErrorUserCanceled && error.code != NFCReaderErrorUnsupportedFeature) {
141 if (self.ndefTag)
142 emit notifier->tagError(QNearFieldTarget::TimeoutError, {});
143
144 emit notifier->invalidateWithError(true);
145 [self reset];
146 }
147
148 // Native errors:
149 //
150 // NFCReaderErrorRadioDisabled
151 // NFCReaderErrorUnsupportedFeature
152 // NFCReaderErrorSecurityViolation
153 // NFCReaderErrorInvalidParameter
154 // NFCReaderErrorParameterOutOfBound
155 // NFCReaderErrorInvalidParameterLength
156 // NFCReaderTransceiveErrorTagConnectionLost
157 // NFCReaderTransceiveErrorRetryExceeded
158 // NFCReaderTransceiveErrorSessionInvalidated
159 // NFCReaderTransceiveErrorTagNotConnected
160 // NFCReaderTransceiveErrorPacketTooLong
161 // NFCReaderSessionInvalidationErrorUserCanceled
162 // NFCReaderSessionInvalidationErrorSessionTimeout
163 // NFCReaderSessionInvalidationErrorSessionTerminatedUnexpectedly
164 // NFCReaderSessionInvalidationErrorSystemIsBusy
165 // NFCReaderSessionInvalidationErrorFirstNDEFTagRead
166 // NFCTagCommandConfigurationErrorInvalidParameters
167 // NFCNdefReaderSessionErrorTagNotWritable
168 // NFCNdefReaderSessionErrorTagUpdateFailure
169 // NFCNdefReaderSessionErrorTagSizeTooSmall
170 //NFCNdefReaderSessionErrorZeroLengthMessage
171
172 // And these are what Qt has ...
173 /*
174 enum Error {
175 NoError,
176 UnknownError,
177 UnsupportedError,
178 TargetOutOfRangeError,
179 NoResponseError,
180 ChecksumMismatchError,
181 InvalidParametersError,
182 ConnectionError,
183 NdefReadError,
184 NdefWriteError,
185 CommandError,
186 TimeoutError
187 };
188 */
189 // TODO: try to map those native errors to Qt ones ...
190}
191
192-(void)readerSession:(NFCNDEFReaderSession *)session
193 didDetectNDEFs:(NSArray<NFCNDEFMessage *> *)messages
194{
195 Q_UNUSED(session);
196 Q_UNUSED(messages);
197 // It's intentionally a noop and should never be called, because
198 // we implement the other method, giving us access to a tag.
199 Q_UNREACHABLE();
200}
201
202-(void)restartPolling
203{
204 if (!self.session)
205 return;
206
207 auto queue = qt_Nfc_Queue();
208 Q_ASSERT(queue);
209
210 dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
211 int64_t(100./1000. * NSEC_PER_SEC)),//100 ms
212 queue,
213 ^{
214 [self.session restartPolling];
215 });
216}
217
218-(void)tag:(id<NFCNDEFTag>)tag didUpdateNDEFStatus:(NFCNDEFStatus)status
219 capacity:(NSUInteger)aCapacity error:(NSError *)error
220{
221 if (!notifier.get()) // Aborted.
222 return;
223
224 if (tag != self.ndefTag)
225 return;
226
227 if (error) {
228 NSLog(@"Querying NDEF tag's status failed: %@, restarting polling ...", error);
229 self.ndefTag = nil;
230 return [self restartPolling];
231 }
232
233 tagStatus = status;
234 capacity = aCapacity;
235
236 if (status == NFCNDEFStatusNotSupported) {
237 qCDebug(QT_IOS_NFC, "The discovered tag does not support NDEF.");
238 return [self restartPolling];
239 }
240
241 if (status == NFCNDEFStatusReadWrite)
242 qCDebug(QT_IOS_NFC, "NDEF read/write capable tag found");
243
244 if (status == NFCNDEFStatusReadOnly)
245 qCDebug(QT_IOS_NFC, "The discovered tag is read only");
246
247 qCInfo(QT_IOS_NFC) << "The max message size for the tag is:" << capacity;
248
249 [self.session connectToTag:self.ndefTag completionHandler:^(NSError * _Nullable error) {
250 if (!error) {
251 if (notifier.get())
252 emit notifier->tagDetected(self.ndefTag);
253 } else {
254 NSLog(@"Failed to connect to NDEF-capable tag, error: %@", error);
255 [self restartPolling];
256 }
257 }];
258}
259
260
261-(void)readerSession:(NFCNDEFReaderSession *)session
262 didDetectTags:(NSArray<__kindof id<NFCNDEFTag>> *)tags
263{
264 if (!notifier.get())
265 return; // Aborted by Qt.
266
267 if (session != self.session) // We stopped _that_ session.
268 return;
269
270 if (tags.count != 1) {
271 qCWarning(QT_IOS_NFC, "Unexpected number of NDEF tags, restarting ...");
272 [self restartPolling];
273 return;
274 }
275
276 NSLog(@"detected a tag! %@", tags[0]);
277
278 id<NFCNDEFTag> tag = tags[0];
279 self.ndefTag = tag; // Strong reference, retains.
280 tagStatus = NFCNDEFStatusNotSupported;
281 capacity = 0;
282
283 [self.ndefTag queryNDEFStatusWithCompletionHandler:
284 ^(NFCNDEFStatus status, NSUInteger aCapacity, NSError * _Nullable error) {
285 [self tag:tag didUpdateNDEFStatus:status capacity:aCapacity error:error];
286 }];
287}
288
289-(void)readerSessionDidBecomeActive:(NFCNDEFReaderSession *)session
290{
291 if (session != self.session)
292 return [session invalidateSession];
293
294 qCInfo(QT_IOS_NFC, "session is active now");
295}
296
297@end