Search code examples
c#iosxamarin.formsxamarin.iosnfc

How can I scan NFC-Tags with iOS / Xamarin from viewmodel


I have a Cross-Platform Xamarin.Forms .net standard app for android / iOS and wanted to add the nfc-scan functionality.

For my first test, I've put all into my AppDelegate class. This code works:

public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, INFCNdefReaderSessionDelegate
{
    public NFCNdefReaderSession Session { get; set; }

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        global::Xamarin.Forms.Forms.Init();

        if (Session == null)
        {
            Session = new NFCNdefReaderSession(this, null, true);
        }
        Session = new NFCNdefReaderSession(this, null, true);
        Session?.BeginSession();

        LoadApplication(new App());

        return base.FinishedLaunching(app, options);
    }

    public void DidInvalidate(NFCNdefReaderSession session, NSError error)
    {
        Console.WriteLine("ServiceToolStandard DidInvalidate: " + error.ToString());
    }

    public void DidDetect(NFCNdefReaderSession session, NFCNdefMessage[] messages)
    {
        var bytes = messages[0].Records[0].Payload.Skip(3).ToArray();
        var message = Encoding.UTF8.GetString(bytes);
        Console.WriteLine("ServiceToolStandard DidDetect: " + message);
    }
}

My Problem:

After that I've put all into a new NfcScanner class and wanted to call the ScanAsync function from my ViewModel. If I run this code, everything looks fine. The scan starts when I press the button on my phone, it shows the blue tick symbol but then it never gets into one of the implemented methods DidDetect, DidInvalidate. Do you know what the reason could be?

public class NfcScanner : INfcScanner, INFCNdefReaderSessionDelegate
{
    public string ErrorText { get; private set; }

    private NFCNdefReaderSession _session;
    private TaskCompletionSource<string> _tcs;

    public Task<string> ScanAsync()
    {
        if (!NFCNdefReaderSession.ReadingAvailable)
        {
            throw new InvalidOperationException("Reading NDEF is not available");
        }

        _tcs = new TaskCompletionSource<string>();
        _session = new NFCNdefReaderSession(this, null, true);
        _session.BeginSession();

        return _tcs.Task;
    }

    public void DidInvalidate(NFCNdefReaderSession session, NSError error)
    {
        Console.WriteLine("ServiceToolStandard DidInvalidate: " + error.ToString());
        _tcs.TrySetException(new Exception(error?.LocalizedFailureReason));
    }

    public void DidDetect(NFCNdefReaderSession session, NFCNdefMessage[] messages)
    {
        Console.WriteLine("ServiceToolStandard DidDetect msgs " + messages.Length);
        var bytes = messages[0].Records[0].Payload.Skip(3).ToArray();
        var message = Encoding.UTF8.GetString(bytes);
        Console.WriteLine("ServiceToolStandard DidDetect msg " + message);
        _tcs.SetResult(message);
    }

    public IntPtr Handle { get; }

    public void Dispose()
    {
        Console.WriteLine("ServiceToolStandard Dispose");
    }
}

I tried also to make a void Scan() Method and call it directly from AppDelegate. The result is the same. The scan window opens and it shows the blue tick symbol, but it never reaches the methods diddetect, didinvalidate.


Edit 07.02.2018 16:56:

I saw in the example code, that their class derives from UITableViewController. This was the only real difference between my code and the example code. After I derived in my NfcScanner class from ViewController it worked. But I'm not sure why and if it's the right way?

public class NfcScanner : UIViewController, INfcScanner, INFCNdefReaderSessionDelegate
{
 ...
}

Thanks a lot for your help!


Solution

  • I tried to find an answer, why it works after i derived from UIViewController. I think I found it, in this tutorial: Core NFC Tutorial

    Creating a session, we can specify a delegate in our NFCNDEFReaderSession class. I would like to use the NFCHelper class as the delegate, so we must first adhere to the delegate protocol, NFCNDEFReaderSessionDelegate. This delegate is based on an Objective-C object, so we must first also adhere to NSObject. NFCNDEFReaderSessionDelegate has two delegate methods we must implement:

    UIViewController inherits from NSObject. Now it also works after I changed my code to:

    public class NfcScanner : NSObject, INfcScanner, INFCNdefReaderSessionDelegate
    {
     ...
    }