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
460
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
280
Featured
See All Featured
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.8k
Practical Orchestrator
shlominoach
192
12k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
The Curse of the Amulet
leimatthew05
3
16k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
What does AI have to do with Human Rights?
axbom
PRO
1
2.5k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
600
Skip the Path - Find Your Career Trail
mkilby
1
280
Public Speaking Without Barfing On Your Shoes - THAT 2023
reverentgeek
1
660
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
2
1.2k
4 Signs Your Business is Dying
shpigford
187
23k
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_