Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Real-Time Countdown Widget

Avatar for DevJonny DevJonny
October 21, 2025
6

Real-Time Countdown Widget

Avatar for DevJonny

DevJonny

October 21, 2025
Tweet

Transcript

  1. Introduce • ᷠࠃᲇᙻ • ࿆၀4ᢉ͝Λ᥀ᏣᫍΑ • ϧϕΩϰέϸνϐΧ |Suzuri} @ GMO

    ϟϖϡ • Apple Developer Academy @ POSTECH • ൃද༻ͷ೔ຊޠ͕·ͩԼखͰ͢
  2. Widgetを作りたいです! 👤 どんなWidget? 日 付を設定したら残りの時間が表 示 されるWidgetです! 👤 お! 日

    数だけ表 示 されればいいんですよね? いいえ!秒まで秒までライブで表 示 されるものです! 👤 ???????
  3. func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline<SimpleEntry>

    { var entries: [SimpleEntry] = [] let targetDate = SharedStorage.loadTargetDate() let currentDate = Date() for secondOffset in 0 ..< 60 { let entryDate = Calendar.current.date(byAdding: .second, value: secondOffset, to: currentDate)! let entry = SimpleEntry(date: entryDate, targetDate: targetDate, configuration: configuration) entries.append(entry) } return Timeline(entries: entries, policy: .atEnd) }
  4. var remainingTime: (days: Int, hours: Int, minutes: Int, seconds: Int)?

    { guard let targetDate = SharedStorage.loadTargetDate() else { return nil } let interval = targetDate.timeIntervalSince(entry.date) guard interval > 0 else { return nil } let totalSeconds = Int(interval) let days = totalSeconds / 86400 let hours = (totalSeconds % 86400) / 3600 let minutes = (totalSeconds % 3600) / 60 let seconds = totalSeconds % 60 return (days, hours, minutes, seconds) }
  5. 🟡: 今は正常に動作しているのですが、ある瞬間からエラ が発 生 してしまいます…。 ⚫: エラ が発 生 した時に、スクリ

    ンショットを送っていただけますか? 🟡: エラ が発 生 すると、ウィジェット 自 体がレンダリングされなくなって、 画 面 が黒、または 白 く表 示 されます。 調べたところ、秒単位のシンプルなライブ動作は可能ですが、 このようにカスタムして動作させるためには、複数のデ タタイムラインを 読み込む必要があります。 しかし、Appleのシステムではタイムラインのリロ ドに 15 60分かかるようで、 たとえ最短の15分でも、その間に約900件のデ タが 読み込まれてしまいます。 その結果、おそらく動作が途中で 止 まってしまうのだと思います。
  6. 🟡: 今は正常に動作しているのですが、ある瞬間からエラ が発 生 してしまいます…。 ⚫: エラ が発 生 した時に、スクリ

    ンショットを送っていただけますか? 🟡: エラ が発 生 すると、ウィジェット 自 体がレンダリングされなくなって、 画 面 が黒、または 白 く表 示 されます。 調べたところ、秒単位のシンプルなライブ動作は可能ですが、 このようにカスタムして動作させるためには、複数のデ タタイムラインを 読み込む必要があります。 しかし、Appleのシステムではタイムラインのリロ ドに 15 60分かかるようで、 たとえ最短の15分でも、その間に約900件のデ タが 読み込まれてしまいます。 その結果、おそらく動作が途中で 止 まってしまうのだと思います。
  7. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } }
  8. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24
  9. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24 00
  10. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24 00 1 1
  11. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24 00 1 1 22
  12. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24 00 1 1 22 33
  13. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24 00 1 1 22 33 44 55 66 77 99 99
  14. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24 00 1 1 22 33 44 55 66 77 99 99
  15. HStack (spacing: 0) { Spacer() VStack { Spacer() Text(endDate, style:

    .timer) .monospacedDigit() .multilineTextAlignment(.trailing) .font(.custom("PretendardJP-Bold", size: 26)) .mask( HStack { Spacer() ZStack { Text("00").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("11").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("22").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("33").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("44").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("55").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("66").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("77").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("88").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) Text("99").monospacedDigit().font(.custom("PretendardJP-Bold", size: 26)) } } ) } VStack { Spacer() Text("᰽") .font(.custom("PretendardJP-Bold", size: 26)) } } 23 : 32 : 24