Xamarin 通知服务扩展问题
Xamarin Notification Service Extension issue
我对通知服务扩展有疑问。
我已经按照一步一步的文档
https://developer.xamarin.com/guides/ios/platform_features/introduction-to-ios10/user-notifications/enhanced-user-notifications/#Working_with_Service_Extensions
为了实现,我是那样做的。
- 添加了与我的应用程序具有相同前缀的通知服务扩展(添加后缀,例如:APP:com.testapp.main - EXT:com.testapp.main.notificationextension)
- 已创建 APPID 标识符 com.testapp.main.notificationextension 到 Apple 会员中心
- 已创建证书和配置文件以发送 APP ID com.testapp.main.notificationextension
的推送通知
- 已导入 Xcode 和 Xamarin 证书和配置
- 参考通知扩展参考构建我的应用程序。
- 已创建存档以上传到 TestFlight
- 已签名的应用及其分发证书和配置文件
- 带有分发证书和配置文件的签名扩展
- 已上传到 TestFlight
- 为我的应用程序下载并允许推送通知
- 使用 Localytics 仪表板发送丰富的推送通知以进行消息传递
- 设备收到推送通知但未通过 NotificationService.cs 通知服务扩展代码!
这是我的 NotificationService 代码:
using System;
using Foundation;
using UserNotifications;
namespace NotificationServiceExtension
{
[Register("NotificationService")]
public class NotificationService : UNNotificationServiceExtension
{
Action<UNNotificationContent> ContentHandler { get; set; }
UNMutableNotificationContent BestAttemptContent { get; set; }
const string ATTACHMENT_IMAGE_KEY = "ll_attachment_url";
const string ATTACHMENT_TYPE_KEY = "ll_attachment_type";
const string ATTACHMENT_FILE_NAME = "-localytics-rich-push-attachment.";
protected NotificationService(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public override void DidReceiveNotificationRequest(UNNotificationRequest request, Action<UNNotificationContent> contentHandler)
{
System.Diagnostics.Debug.WriteLine("Notification Service DidReceiveNotificationRequest");
ContentHandler = contentHandler;
BestAttemptContent = (UNMutableNotificationContent)request.Content.MutableCopy();
if (BestAttemptContent != null)
{
string imageURL = null;
string imageType = null;
if (BestAttemptContent.UserInfo.ContainsKey(new NSString(ATTACHMENT_IMAGE_KEY)))
{
imageURL = BestAttemptContent.UserInfo.ValueForKey(new NSString(ATTACHMENT_IMAGE_KEY)).ToString();
}
if (BestAttemptContent.UserInfo.ContainsKey(new NSString(ATTACHMENT_TYPE_KEY)))
{
imageType = BestAttemptContent.UserInfo.ValueForKey(new NSString(ATTACHMENT_TYPE_KEY)).ToString();
}
if (imageURL == null || imageType == null)
{
ContentHandler(BestAttemptContent);
return;
}
var url = NSUrl.FromString(imageURL);
var task = NSUrlSession.SharedSession.CreateDownloadTask(url, (tempFile, response, error) =>
{
if (error != null)
{
ContentHandler(BestAttemptContent);
return;
}
if (tempFile == null)
{
ContentHandler(BestAttemptContent);
return;
}
var cache = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User, true);
var cachesFolder = cache[0];
var guid = NSProcessInfo.ProcessInfo.GloballyUniqueString;
var fileName = guid + ATTACHMENT_FILE_NAME + imageType;
var cacheFile = cachesFolder + fileName;
var attachmentURL = NSUrl.CreateFileUrl(cacheFile, false, null);
NSError err = null;
NSFileManager.DefaultManager.Move(tempFile, attachmentURL, out err);
if (err != null)
{
ContentHandler(BestAttemptContent);
return;
}
UNNotificationAttachmentOptions options = null;
var attachment = UNNotificationAttachment.FromIdentifier("localytics-rich-push-attachment", attachmentURL, options, out err);
if (attachment != null)
{
BestAttemptContent.Attachments = new UNNotificationAttachment[] { attachment };
}
ContentHandler(BestAttemptContent);
return;
});
task.Resume();
}
else {
ContentHandler(BestAttemptContent);
}
}
public override void TimeWillExpire()
{
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
ContentHandler(BestAttemptContent);
return;
}
}
}
您做的一切都正确,这是其他一些 xamarin 开发人员提出的问题。据我所知,一旦你 运行 NSURLSession 下载一些东西,即使它非常非常小,你也会超出此类扩展允许的内存限制。这很可能是 xamarin 特有的。
这是 bugzilla 的 link。
https://bugzilla.xamarin.com/show_bug.cgi?id=43985
我发现 work-around/hack 远非理想。我在 objective-c 中的 xcode 中重写了这个应用程序扩展(我想你也可以使用 swift)。这是一个相当小的 (1 class) 扩展。然后使用相同的代码签名证书/配置文件在 xcode 中构建它,然后在 xcode 的输出中找到 .appex 文件。
从那时起,您可以使用 "cheap way",并在退出和提交应用程序之前手动交换 .ipa 文件夹中的这个 .appex 文件。如果这对你来说足够好,你可以到此为止。
或者您可以自动执行此过程,为此,将 appex 文件放在 csproj 的扩展名中并将构建操作设置为 "content"。然后在这个 csproj 的文件中(你需要直接编辑)你可以添加这样的东西。 (在这种情况下,文件名为 Notifications.appex 并放置在名为 NativeExtension 的文件夹中)
<Target Name="BeforeCodeSign">
<ItemGroup>
<NativeExtensionDirectory Include="NativeExtension\Debug\**\*.*" />
</ItemGroup>
<!-- cleanup the application extension built with Xamarin (too heavy in memory)-->
<RemoveDir SessionId="$(BuildSessionId)"
Directories="bin\iPhone\Debug\Notifications.appex"/>
<!-- copy the native one, built in obj-c -->
<Copy
SessionId="$(BuildSessionId)"
SourceFiles="@(NativeExtensionDirectory)"
DestinationFolder="bin\iPhone\Debug\Notifications.appex"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="true"
Retries="3"
RetryDelayMilliseconds="300"/>
</Target>
这给了你一般的想法,但显然如果你想支持临时分发签名,iOS 应用程序商店分发签名你需要添加更多代码到这里(并且可能添加在 csproj 中,每个不同签名都有一个本机 appex 文件),我建议将此类 xml 代码放在单独的“.targets”文件中,并在 csproj 中使用条件调用目标。像这样:
<Target Name="BeforeCodeSign">
<CallTarget Targets="ImportExtension_Debug" Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' " />
<CallTarget Targets="ImportExtension" Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' " />
</Target>
如果其他人来到这里,原始发帖者的代码对我有用,并且提到的错误现在标记为已修复。如果我有一个提示,请不要在 Windows 上尝试这样做。您将陷入整个痛苦的世界,并且一事无成(实际上,它确实对我有用,一次!)。如果您尝试调试,还预计 Mac 上的 Visual Studio 会崩溃很多!
我对通知服务扩展有疑问。 我已经按照一步一步的文档 https://developer.xamarin.com/guides/ios/platform_features/introduction-to-ios10/user-notifications/enhanced-user-notifications/#Working_with_Service_Extensions
为了实现,我是那样做的。
- 添加了与我的应用程序具有相同前缀的通知服务扩展(添加后缀,例如:APP:com.testapp.main - EXT:com.testapp.main.notificationextension)
- 已创建 APPID 标识符 com.testapp.main.notificationextension 到 Apple 会员中心
- 已创建证书和配置文件以发送 APP ID com.testapp.main.notificationextension 的推送通知
- 已导入 Xcode 和 Xamarin 证书和配置
- 参考通知扩展参考构建我的应用程序。
- 已创建存档以上传到 TestFlight
- 已签名的应用及其分发证书和配置文件
- 带有分发证书和配置文件的签名扩展
- 已上传到 TestFlight
- 为我的应用程序下载并允许推送通知
- 使用 Localytics 仪表板发送丰富的推送通知以进行消息传递 - 设备收到推送通知但未通过 NotificationService.cs 通知服务扩展代码!
这是我的 NotificationService 代码:
using System;
using Foundation;
using UserNotifications;
namespace NotificationServiceExtension
{
[Register("NotificationService")]
public class NotificationService : UNNotificationServiceExtension
{
Action<UNNotificationContent> ContentHandler { get; set; }
UNMutableNotificationContent BestAttemptContent { get; set; }
const string ATTACHMENT_IMAGE_KEY = "ll_attachment_url";
const string ATTACHMENT_TYPE_KEY = "ll_attachment_type";
const string ATTACHMENT_FILE_NAME = "-localytics-rich-push-attachment.";
protected NotificationService(IntPtr handle) : base(handle)
{
// Note: this .ctor should not contain any initialization logic.
}
public override void DidReceiveNotificationRequest(UNNotificationRequest request, Action<UNNotificationContent> contentHandler)
{
System.Diagnostics.Debug.WriteLine("Notification Service DidReceiveNotificationRequest");
ContentHandler = contentHandler;
BestAttemptContent = (UNMutableNotificationContent)request.Content.MutableCopy();
if (BestAttemptContent != null)
{
string imageURL = null;
string imageType = null;
if (BestAttemptContent.UserInfo.ContainsKey(new NSString(ATTACHMENT_IMAGE_KEY)))
{
imageURL = BestAttemptContent.UserInfo.ValueForKey(new NSString(ATTACHMENT_IMAGE_KEY)).ToString();
}
if (BestAttemptContent.UserInfo.ContainsKey(new NSString(ATTACHMENT_TYPE_KEY)))
{
imageType = BestAttemptContent.UserInfo.ValueForKey(new NSString(ATTACHMENT_TYPE_KEY)).ToString();
}
if (imageURL == null || imageType == null)
{
ContentHandler(BestAttemptContent);
return;
}
var url = NSUrl.FromString(imageURL);
var task = NSUrlSession.SharedSession.CreateDownloadTask(url, (tempFile, response, error) =>
{
if (error != null)
{
ContentHandler(BestAttemptContent);
return;
}
if (tempFile == null)
{
ContentHandler(BestAttemptContent);
return;
}
var cache = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User, true);
var cachesFolder = cache[0];
var guid = NSProcessInfo.ProcessInfo.GloballyUniqueString;
var fileName = guid + ATTACHMENT_FILE_NAME + imageType;
var cacheFile = cachesFolder + fileName;
var attachmentURL = NSUrl.CreateFileUrl(cacheFile, false, null);
NSError err = null;
NSFileManager.DefaultManager.Move(tempFile, attachmentURL, out err);
if (err != null)
{
ContentHandler(BestAttemptContent);
return;
}
UNNotificationAttachmentOptions options = null;
var attachment = UNNotificationAttachment.FromIdentifier("localytics-rich-push-attachment", attachmentURL, options, out err);
if (attachment != null)
{
BestAttemptContent.Attachments = new UNNotificationAttachment[] { attachment };
}
ContentHandler(BestAttemptContent);
return;
});
task.Resume();
}
else {
ContentHandler(BestAttemptContent);
}
}
public override void TimeWillExpire()
{
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
ContentHandler(BestAttemptContent);
return;
}
}
}
您做的一切都正确,这是其他一些 xamarin 开发人员提出的问题。据我所知,一旦你 运行 NSURLSession 下载一些东西,即使它非常非常小,你也会超出此类扩展允许的内存限制。这很可能是 xamarin 特有的。 这是 bugzilla 的 link。 https://bugzilla.xamarin.com/show_bug.cgi?id=43985
我发现 work-around/hack 远非理想。我在 objective-c 中的 xcode 中重写了这个应用程序扩展(我想你也可以使用 swift)。这是一个相当小的 (1 class) 扩展。然后使用相同的代码签名证书/配置文件在 xcode 中构建它,然后在 xcode 的输出中找到 .appex 文件。
从那时起,您可以使用 "cheap way",并在退出和提交应用程序之前手动交换 .ipa 文件夹中的这个 .appex 文件。如果这对你来说足够好,你可以到此为止。
或者您可以自动执行此过程,为此,将 appex 文件放在 csproj 的扩展名中并将构建操作设置为 "content"。然后在这个 csproj 的文件中(你需要直接编辑)你可以添加这样的东西。 (在这种情况下,文件名为 Notifications.appex 并放置在名为 NativeExtension 的文件夹中)
<Target Name="BeforeCodeSign">
<ItemGroup>
<NativeExtensionDirectory Include="NativeExtension\Debug\**\*.*" />
</ItemGroup>
<!-- cleanup the application extension built with Xamarin (too heavy in memory)-->
<RemoveDir SessionId="$(BuildSessionId)"
Directories="bin\iPhone\Debug\Notifications.appex"/>
<!-- copy the native one, built in obj-c -->
<Copy
SessionId="$(BuildSessionId)"
SourceFiles="@(NativeExtensionDirectory)"
DestinationFolder="bin\iPhone\Debug\Notifications.appex"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="true"
Retries="3"
RetryDelayMilliseconds="300"/>
</Target>
这给了你一般的想法,但显然如果你想支持临时分发签名,iOS 应用程序商店分发签名你需要添加更多代码到这里(并且可能添加在 csproj 中,每个不同签名都有一个本机 appex 文件),我建议将此类 xml 代码放在单独的“.targets”文件中,并在 csproj 中使用条件调用目标。像这样:
<Target Name="BeforeCodeSign">
<CallTarget Targets="ImportExtension_Debug" Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' " />
<CallTarget Targets="ImportExtension" Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' " />
</Target>
如果其他人来到这里,原始发帖者的代码对我有用,并且提到的错误现在标记为已修复。如果我有一个提示,请不要在 Windows 上尝试这样做。您将陷入整个痛苦的世界,并且一事无成(实际上,它确实对我有用,一次!)。如果您尝试调试,还预计 Mac 上的 Visual Studio 会崩溃很多!