在 iOS 设备上使用 .NET MAUI 扫描 NFC 标签

问题描述 投票:0回答:1

我正在开发一个 .NET MAUI 应用程序,扫描 NFC 标签是要求之一。我已经完成了Android的开发,非常简单。对于 Android,从检测 NFC 标签、获取有效负载到处理有效负载,一切正常。

对于 iOS,我无法检测到 NFC 标签。这是我所做的。

iOS NFC 读卡器服务

我为 iOS 实现了一个非常简单的 NFC 服务,如下所示。

using CoreFoundation;
using CoreNFC;
using Foundation;
using MSS.App.Services.Interfaces;
using System;
using UIKit;

namespace OurAppNamespace.Platforms.iOS.Services
{
    public class IosNfcService : NFCTagReaderSessionDelegate, INfcService
    {
        private readonly ILogService? _logService;

        private NFCTagReaderSession? _nfcSession;

        public IosNfcService(ILogService? logService)
        {
            _logService = logService;
        }

        public async void Enable()
        {
            _logService?.LogInfo($"{this}.Enable");

            try
            {
                var isNfcAvailable = UIDevice.CurrentDevice.CheckSystemVersion(13, 0);

                _logService?.LogInfo($"{this}.Enable > {nameof(isNfcAvailable)} = {isNfcAvailable}");

                if (isNfcAvailable)
                {
                    _nfcSession = new NFCTagReaderSession(NFCPollingOption.Iso14443 | NFCPollingOption.Iso15693, this, DispatchQueue.CurrentQueue)
                    {
                        AlertMessage = "Test123"
                    };
                    _nfcSession.BeginSession();

                    _logService?.LogInfo($"{this}.Enable > Session started.");
                }
            }
            catch (Exception exception)
            {
                _logService?.LogError($"{this}.Enable > Starting Reader Session failed.", exception);
            }
        }

        public override void DidBecomeActive(NFCTagReaderSession session)
        {
            _logService?.LogInfo($"{this}.DidBecomeActive");
        }

        public override void DidInvalidate(NFCTagReaderSession session, NSError error)
        {
            _logService?.LogInfo($"{this}.DidInvalidate");
        }

        public override void DidDetectTags(NFCTagReaderSession session, INFCTag[] tags)
        {
            _logService?.LogInfo($"{this}.DidDetectTags");
        }

        public void Disable()
        {
            _logService?.LogInfo($"{this}.Disable");

            _nfcSession?.InvalidateSession();
        }
    }
}

它基于我在网上找到的几个代码示例。基本思想(据我了解)是,如果 iOS 版本为 13 或更高版本,则创建一个 NFCTagReaderSession 实例,只要打开视图就启动读取器会话,一旦检测到某些内容,就会调用适当的函数。根据我的理解,一旦我将手机放在 NFC 标签上,我应该会接到

DidDetectTags
的电话 - 但我没有。

信息.plist

我已经编辑了 .NET MAUI 项目中 Platforms/iOS 下的 Info.plist,以添加 NFC 标签的权限。我添加到文件中的行是:

<key>NFCReaderUsageDescription</key>
<string>NFC tag to read NDEF messages into the application</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
    <string>com.apple.developer.nfc.readersession.iso7816.select-identifiers</string>
    <string>D2760000850100</string>
</array>

权利.plist

这是我的第一个针对 iOS 的应用程序,我了解了 iOS 所需的权利。我按照官方指南的所有步骤进行操作。

  1. 我在 Platforms/iOS 下的 Info.plist 旁边的项目中添加了一个空的 Entitlements.plist 文件。
  2. 我打开项目的设置,转到“iOS”类别,然后转到“Bundle Signing”,并在“自定义权利”下添加刚刚添加的 Entitlements.plist。

这将以下文本添加到我的项目配置中。

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-ios|AnyCPU'">
  <CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-ios|AnyCPU'">
  <CodesignEntitlements>Platforms\iOS\Entitlements.plist</CodesignEntitlements>
</PropertyGroup>

结果

我已经删除了解决方案的 bin 和 obj 文件夹,从 iOS 设备中删除了之前部署的应用程序测试版本,重新启动了设备,然后重建了整个解决方案。如果我现在打开应用程序,我可以打开应用程序,但我没有收到任何 NFC 事件。我看到了

Enable
函数的日志输出,但没有看到
DidDetectTags
的调用。

我已经添加了Entitlements.plist,但我没有在文件中设置“近场通信标签读取”复选框。如果这样做,我将无法再启动该应用程序。 Visual Studio 的输出并没有告诉我太多信息,但 Xamarin 的日志文件表明配置文件存在问题。我研究了下面提到的错误,但我不确定如何继续。如果我从 Entitlements.plist 中删除复选框,应用程序将正常运行 - 但我没有检测到任何 NFC 标签。

