Как отправить локальный файл в качестве вложения в Expo Notifications для IOS?

Вопрос или проблема

Похож на UNNotificationAttachment не удается прикрепить изображение, но специфично для Expo.

Основываясь на вопросе, который я упомянул, предполагается, что нужно иметь отдельный файл. Итак, вот что я сделал.

  const [localAssets] = useAssets(localAttachments ?? []);
  const [assets, setAssets] = useState<Asset[]>([]);

  useEffect(() => {
    let mounted = true;
    (async () => {
      if (!localAssets) {
        return;
      }
      const uuid = Crypto.randomUUID();
      const nextAssets = _.cloneDeep(localAssets.filter((it) => it.downloaded));
      for (const asset of nextAssets) {
        const tempLocalUri = `${FileSystem.cacheDirectory}${asset.hash}.${uuid}.${asset.type}`;
        await FileSystem.copyAsync({
          from: asset.localUri!,
          to: tempLocalUri,
        });

        asset.localUri = tempLocalUri;
      }
      if (mounted) {
        setAssets(nextAssets);
      }
    })();
    return () => {
      mounted = false;
    };
  }, [localAssets]);

Где я сделал копию актива в другое место и обновил значение localUri. Затем я собираю данные и отправляю уведомление, используя эти useMemo и обратный вызов.

  const attachments = useMemo<
    Notifications.NotificationContentAttachmentIos[]
  >(() => {
    if (!assets) {
      return [];
    }
    return assets
      .filter((it) => it)
      .map((it) => ({
        identifier: it.hash,
        type: `public.${it.type}`,
        typeHint: `public.${it.type}`,
        url: it.localUri,
        hideThumbnail: false,
      }));
  }, [assets]);

  const content = useMemo<Notifications.NotificationContentInput>(
    () => ({
      ...notificationPayload,
      attachments: attachments,
    }),
    [notificationPayload, attachments],
  );

  const onSendNotification = useCallback(() => {
    (async () => {
      const request: Notifications.NotificationRequestInput = {
        content: content,
        trigger: {
          date: Date.now() + 2_000,
        },
      };
      try {
        console.debug(attachments);
        await Notifications.scheduleNotificationAsync(request);
      } catch (error) {
        console.error(error);
      }
    })();
  }, [content]);

Результат следующего отладки показывает UUID и является URI файла. У меня также есть <Image />, который подтверждает, что он отображается как изображение.

[{"hideThumbnail": false, 
  "identifier": "633435dcb418833920a16771610ca404", 
  "type": "public.png", 
  "typeHint": "public.png", 
  "url": "file:///var/mobile/Containers/Data/Application/110DC7BD-A51E-44D2-A072-8C75BC8F1E3E/Library/Caches/633435dcb418833920a16771610ca404.3acf6d50-e0f0-4d5e-ad55-13b030479460.png"}]

и сообщение об ошибке

[Ошибка: Не удалось запланировать уведомление. Ошибка Domain=UNErrorDomain Код=100 “Недопустимый URL-адрес файла вложения” UserInfo={NSLocalizedDescription=Недопустимый URL-адрес файла вложения}]

Также попытка использовать react-native-fs, чтобы предоставить TempDirectoryPath, и копирование также не работает.

const tempLocalUri = `file://${RNFS.TemporaryDirectoryPath}${asset.hash}.${uuid}.${asset.type}`;

let target = `${RNFS.TemporaryDirectoryPath}${asset.hash}.${uuid}.${asset.type}`;
if (await RNFS.exists(target)) {
  await RNFS.unlink(target);
}
await RNFS.copyFile(
  asset.localUri!.substring('file://'.length),
  target,
);
asset.localUri = tempLocalUri;

Код на https://github.com/trajano/expo-experiments/blob/test-expand-idea/packages/my-app/src/stories/Notification.stories.tsx

Ответ или решение

Как отправить локальный файл в Expo Notifications на iOS с использованием вложений

В данной инструкции мы рассмотрим, как отправить локальные вложения (например, изображения) через Expo Notifications на платформе iOS. Мы обратим особое внимание на возможные ошибки при отправке уведомлений, включая ошибку "Invalid attachment file URL".

1. Подготовка локальных файлов

