Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Real-Time Countdown Widget
Search
DevJonny
October 21, 2025
450
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Real-Time Countdown Widget
DevJonny
October 21, 2025
More Decks by DevJonny
See All by DevJonny
Machine Learning with Create ML
devjonny
0
270
Featured
See All Featured
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
The untapped power of vector embeddings
frankvandijk
2
1.9k
sira's awesome portfolio website redesign presentation
elsirapls
0
450
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
360
KATA
mclloyd
PRO
35
16k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
2
950
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.7k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Become a Pro
speakerdeck
PRO
31
6.3k
Into the Great Unknown - MozCon
thekraken
41
2.8k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
Transcript
Real-Time Countdown Wigdet Jonny, Mobile Engineer @ GMOϟϖϡ
Introduce • ᷠࠃᲇᙻ • ࿆၀4ᢉ͝Λ᥀ᏣᫍΑ • ϧϕΩϰέϸνϐΧ |Suzuri} @ GMO
ϟϖϡ • Apple Developer Academy @ POSTECH • ൃද༻ͷຊޠ͕·ͩԼखͰ͢
None
Widgetを作りたいです! 👤 どんなWidget? 日 付を設定したら残りの時間が表 示 されるWidgetです! 👤 お! 日
数だけ表 示 されればいいんですよね? いいえ!秒まで秒までライブで表 示 されるものです! 👤 ???????
None
やってみます
None
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) }
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) }
None
None
None
None
None
None
None
None
None
None
23時間 32分 24秒
23 : 32 : 24
None
None
23 : 32 : 24
23 : 32 : 24
23 : 32 : 24᰽
23 : 32 : 24᰽ 23 : 32 : 24
23 : 32 : 24᰽ 23 : 32 : 24
23 : 32 : 24᰽ 23 : 32 : 24
ᐠ
23 : 32 : 24᰽ 23 : 32 : 24
23 : 32 : 24᰽ 23 : 32 : 24
23 : 32 : 24᰽ 23 : 32 : 24
None
🟡: 今は正常に動作しているのですが、ある瞬間からエラ が発 生 してしまいます…。 ⚫: エラ が発 生 した時に、スクリ
ンショットを送っていただけますか? 🟡: エラ が発 生 すると、ウィジェット 自 体がレンダリングされなくなって、 画 面 が黒、または 白 く表 示 されます。 調べたところ、秒単位のシンプルなライブ動作は可能ですが、 このようにカスタムして動作させるためには、複数のデ タタイムラインを 読み込む必要があります。 しかし、Appleのシステムではタイムラインのリロ ドに 15 60分かかるようで、 たとえ最短の15分でも、その間に約900件のデ タが 読み込まれてしまいます。 その結果、おそらく動作が途中で 止 まってしまうのだと思います。
🟡: 今は正常に動作しているのですが、ある瞬間からエラ が発 生 してしまいます…。 ⚫: エラ が発 生 した時に、スクリ
ンショットを送っていただけますか? 🟡: エラ が発 生 すると、ウィジェット 自 体がレンダリングされなくなって、 画 面 が黒、または 白 く表 示 されます。 調べたところ、秒単位のシンプルなライブ動作は可能ですが、 このようにカスタムして動作させるためには、複数のデ タタイムラインを 読み込む必要があります。 しかし、Appleのシステムではタイムラインのリロ ドに 15 60分かかるようで、 たとえ最短の15分でも、その間に約900件のデ タが 読み込まれてしまいます。 その結果、おそらく動作が途中で 止 まってしまうのだと思います。
None
None
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)) } }
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
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
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
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
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
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
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
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
None
None
None
Thank you! Real-Time Countdown Wigdet JeEon Park / Jonny GMO
Pepabo X: @devjonny_