Xamarin.Messaging.IDB.Local.DeployAppMessageHandler Error: 0 : An error occurred while trying to deploy the app '[APP_NAME]'. Details: Could not install the application '[PATH]\[APP_NAME]\out\[APP].ipa' on the device [TEST_DEVICE]. Details: ApplicationVerificationFailed|0xE8008015 - Failed to verify code signature of /var/installd/Library/Caches/com.apple.mobile.installd.staging/temp.59k3xO/extracted/Payload/[APP_NAME] : 0xe8008015 (A valid provisioning profile for this executable was not found.)
Xamarin.iOS.Windows.WindowsiOSException: Could not install the application '[LOCAL_PATH]\[APP_NAME]\out\[APP].ipa' on the device [TEST_DEVICE]. Details: ApplicationVerificationFailed|0xE8008015 - Failed to verify code signature of /var/installd/Library/Caches/com.apple.mobile.installd.staging/temp.59k3xO/extracted/Payload/[APP_NAME] : 0xe8008015 (A valid provisioning profile for this executable was not found.)
   at Xamarin.iOS.Windows.Installer.ApplicationSession.InstallApp(String appPath, String appBundleId) in D:\a\_work\1\s\src\Tools\Xamarin.iOS.Windows.Client\Installer\ApplicationSession.cs:line 276
   at Xamarin.iOS.Windows.Installer.ApplicationSession.Deploy(String appRootFolder, String appBundleId, String appName) in D:\a\_work\1\s\src\Tools\Xamarin.iOS.Windows.Client\Installer\ApplicationSession.cs:line 95
   at Xamarin.iOS.Windows.HotRestartClient.Deploy(AppleDevice nativeDevice, String appBundleId, String appBundleName, Boolean& incremental) in D:\a\_work\1\s\src\Tools\Xamarin.iOS.Windows.Client\HotRestartClient.cs:line 250
   at Xamarin.Messaging.IDB.Local.DeployAppMessageHandler.<ExecuteAsync>d__5.MoveNext() in D:\a\_work\1\s\src\Messaging\Xamarin.Messaging.IDB.Local\Handlers\DeployAppMessageHandler.cs:line 43: 04/04/2024 16:20:09Z
    DateTime=2024-04-04T16:20:09.7343180Z: 04/04/2024 16:20:09Z
ios maui nfc core-nfc
1个回答
0
投票

如果您在这里查看我的博客:https://medium.com/stackademic/nfc-ndef-communication-with-maui-and-xamarin-forms-fae28632c2d3

我已经展示了如何创建一个自定义会话来让您接收回调:

 public class SessionDelegate : NFCNdefReaderSessionDelegate
{
    private readonly byte[] bytes;
    public TaskCompletionSource<NfcTransmissionStatus> WasDataTransmitted { get; set; }

    public SessionDelegate(byte[] bytes)
    {
        this.bytes = bytes;
        WasDataTransmitted = new TaskCompletionSource<NfcTransmissionStatus>();
    }

    public override void DidDetect(NFCNdefReaderSession session, NFCNdefMessage[] messages)
    {

    }

    public override void DidDetectTags(NFCNdefReaderSession session, INFCNdefTag[] tags)
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            session.AlertMessage = "Tag detected";
            try
            {
                if (tags.Length != 1)
                {
                    session.InvalidateSession(errorMessage: "Cannot write on multiple tags at the same time");
                    WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                    return;
                }
                var NdefTag = tags.First();
                session.ConnectToTag(NdefTag, (error) =>
                {
                    if (error != null)
                    {
                        WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                        session.InvalidateSession("There was an error while making this request");
                        return;
                    }
                });
                NdefTag?.QueryNdefStatus((status, capacity, error) =>
                {
                    if (error != null)
                    {
                        WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                        session.InvalidateSession("Could not query status of tag");
                    }
                    switch (status)
                    {
                        case NFCNdefStatus.NotSupported:
                            WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                            session.InvalidateSession("This is an unsupported tag");
                            break;
                        case NFCNdefStatus.ReadOnly:
                            WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                            session.InvalidateSession("This tag is readonly");
                            break;
                        case NFCNdefStatus.ReadWrite:
                            var isNfcWriteAvailable = UIDevice.CurrentDevice.CheckSystemVersion(13, 0);
                            if (isNfcWriteAvailable)
                            {
                                var chunkString = Encoding.UTF8.GetString(bytes);
                                var textPayload = NFCNdefPayload.CreateWellKnownTypePayload(chunkString);
                                var ndefpayloadArray = new NFCNdefPayload[] { textPayload };
                                var ndefMessage = new NFCNdefMessage(ndefpayloadArray);
                                NdefTag.WriteNdef(ndefMessage, (tagError) =>
                                {
                                    if (tagError != null)
                                    {
                                        WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                                        session.InvalidateSession("Falied to write this message");
                                    }
                                    else
                                    {
                                        session.AlertMessage = "Write successful";
                                        session.InvalidateSession();
                                        WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Success);
                                    }
                                });
                            }
                            else
                            {
                                WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
                                session.InvalidateSession("There was an error while trying to write on this tag");
                            }
                            break;
                        default:
                            WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Unknown);
                            session.InvalidateSession("Tag status unkoown");
                            break;
                    }
                });
            }
            catch
            {
                session.InvalidateSession();
                WasDataTransmitted.TrySetResult(NfcTransmissionStatus.Failed);
            }
        });
    }

    public override void DidInvalidate(NFCNdefReaderSession session, NSError error)
    {
        if (error.Code == (int)NFCReaderError.ReaderSessionInvalidationErrorSessionTimeout)
        {
            MainThread.BeginInvokeOnMainThread(async () =>
            {
                await Application.Current.MainPage.DisplayAlert("Error ", "Tag search timed out", "Ok");
            });
        }
    }
}

然后将其与 TaskCompletionSource 一起使用,您应该能够获得回调。

© www.soinside.com 2019 - 2024. All rights reserved.