Перед отправкой уведомления нам нужно убедиться, что локальные файлы доступны и корректно подготовлены. Для этого мы используем библиотеку FileSystem от Expo для работы с файловой системой. Ваша задача — создать копии локальных файлов в кэше приложения. В приведенном вами коде есть хороший пример этого процесса:

const [localAssets] = useAssets(localAttachments ?? []);
const [assets, setAssets] = useState<Asset[]>([]);

useEffect(() => {
  let mounted = true;
  (async () => {
    if (!localAssets) {
      return;
    }
    const uuid = Crypto.randomUUID();
    const nextAssets = _.cloneDeep(localAssets.filter((it) => it.downloaded));
    for (const asset of nextAssets) {
      const tempLocalUri = `${FileSystem.cacheDirectory}${asset.hash}.${uuid}.${asset.type}`;
      await FileSystem.copyAsync({
        from: asset.localUri!,
        to: tempLocalUri,
      });
      asset.localUri = tempLocalUri;
    }
    if (mounted) {
      setAssets(nextAssets);
    }
  })();
  return () => {
    mounted = false;
  };
}, [localAssets]);

В этом коде мы проверяем, доступны ли локальные файлы, создаем их копии в кэше и обновляем значение localUri.

2. Формирование вложений для уведомления

После того как вы подготовили локальные файлы, следующим шагом будет формирование массива вложений для уведомления:

const attachments = useMemo<Notifications.NotificationContentAttachmentIos[]>(() => {
  if (!assets) {
    return [];
  }
  return assets
    .filter((it) => it)
    .map((it) => ({
      identifier: it.hash,
      type: `public.${it.type}`,
      typeHint: `public.${it.type}`,
      url: it.localUri,
      hideThumbnail: false,
    }));
}, [assets]);

Этот код собирает все необходимые данные, включая URL локального файла, для формирования массива вложений, который будет использоваться в уведомлении.

3. Отправка уведомления

Теперь, когда все подготовлено, мы можем отправить уведомление:

const content = useMemo<Notifications.NotificationContentInput>(
  () => ({
    ...notificationPayload,
    attachments: attachments,
  }),
  [notificationPayload, attachments],
);

const onSendNotification = useCallback(() => {
  (async () => {
    const request: Notifications.NotificationRequestInput = {
      content: content,
      trigger: {
        date: Date.now() + 2_000,
      },
    };
    try {
      console.debug(attachments);
      await Notifications.scheduleNotificationAsync(request);
    } catch (error) {
      console.error(error);
    }
  })();
}, [content]);

Обратите внимание, что во время выполнения этой функции, можем столкнуться с ошибкой "Invalid attachment file URL".

4. Общие проблемы и их решения

  1. Проверка URL: Убедитесь, что URL файла имеет правильный формат. Он должен начинаться с file://. Ваш код показывает, что URL правильный, но наличие такой ошибки может указывать на проблемы с доступом к файлу в кэше.

  2. Проверка наличия файлов: Убедитесь, что файлы действительно существуют по указанному пути. Используйте отладочные сообщения для вывода значений и проверки существования файла.

  3. Использование RNFS: Если вы решили использовать библиотеку react-native-fs, убедитесь, что путь к временным файлам также формируется корректно. Визуальный осмотр кода, который вы предоставили, показывает, что вы используете правильный метод копирования, но стоит убедиться в том, что файл доступен перед отправкой:

const tempLocalUri = `file://${RNFS.TemporaryDirectoryPath}${asset.hash}.${uuid}.${asset.type}`;

let target = `${RNFS.TemporaryDirectoryPath}${asset.hash}.${uuid}.${asset.type}`;
if (await RNFS.exists(target)) {
  await RNFS.unlink(target);
}
await RNFS.copyFile(
  asset.localUri!.substring('file://'.length),
  target,
);
asset.localUri = tempLocalUri;

Заключение

Следуйте этим шагам для успешной отправки локальных вложений через Expo Notifications на iOS. Убедитесь, что все локальные файлы правильно скопированы и доступны по правильным путям, а также используйте отладку для выявления потенциальных проблем. Поскольку работа с файлами в iOS требует аккуратности, убедитесь, что ресурсы правильно формируются и вызываются.

Оцените материал
Добавить комментарий

Капча загружается...