Выпуск расширения службы уведомления Xamarin
У меня проблема с расширением службы уведомлений. Я выполнил пошаговую документацию 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 в Member Center of Apple
- Создал сертификат и профиль подготовки, чтобы отправить push-уведомление для APP ID. Com.testapp.main.notificationextension
- Импортировано в Xcode и Xamarin сертификат и обеспечение
- Создайте мое приложение со ссылкой на ссылку Расширение уведомлений.
- Созданный архив для загрузки в TestFlight
- Подписанное приложение с его сертификатом распространения и профилем Provisioning Profile
- Подписанное расширение с его сертификатом распространения и профилем Provisioning Profile
- Загружено в TestFlight
- Загрузка и разрешение push-уведомления для моего приложения
- Отправленное насыщенное уведомление с помощью Localityics Dashboard для обмена сообщениями - устройство получает push-уведомление, но не передает код NotificationService.cs службы Notification Service Extension!
Это мой код 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;
}
}
}
Ответы
Ответ 1
Вы делаете все правильно, это проблема, поднятая несколькими другими разработчиками xamarin. Из того, что я могу сказать, как только вы запустите NSURLSession, чтобы загрузить что-то, даже если оно супер супер мало, вы перейдете к пределу памяти, разрешенному для этого типа расширения. Это, скорее всего, очень специфично для ксамина. Вот ссылка на bugzilla. https://bugzilla.xamarin.com/show_bug.cgi?id=43985
Обход/хак, который я нашел, далек от идеала. Я переписал это расширение приложения в xcode в объективе-c (вы можете использовать swift тоже, я полагаю). Это довольно небольшое (1 класс) расширение. Затем построил его в xcode, используя тот же сертификат сертификата/профиля подписи кода, а затем нашел файл.appex в выводе xcode.
С этого момента вы можете взять "дешевый способ" и поменять этот файл.appex в своей.ipa-папке вручную перед отставкой и отправкой приложения. Если это будет достаточно для вас, вы можете остановиться здесь.
Или вы можете автоматизировать этот процесс, чтобы таким образом поместить файл appx в расширение 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>
Это дает вам общую идею, но, очевидно, если вы хотите поддерживать подписку ad-hoc-дистрибутива, подписку на дистрибутив App Store, вам нужно будет добавить в нее немного больше кода (и, возможно, добавить в csproj собственный файл-приманку для каждого другая подпись), я бы предложил разместить такой XML-код в отдельный файл ".targets" и использовать условные calltargets в csproj. Как это:
<Target Name="BeforeCodeSign">
<CallTarget Targets="ImportExtension_Debug" Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' " />
<CallTarget Targets="ImportExtension" Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' " />
</Target>
Ответ 2
Если кто-то еще сюда приходит, код оригинального плаката работает для меня, и упоминание об ошибке теперь отмечено как фиксированное. Если у меня есть один совет, не пытайтесь делать это в Windows. Вы станете жертвой целого мира боли и никуда не денутся (на самом деле, это сработало для меня, когда-то!). Также ожидайте, что Visual Studio на Mac будет разбиваться, много, если вы попытаетесь отладить!