Zenn
🐞

Expo Calendarで繰り返しイベントを取得するときの不具合に対応した

2025/03/02に公開

Expo Calendarで繰り返しイベントを取得しようとすると、iOS版で正常に動作しなかったです。
仕様なのか、バグなのかは分かりませんが、Issueはありましたが、特に対応はされず閉じられていました。

https://github.com/expo/expo/issues/31668

プルリクを送るか悩みましたが、正直なところ、Expoほどの巨大リポジトリで、コンテキストもあまり理解していない状態で、英語でコミュニケーション取りながらやる自信がなかったので、パッチを当てることにしました。

不具合の内容

以下のようにgetEventAsyncを使って、繰り返しイベントを取得しようとすると、引数に指定したinstanceStartDateの値を無視して、繰り返しイベントの最初のインスタンスの情報を取得してしまいます。

コード例

const event = await Calendar.getEventAsync(eventId, {
  instanceStartDate: "2025-03-02T07:00:00.000Z",
});

期待する動きは、繰り返しイベントの内、引数に指定したinstanceStartDateの値と同じstartDateを持つイベントが取得できることです。が、そうはなっていません。

実行結果

前述のコードを実行した場合、次のような結果が返ってきます。

{"alarms": [], "allDay": false, "availability": "notSupported", "calendarId": "28982EB5-F6F1-4FD0-B91B-E3E07D6B0EB8", "creationDate": "2025-03-01T07:54:02.743Z", "endDate": "2025-03-01T08:00:00.000Z", "id": "2750D93A-D17B-458C-A611-0B8CB8D67644", "isDetached": false, "lastModifiedDate": "2025-03-01T07:54:02.743Z", "originalStartDate": "2025-03-01T07:00:00.000Z", "recurrenceRule": {"frequency": "daily", "interval": 1}, "startDate": "2025-03-01T07:00:00.000Z", "status": "none", "timeZone": "JST", "title": "繰り返しイベント", "url": ""}

startDate2025-03-02T07:00:00.000Zではなく、2025-03-01T07:00:00.000Zになっています。

取得処理の修正

今回はpnpm patchを使ってパッチを当てることにしました。

パッチを当てる前の処理は、次の通りです。

Expo Calendarのバージョン13.0.5や執筆時点で最新の14.0.6でも問題があるのを確認しています。

https://github.com/expo/expo/blob/c46aeff68ecf6747d792b530482f2c88111cff39/packages/expo-calendar/ios/CalendarModule.swift#L495

private func getEvent(with id: String, startDate: Date?) -> EKEvent? {
  guard let firstEvent = eventStore.calendarItem(withIdentifier: id) as? EKEvent else {
    return nil
  }

  guard let startDate else {
    return firstEvent
  }

  // 問題があるのはここ。
  // firstEvent.startDate = 繰り返しイベントの最初のインスタンスのstartDate
  // startDate = instanceStartDate
  // この2つが異なる場合、最初のインスタンスを返すという処理になっている
  guard let firstEventStart = firstEvent.startDate, firstEventStart.compare(startDate) == .orderedSame else {
    return firstEvent
  }

  let endDate = startDate.addingTimeInterval(2_592_000)
  let events = eventStore.events(
    matching: eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: [firstEvent.calendar])
  )

  for event in events {
    if event.calendarItemIdentifier != id {
      break
    }
    if let eventStart = event.startDate, eventStart.compare(startDate) == .orderedSame {
      return event
    }
  }
  return nil
}

ちなみに、元々Objective-Cで書かれていたのをSwiftに移行したときに、guard文に変えたようです。

https://github.com/expo/expo/pull/24282

Objective-Cのコードは、以下の通りです。

https://github.com/expo/expo/blob/2d0a606b493f45392d1ae254fcb45fddb8e4003a/packages/expo-calendar/ios/EXCalendar/EXCalendar.m#L804

パッチについて

ということで、パッチです。

diff --git a/ios/CalendarModule.swift b/ios/CalendarModule.swift
index 96cb1b74f636871b048c6b3bc666b86b08ff87a0..705b6328837c10591753146ea2678b8fcf98dd97 100644
--- a/ios/CalendarModule.swift
+++ b/ios/CalendarModule.swift
@@ -501,7 +501,7 @@ public class CalendarModule: Module {
       return firstEvent
     }
 
-    guard let firstEventStart = firstEvent.startDate, firstEventStart.compare(startDate) == .orderedSame else {
+    if let firstEventStart = firstEvent.startDate, firstEventStart.compare(startDate) == .orderedSame {
       return firstEvent
     }

簡単にいうと

guardifに変える」

これだけです。

あとは、パッチを当てるだけです。

pnpm patch expo-calendar
# .pnpm-patch/にファイルが展開されるので、編集してpatch-commitを実行
pnpm patch-commit 'path/to/expo-calendar@13.0.5'

Swiftのコードへの変更なので、ビルドが必要なのでお忘れなく。

おわりに

Expo Calendarのパッチを当てることで、iOS版で繰り返しイベントを扱うときの不具合に対応できました。

どこかでExpo側で対応されたら良いなと思いつつ、その場合はおそらく最新のExpoへの適用になると思うので、Expo本体のバージョンアップが必要になり…という未来が見えるので、なんとも言い難いです。

参考になれば幸いです。それでは。

Discussion

ログインするとコメントできます