Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow iOS to read tag before writing #420

Merged
merged 5 commits into from
Jul 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,74 @@ Function `nfc.write` writes an NdefMessage to a NFC tag.

On **Android** this method *must* be called from within an NDEF Event Handler.

On **iOS** this method should be called outside the NDEF Event Handler, it will start a new scanning session.
On **iOS** this method can be called outside the NDEF Event Handler, it will start a new scanning session. Optionally you can reuse the read session to write data. See example below.

On **Windows** this method *may* be called from within the NDEF Event Handler.

On **Windows Phone 8.1** this method should be called outside the NDEF Event Handler, otherwise Windows tries to read the tag contents as you are writing to the tag.

### Examples

#### Android

On Android, write must be called inside an event handler

function onNfc(nfcEvent) {

console.log(nfcEvent.tag);
var message = [
ndef.textRecord(new String(new Date()))
];
nfc.write(
message,
success => console.log('wrote data to tag'),
error => console.log(error)
);

nfc.addNdefListener(onNfc);


#### iOS - Simple

Calling `nfc.write` on iOS will create a new session and write data when the user taps a NFC tag

var message = [
ndef.textRecord("Hello, world")
];

nfc.write(
message,
success => console.log('wrote data to tag'),
error => console.log(error)
);

#### iOS - Read and Write

On iOS you can optionally write to NFC tag using the read session

try {
let tag = await nfc.scanNdef({ keepSessionOpen: true});

// you can read tag data here
console.log(tag);
// this example writes a new message with a timestamp
var message = [
ndef.textRecord(new String(new Date()))
];

nfc.write(
message,
success => console.log('wrote data to tag'),
error => console.log(error)
);

} catch (err) {
console.log(err);
}

### Supported Platforms

- Android
Expand Down
61 changes: 49 additions & 12 deletions src/ios/NfcPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
@interface NfcPlugin() {
NSString* sessionCallbackId;
NSString* channelCallbackId;
id<NFCNDEFTag> connectedTag API_AVAILABLE(ios(13.0));
NFCNDEFStatus connectedTagStatus API_AVAILABLE(ios(13.0));
}
@property (nonatomic, assign) BOOL writeMode;
@property (nonatomic, assign) BOOL shouldUseTagReaderSession;
@property (nonatomic, assign) BOOL sendCallbackOnSessionStart;
@property (nonatomic, assign) BOOL returnTagInCallback;
@property (nonatomic, assign) BOOL returnTagInEvent;
@property (nonatomic, assign) BOOL keepSessionOpen;
@property (strong, nonatomic) NFCReaderSession *nfcSession API_AVAILABLE(ios(11.0));
@property (strong, nonatomic) NFCNDEFMessage *messageToWrite API_AVAILABLE(ios(11.0));
@end
Expand Down Expand Up @@ -52,7 +55,8 @@ - (void)beginSession:(CDVInvokedUrlCommand*)command {
self.sendCallbackOnSessionStart = YES; // Not sure why we were doing this
self.returnTagInCallback = NO;
self.returnTagInEvent = YES;

self.keepSessionOpen = NO;

[self startScanSession:command];
}

Expand All @@ -64,6 +68,9 @@ - (void)scanNdef:(CDVInvokedUrlCommand*)command {
self.returnTagInCallback = YES;
self.returnTagInEvent = NO;

NSArray<NSDictionary *> *options = [command argumentAtIndex:0];
self.keepSessionOpen = [options valueForKey:@"keepSessionOpen"];

[self startScanSession:command];
}

Expand All @@ -75,6 +82,9 @@ - (void)scanTag:(CDVInvokedUrlCommand*)command {
self.returnTagInCallback = YES;
self.returnTagInEvent = NO;

NSArray<NSDictionary *> *options = [command argumentAtIndex:0];
self.keepSessionOpen = [options valueForKey:@"keepSessionOpen"];

[self startScanSession:command];
}

Expand All @@ -83,6 +93,7 @@ - (void)writeTag:(CDVInvokedUrlCommand*)command API_AVAILABLE(ios(13.0)){

self.writeMode = YES;
self.shouldUseTagReaderSession = NO;
BOOL reusingSession = NO;

NSArray<NSDictionary *> *ndefData = [command argumentAtIndex:0];

Expand All @@ -107,30 +118,41 @@ - (void)writeTag:(CDVInvokedUrlCommand*)command API_AVAILABLE(ios(13.0)){
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

// Start the NFC Session
if (self.shouldUseTagReaderSession) {
NSLog(@"Using NFCTagReaderSession");

self.nfcSession = [[NFCTagReaderSession new]
initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693)
delegate:self queue:dispatch_get_main_queue()];
if (self.nfcSession && self.nfcSession.isReady) { // reuse existing session
reusingSession = YES;
} else { // create a new session
if (self.shouldUseTagReaderSession) {
NSLog(@"Using NFCTagReaderSession");

} else {
NSLog(@"Using NFCTagReaderSession");
self.nfcSession = [[NFCNDEFReaderSession new]initWithDelegate:self queue:nil invalidateAfterFirstRead:FALSE];
self.nfcSession = [[NFCTagReaderSession new]
initWithPollingOption:(NFCPollingISO14443 | NFCPollingISO15693)
delegate:self queue:dispatch_get_main_queue()];

} else {
NSLog(@"Using NFCTagReaderSession");
self.nfcSession = [[NFCNDEFReaderSession new]initWithDelegate:self queue:nil invalidateAfterFirstRead:FALSE];
}
}

self.nfcSession.alertMessage = @"Hold near writable NFC tag to update.";
sessionCallbackId = [command.callbackId copy];
[self.nfcSession beginSession];

if (reusingSession) { // reusing a read session to write
self.keepSessionOpen = NO; // close session after writing
[self writeNDEFTag:self.nfcSession status:connectedTagStatus tag:connectedTag];
} else {
[self.nfcSession beginSession];
}
}

- (void)cancelScan:(CDVInvokedUrlCommand*)command API_AVAILABLE(ios(11.0)){
NSLog(@"cancelScan");
if (self.nfcSession) {
[self.nfcSession invalidateSession];
}
connectedTag = NULL;
connectedTagStatus = NFCNDEFStatusNotSupported;
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
Expand Down Expand Up @@ -326,6 +348,11 @@ - (void)processNDEFTag: (NFCReaderSession *)session tag:(__kindof id<NFCNDEFTag>
if (self.writeMode) {
[self writeNDEFTag:session status:status tag:tag];
} else {
// save tag & status so we can re-use in write
if (self.keepSessionOpen) {
self->connectedTagStatus = status;
self->connectedTag = tag;
}
[self readNDEFTag:session status:status tag:tag metaData:metaData];
}

Expand Down Expand Up @@ -456,8 +483,16 @@ - (void) sessionDidBecomeActive:(NFCReaderSession *) session API_AVAILABLE(ios(
}

- (void) closeSession:(NFCReaderSession *) session API_AVAILABLE(ios(11.0)){

// this is a hack to keep a read session open to allow writing
if (self.keepSessionOpen) {
return;
}

// kill the callback so the Cordova doesn't get "Session invalidated by user"
sessionCallbackId = NULL;
connectedTag = NULL;
connectedTagStatus = NFCNDEFStatusNotSupported;
[session invalidateSession];
}

Expand All @@ -466,6 +501,8 @@ - (void) closeSession:(NFCReaderSession *) session withError:(NSString *) errorM

// kill the callback so Cordova doesn't get "Session invalidated by user"
sessionCallbackId = NULL;
connectedTag = NULL;
connectedTagStatus = NFCNDEFStatusNotSupported;

if (@available(iOS 13.0, *)) {
[session invalidateSessionWithErrorMessage:errorMessage];
Expand Down
6 changes: 3 additions & 3 deletions www/phonegap-nfc.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,16 +501,16 @@ var nfc = {
},

// iOS only - scan for NFC NDEF tag using NFCNDEFReaderSession
scanNdef: function () {
scanNdef: function (options) {
return new Promise(function(resolve, reject) {
cordova.exec(resolve, reject, "NfcPlugin", "scanNdef", []);
cordova.exec(resolve, reject, "NfcPlugin", "scanNdef", [options]);
});
},

// iOS only - scan for NFC Tag using NFCTagReaderSession
scanTag: function (options) {
return new Promise(function(resolve, reject) {
cordova.exec(resolve, reject, "NfcPlugin", "scanTag", []);
cordova.exec(resolve, reject, "NfcPlugin", "scanTag", [options]);
});
},

Expand Down