iOS 11 Core NFC - any sample code?
Asked Answered
H

7

22

I just installed the first iOS 11 beta to an iPhone 7 and am interested in trying the NFC. There's nothing about it in settings. I am wondering if there's any sample code out there showing how to read a tag. Can anyone show how to use the Core NFC SDK, in a code snippet?

Habergeon answered 6/6, 2017 at 2:8 Comment(4)
I tried it, But always return NFCReaderSessionInvalidationErrorSessionTerminatedUnexpectedly.Thurstan
Follow Boris' suggestions in the accepted answer below. Make sure your info.plist and entitlements are set correctly.Habergeon
Good background information in a WWDC video at the Apple Developer site. It answers most of the questions being raised: developer.apple.com/videos/play/wwdc2017/718Habergeon
Here's one I made, you still need an Apple Dev account and setup your App ID to make it work: github.com/x43x61x69/Core-NFC-ExampleNembutal
C
40

In the Apple Developer site, create a new App ID and make sure that NFC Tag Reading is enabled.

Dev portal capabilities

Add the following lines to your .plist file:

<key>NFCReaderUsageDescription</key>
<string>NFC Tag!</string>

and these to the entitlements file:

<key>com.apple.developer.nfc.readersession.formats</key>
    <array>
        <string>NDEF</string>
    </array>

It should look something like this in the corresponding files:

enter image description here

Also Core NFC can be enabled via the Capabilities tab in Xcode.

enter image description here

Objective-c

Import CoreNFC

#import <CoreNFC/CoreNFC.h>

and set the delegate:

@interface YourViewController : UIViewController <NFCNDEFReaderSessionDelegate>

In viewDidLoad:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NFCNDEFReaderSession *session = [[NFCNDEFReaderSession alloc] initWithDelegate:self queue:dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT) invalidateAfterFirstRead:NO];
    [session beginSession];
}

In the delegate callback:

- (void) readerSession:(nonnull NFCNDEFReaderSession *)session didDetectNDEFs:(nonnull NSArray<NFCNDEFMessage *> *)messages {

    for (NFCNDEFMessage *message in messages) {
        for (NFCNDEFPayload *payload in message.records) {
            NSLog(@"Payload data:%@",payload.payload);
        }
    }        
}

You must also add the didInvalidateWithError delegate callback or you'll not conform with protocol:

- (void)readerSession:(nonnull NFCNDEFReaderSession *)session didInvalidateWithError:(nonnull NSError *)error {

}

You can stop the reader with:

[session invalidateSession];

Swift 3/4

Import CoreNFC

import CoreNFC

and set the delegate:

class YourViewController: UIViewController, NFCNDEFReaderSessionDelegate

In viewDidLoad:

override func viewDidLoad() {
        super.viewDidLoad()

        let session = NFCNDEFReaderSession(delegate: self,
                      queue: DispatchQueue(label: "queueName", attributes: .concurrent), invalidateAfterFirstRead: false)  
        session?.begin()
    }

In the delegate callback:

func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
  for message in messages {
    for record in message.records {
      print(record.payload)
    }
  }
}

You can stop the reader with:

session.invalidateSession

Usage

After launching the view you should immediately see the iOS NFC reader dialog like so:

iOS NFC reader dialog

