我正在开发一个 .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 所需的权利。我按照官方指南的所有步骤进行操作。
这将以下文本添加到我的项目配置中。
<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
如果您在这里查看我的博客: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 一起使用,您应该能够获得回调。