Xamarin通知服务扩展问题

21

我在通知服务扩展方面遇到了问题。 我已经按照逐步文档进行了操作 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
  • 创建证书和配置文件以发送推送通知给APP ID com.testapp.main.notificationextension
  • 将Xcode和Xamarin证书和配置文件导入
  • 使用通知扩展引用构建我的应用程序。
  • 创建存档以上传到TestFlight
  • 使用其分发证书和配置文件签署应用程序
  • 使用其分发证书和配置文件签署扩展名
  • 上传到TestFlight
  • 下载并允许我的应用程序推送通知
  • 使用Localytics Dashboard发送富推送通知进行消息传递 - 设备接收推送通知但未通过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;
        }

    }
}

那么问题是什么? - Demitrian
设备接收推送通知,但未通过Notification Service Extension的NotificationService.cs代码。 - Luigi Saggese
1
“不通过”是什么意思?你的意思是DidReceiveNotificationRequest没有被执行吗? - Demitrian
准确。我也尝试仅更改DidReceiveNotificationRequest中推送的标题,但它没有被调用。 - Luigi Saggese
Yes是一个新项目(作为通知服务扩展创建),部署目标为10.0。在我的应用程序项目中,我已经添加了对项目扩展的引用(在.csproj文件中还有自动设置为true的xml标签<IsAppExtension>true</IsAppExtension>,然后被识别为扩展)。我的应用程序部署目标从8.1开始。我已经在iOS 10.2设备上安装了应用程序。 - Luigi Saggese
显示剩余4条评论
2个回答

5
您做得非常正确,这是一些其他Xamarin开发人员提出的问题。据我所知,只要您运行NSURLSession来下载任何东西,即使它非常小,您也会超过允许此类型扩展的内存限制。这很可能非常特定于Xamarin。以下是bugzilla的链接。 https://bugzilla.xamarin.com/show_bug.cgi?id=43985 我找到的解决方法/技巧远非理想。我在xcode中用objective-c(您也可以使用swift)重写了此应用程序扩展。它是一个相当小的(1个类)扩展。然后使用相同的代码签名证书/配置文件在xcode中构建它,然后在xcode的输出中找到.appex文件。
从那时起,您可以采取“廉价”的方式,只需在重新签名并提交应用程序之前手动交换.ipa文件夹中的此.appex文件即可。如果对您来说足够好,您可以在此停止。
或者,您可以自动化此过程。为此,请将appex文件放置在csproj的扩展中,并将构建操作设置为“内容”。然后,在此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中使用有条件的calltargets。代码如下:

<Target Name="BeforeCodeSign">
    <CallTarget Targets="ImportExtension_Debug" Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' " />
    <CallTarget Targets="ImportExtension" Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' " />
 </Target>

发布我的应用程序的存档->使用与签署Obj扩展的相同证书进行签名和分发。现在,我已解压缩ipa并替换了appex。将其压缩并压缩为ipa。现在,我已经使用此工具重新签署了应用程序(https://github.com/maciekish/iReSign)。也许重新签署扩展存在问题,因为我在安装应用程序时遇到了这个错误[_validateSignatureAndCopyInfoForURL:withOptions:error:]: 147:无法验证.../extracted/Payload/JRUITouch.app/PlugIns/NotificationServiceExtension.appex的代码签名:0xe8008001]。您知道要执行哪个命令来重新签署应用程序吗? - Luigi Saggese
如果您正在使用iResign,它只会签署整个ipa文件,但在此之前您需要先签署内部的appex,然后再使用iResign来签署完整的ipa包。以下是重新压缩ipa包之前可以运行的命令示例:codesign -f -s "证书颁发机构名称" -i "com.company.app.notifications" --entitlements "../ExtensionEntitlements.plist" "Payload/MyApp.app/PlugIns/Notifications.appex" - matfillion
当我重新签名后尝试在设备上安装时,出现了以下问题:Apr 28 09:41:16 iPhone installd(MobileSystemServices)[3541] <Notice>: 0x16dfef000 +[MICodeSigningVerifier _validateSignatureAndCopyInfoForURL:withOptions:error:]: 147: Failed to verify code signature of /private/var/installd/Library/Caches/com.apple.mobile.installd.staging/temp.QBb4HH/extracted/Payload/JRUITouch.app/PlugIns/NotificationServiceExtension.appex : 0xe8008001 (发生未知错误。) - Luigi Saggese
1
抱歉,这是我的问题,基于使用 Xcode 编译的扩展名字。如果使用相同的名称则可以完美运行。 - Luigi Saggese
@LuigiSaggese,您能详细说明一下您最后的评论吗,这样我们就可以避免这种情况了吗?听起来好像有些关于名称的问题可能会影响到其他人。 - Philipp Sumi
@PhilippSumi 的扩展名必须在 Xamarin 和 Xcode 之间编译时相同。 - Luigi Saggese

1
如果有其他人来到这里,原帖作者的代码对我有效,提到的错误现在已经标记为已修复。如果我有一个提示,不要在Windows上尝试这样做。你将会面临整个痛苦世界,并且一无所获(实际上,它曾经对我起作用过!)。如果你尝试调试,还应该预计Visual Studio在Mac上会频繁崩溃!

那你有什么建议呢?我不能在Visual Studio上的Xamarin扩展服务中拦截推送通知吗? - FreedomOfSpeech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接