Once this dialog appears you have about a second to place the iPhone near the NFC tag you want to read. Otherwise, the field is deactivated (this seems to be a bug on Apple's end). I often needed to cancel and retry to get consistent readings. More details here.

Clabo answered 6/6, 2017 at 7:17 Comment(22)
You are successful? I try it always return NFCReaderSessionInvalidationErrorSessionTerminatedUnexpected‌​ly.Thurstan
Updated the answer with the required .plist files entriesClabo
Boris - you're brilliant! I was able to get it working. It only works for a couple of minutes, then the prompt disappears and I have to restart the app. But at least it's a start.Habergeon
You set invalidateAfterFirstRead to false. I believe this makes it scan forever. How can I stop or start the scanning when I'm done with it via code?Cattle
set invalidateAfterFirstRead=YES Then put [session beginSession] into a button callback or similar. Press a button on your app's home screen, it pops up the scan prompt, then after scanning one tag, control is returned to your UI. This is probably the workflow you want to use anyway.Habergeon
You can always call the invalidateSession method (developer.apple.com/documentation/corenfc/nfcreadersession/…) I'll update the answer.Clabo
Awesome got it working. Only issue I have now is I can't get the URL from the tag. I make an NSString using the payload.payload NSData and I get \^Cyoutube.com. It is supposed to say http:// in front. Not sure how to get that. I was expecting the content to just show in text format.Cattle
Payload is NSData, which encoding are you using for NSString initWithData?Clabo
The first byte of the NDEF is the TNF (type name format) field. ^C is 0x03 which is URI type. iOS, unlike Android, doesn't seem to come with an OS-level parser that automatically determines the NDEF record type for you. You have to parse it yourself, at least for now.Habergeon
@Habergeon I'm using UTF8 for the encoding as a guess. If ^C is URL that helps, but is it supposed to strip the http:// from the tag content? The text following it is always just the domain like youtube.com. I'm wondering if https would come up as ^C as well.Cattle
that first byte follows this spec: nfcpy.readthedocs.io/en/latest/topics/ndef.html So you can get the byte and append a string based on the type, if you ask a question for that I can put some code for that issue, but it's too large for a commentClabo
@Clabo I ask the question here: #44445659Cattle
Your image accompanying the "Entitlements plist file" is referencing an Info.plist.Southwestward
Yeah, I added the image as reference and put the "Should look something like this in the corresponding files" text, but I guess it's not clear enoughClabo
SolidSnake4444, ^C = HTTP and ^D = HTTPS. It's just an abbreviation because NFC tags have limited capacity (especially the old 52-byte tags). Hope that helps.Habergeon
I got it working but for some reason, the detection can only occur about 1 second after the session has been started. If I wait 5 seconds, the phone won't detect anything anymore. Is there anyone with the same problem or that would know why I get this behaviour? Any fix?Distilled
According to the session video, sessions expire after 60 seconds. You'll need to stop and restart them each time, or build some way to stop and restart.Deoxyribonuclease
Yes, I read that but I'm talking about a really shorter time and no expire event is sent before I cancel the session or the timeout time. I'm trying to see if anyone gets the same problem.Distilled
I have done everything like this. If I type NFCNDEFReaderSession.readingAvailable it even prints out true. I have the privacy string and the entitlement settled, and enabled in developer console.. But when I lauch my app, the dialog does not appear.Extenuate
Oh - nevermind. I was blindly following the Swift example, and you have forgotten to put in session.begin() there. It is present in the example for Objective-C.Extenuate
Why I crash at session?.begin() with message "Thread 1: signal SIGABRT" ??Gogh
Where do you put the sessionInvalidate? My app runs - reads tag. But there's about a 4-5 sec delay between the read and updating the label text. Almost like it only does so after timing out.Katlynkatmai
D
5

To fix this issue you can add com.apple.developer.nfc.readersession.formats key into your entitlements file. The key should be associated with the array of enabled NFS types. For instance, you can try the following:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>NDEF</string>
</array>

It worked for me.

Delibes answered 6/6, 2017 at 9:7 Comment(5)
Thanks! I am no longer confronted with Error202. But now the app crashes with CoreNFC__CRASHING_DUE_TO_PRIVACY_VIOLATION__. Do you have a usage description in your info.plist?Sapajou
Yes, I have a description.Delibes
It is a key for description. NFCReaderUsageDescriptionDelibes
@Max thanks! I was looking for it but the documentation is too short! Where did you find it? Thanks againDemolish
Went thru all the steps (thanks!) but when I go back to verify under Capabilities I only see the 2 steps - not the 3rd for add NFC to your info.plist file. It's in there. Does it matter where? And a 2nd question: so this means you can only create a new NFC app when you have internet access (since you have to register the app and turn on NFC in the Developer Site)?Katlynkatmai
D
4

Updated for second Xcode beta.

Add the NFC capability to your app from the Developer Center. Identifiers -> App IDs -> enable "NFC Tag Reading".

If your project does not have an entitlement file, let Xcode create one for you by just activating and then subsequently deactivating any capability from within Xcode -> Project Targets -> Capabilities. You will find a new [AppName].entitlements file in your project navigator. Right-click on that file and select "Open as -> Source Code". Enter the following entry manually between <dict></dict>:

<key>com.apple.developer.nfc.readersession.formats</key>
<array>
    <string>NDEF</string>
</array>

As soon as Xcode 9 allows enabling NFC Tag Reading from the Capabilities selection this step becomes obsolete because all you have to do is to enable it there. The current (first) Beta version does not support this.

You also need to enter a usage description for the privacy warning iOS will show the user. (At the moment (beta 1) this warning will be displayed when the device is ready to scan and shows a native system dialogue which will include this message. However, this seems to be unploished.) Open your target's Info.plist and start typing "Privacy" and you can scroll down to "Privacy - NFC Usage Description" to select it by hitting return. Enter a meaningful explanation to your user in the right column.

Now you should be able to import CoreNFC, in Swift:

import CoreNFC

Then head over to Apple's documentation.

Important: If the compiler returns an error with No such module 'CoreNFC' check if you have selected an actual iOS 11 device to build for, not a simulator. It also has to be the iPhone 7 or 7 plus. This might change in a future version, but testing NFC will only ever be fully working with actual hardware. (cp. Core Bluetooth where you can run on Simulator but not test the actual capabilities.) Beta 2 doesn't have this issue anymore. However, actual hardware in form of iPhone 7/p is still required for actual testing.

Depew answered 7/6, 2017 at 16:40 Comment(1)
Good note on the "No such module" error. You can also bypass by just using the Generic Device option when compiling. You can run it on anything, but will at least let you compile things to make sure it works.Deoxyribonuclease
P
3

Just to enrich previous answers, it's important to bear in mind these considerations specifically of the NFCNDEFReaderSession class:

  • Reader session for processing NFC Data Exchange Format (NDEF) tags. This session requires the "com.apple.developer.nfc.readersession.formats" entitlement in your process. In addition your application's Info.plist must contain a non-empty usage description string.
  • A NDEF reader session will automatically scan and detect NFC Forum tags that contain a valid NDEF message. NFC Forum Tag type 1 to 5 that is NDEF formatted are supported. A modal system UI will present once -beginSession is called to inform the start of the session; the UI sheet is automatically dismissed when the session is invalidated either by the user or by calling -invalidateSession.
  • An opened session has a 60 seconds time limit restriction after -beginSession is called; -readerSession:didInvalidateWithError: will return NFCReaderSessionInvalidationErrorSessionTimeout error when the time limit is reached.
  • Only 1 active reader session is allowed in the system; -readerSession:didInvalidateWithError: will return NFCReaderSessionInvalidationErrorSystemIsBusy when a new reader session is initiated by -beginSession when there is an active reader session.
  • -readerSession:didInvalidateWithError: will return NFCReaderSessionInvalidationErrorUserCanceled when user clicks on the done button on the UI.
  • -readerSession:didInvalidateWithError: will return NFCReaderSessionInvalidationErrorSessionTerminatedUnexpectedly when the client application enters the background state. -readerSession:didInvalidateWithError: will return NFCReaderErrorUnsupportedFeature when
    1. reader mode feature is not available on the hardware
    2. client application does not have the required entitlement.
Photoactinic answered 22/6, 2017 at 8:24 Comment(2)
I keep on getting "Feature not supported" error in -readerSession:didInvalidateWithError: Is it because I'm testing on an iPhone SE? But the SE has NFC AFAIKDevol
@Devol yes, currently reading NFC NDEF tags is just supported on iPhone 7 and iPhone 7 Plus.Photoactinic
A
1

You need to make sure the usage description is in place and also add the capability to the app inside of the Apple Developer Center. I have a tutorial based on my experience (Swift 4-based). It's available here: Core NFC Tutorial

Atone answered 6/6, 2017 at 16:6 Comment(0)
B
0

I added Core NFC to a project using the resources in these answers. One additional thing that wasn't noted though was that even if you add the capability manually via the entitlements, Xcode doesn't seem to look at the file unless you have a capability turned on. This is probably due to Xcode 9 Beta 1 not having Core NFC as a capability switch for lots of people's sample projects. So just be sure to turn at least one other capability on if you're still seeing issues! I was seeing an unexpected termination error immediately return until I did this.

I would leave this as a comment as it belongs, but don't have enough reputation yet to do so. Figured this was important enough to note.

Blinker answered 16/6, 2017 at 15:49 Comment(0)
M
0

my two cents:

1) under xcode 9.0 (beta 4 9M189t ) if You have already added capabilities, no need to manually add:

<key>com.apple.developer.nfc.readersession.formats</key>
    <array>
        <string>NDEF</string>
    </array>

it is done automatically

2) no crash if not using iPhone 7 OR you are in simulator:

You wil be called in:

func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        print(error.localizedDescription)
    }

it will show: "Feature not supported"

3) dont miss:

self.nfcSession?.begin() // will trigger callback

so:

    final private func setup(){
    self.nfcSession = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
    self.nfcSession?.alertMessage = "Put your NFC TAG over iPhone.."
    self.nfcSession?.begin() // will trigger callback

}

4) if user cancels, you will get:

"Session is invalidated by user cancellation"

in didInvalidateWithError callback.

Millennium answered 6/8, 2017 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.