Live Activity למעקב אחר מחירי קריפטו ב-iOS
דמיינו סוחר שרואה שביטקוין צונח בחדות אבל לא יכול לפתוח את האפליקציה מיד—המסך נעול. עד שיפתחו את האייפון, המחיר עלול לזוז עוד כמה אחוזים. Live Activity פותר את זה: המחיר הנוכחי והשינוי ב-24 שעות מוצגים ישירות על מסך הנעילה וב-Dynamic Island (אייפון 14 Pro ומעלה). המשתמש רואה את התנועה בלי לחיצה אחת. יישמנו את זה ביותר מ-15 פרויקטים של מעקב קריפטו—הנה הארכיטקטורה.
למה Live Activity במקום ווידג'ט רגיל?
ווידג'טים של מסך הבית מתעדכנים כל 15–60 דקות, וזה לא מקובל עבור מטבעות קריפטו. התראות push דורשות תשומת לב ויכולות להיות מעצבנות. Live Activity הוא נקודת האיזון: נתונים מתעדכנים כל כמה שניות (באמצעות ActivityKit Push), תופסים מקום מינימלי, ולא דורשים פעולה מהמשתמש. זה מאפשר לסוחרים להגיב לשינויים מהר יותר—הנתונים שלנו מראים ירידה של 40% בזמן התגובה.
ActivityKit: מגבלות עיקריות
Live Activity נוצר באמצעות ActivityKit. לפני שמתחילים בפיתוח, צריך להבין מגבלה בסיסית: ניתן להתחיל Activity רק מהאפליקציה עצמה כשהיא בחזית. אי אפשר להתחיל Activity מתהליך רקע או מהודעת push. עדכונים אפשריים דרך ה-API של ActivityKit או דרך push (ActivityKit Push Update).
משך החיים המקסימלי של Activity אחד הוא 12 שעות (המערכת עשויה לסיים אותו מוקדם יותר). ActivityAttributes הוא סטטי לכל משך החיים. ContentState הוא דינמי—הוא מתעדכן.
עבור ווידג'ט קריפטו, הארכיטקטורה נראית כך:
struct CryptoActivityAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
var price: Double
var change24h: Double
var lastUpdated: Date
}
var symbol: String // статично: BTC, ETH и т.д.
var baseCurrency: String // статично: USD
} התחלה ועדכון
let attributes = CryptoActivityAttributes(symbol: "BTC", baseCurrency: "USD")
let initialState = CryptoActivityAttributes.ContentState(
price: 67_430.0,
change24h: 2.3,
lastUpdated: .now
)
let activity = try Activity<CryptoActivityAttributes>.request(
attributes: attributes,
content: .init(state: initialState, staleDate: Date().addingTimeInterval(60)),
pushType: .token // если планируем обновлять через push
)struct CryptoActivityAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { var price: Double var change24h: Double var lastUpdated: Date } var symbol: String // статично: BTC, ETH и т.д. var baseCurrency: String // статично: USD } הוא כאשר המערכת מחשיבה נתונים כלא עדכניים ועשויה להציג ממשק מיוחד. עבור מחירי קריפטו, הגדירו אותו ל-60–120 שניות.
עדכון מקומי בקוד:
let updatedState = CryptoActivityAttributes.ContentState(
price: newPrice,
change24h: newChange,
lastUpdated: .now
)
await activity.update(.init(
state: updatedState,
staleDate: Date().addingTimeInterval(60)
)) איך עובדים עדכוני push דרך ActivityKit?
לעדכוני מחיר בזמן אמת, צריך backend ששולח ActivityKit Push Notifications—סוג push נפרד, לא APNs. ה-payload נראה כך:
{
"aps": {
"timestamp": 1699000000,
"event": "update",
"content-state": {
"price": 68100.0,
"change24h": 2.8,
"lastUpdated": 1699000000
},
"alert": {
"title": "BTC",
"body": "$68,100"
}
}
}ה-token ל-ActivityKit Push נפרד מה-token הרגיל של APNs. האפליקציה מקבלת אותו דרך let attributes = CryptoActivityAttributes(symbol: "BTC", baseCurrency: "USD") let initialState = CryptoActivityAttributes.ContentState( price: 67_430.0, change24h: 2.3, lastUpdated: .now ) let activity = try Activity<CryptoActivityAttributes>.request( attributes: attributes, content: .init(state: initialState, staleDate: Date().addingTimeInterval(60)), pushType: .token // если планируем обновлять через push ) וחייבת לשלוח אותו לשרת. אם ה-token לא מתעדכן אחרי אתחול של Activity, עדכוני push מפסיקים להגיע.
Dynamic Island: תצוגות קומפקטיות ומורחבות
פריסת SwiftUI ל-Dynamic Island מחולקת למספר תצוגות: staleDate, let updatedState = CryptoActivityAttributes.ContentState( price: newPrice, change24h: newChange, lastUpdated: .now ) await activity.update(.init(state: updatedState, staleDate: Date().addingTimeInterval(60))) , { "aps": { "timestamp": 1699000000, "event": "update", "content-state": { "price": 68100.0, "change24h": 2.8, "lastUpdated": 1699000000 }, "alert": { "title": "BTC", "body": "$68,100" } } } , activity.pushTokenUpdates. כל אחת היא תצוגת SwiftUI נפרדת. יש מגבלת גודל קפדנית לתצוגות קומפקטיות—רק כמה פיקסלים, אין רשימות.
.dynamicIsland { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading) {
Text(context.attributes.symbol).font(.headline)
}
DynamicIslandExpandedRegion(.trailing) {
Text(context.state.change24h >= 0 ? "↑" : "↓")
.foregroundColor(context.state.change24h >= 0 ? .green : .red)
}
DynamicIslandExpandedRegion(.center) {
Text("$\(context.state.price, format: .number.precision(.fractionLength(2)))")
.font(.title2)
}
} compactLeading: {
Text(context.attributes.symbol).font(.caption2)
} compactTrailing: {
Text("$\(Int(context.state.price))").font(.caption2)
} minimal: {
Text(context.attributes.symbol.prefix(1))
}
} | שיטת עדכון | זמן השהיה | דורש שרת | מצב לא מקוון |
|---|---|---|---|
| מקומי (עדכון) | מיידי | לא | כן |
| עדכון push | 1–5 שניות | כן | לא |
איך לעדכן נתונים בלי שרת?
אם האפליקציה כבר משתמשת ב-WebSocket או בערוץ זמן אמת אחר, אפשר לעדכן את ה-Activity ישירות: לקבל מחיר חדש, לקרוא ל-compactLeading. זה פשוט וזול יותר מהקמת תשתית ActivityKit Push. החיסרון הוא שהאפליקציה חייבת להיות פעילה לפחות ברקע (Background fetch או WebSocket עם keep-alive). למעקב קריפטו, אנחנו בדרך כלל משלבים: עדכונים מקומיים לנתונים מיידיים ו-push כערוץ גיבוי.
טעויות נפוצות ואיך להימנע מהן
בעיות נפוצות ביישום Live Activity
- שכחנו לעדכן את ה-push token – אם ה-Activity מתאתחל, ה-token משתנה. השרת חייב לטפל בעדכונים, אחרת הודעות push מפסיקות להגיע.
-
compactTrailingגדול מדי – עם מחירי קריפטו, יותר מ-2 דקות של חוסר עדכניות מציג נתונים שגויים. הגדירו אותו ל-60 שניות. - לא מטפלים בסיום מערכת – אם המערכת מסיימת את ה-Activity לפני 12 שעות (למשל, זיכרון נמוך), האפליקציה צריכה להגיב ולהפעיל מחדש את ה-Activity בכניסה הבאה לחזית.
מה לשקול כשמתחילים Activity?
- Activity יכול להתחיל רק בחזית – זו מגבלה מרכזית. תכננו את לוגיקת האתחול כשהמשתמש נכנס לאפליקציה.
- השתמשו ב-
minimalבחוכמה: 60–90 שניות הוא אופטימלי עבור מטבעות קריפטו. - ודאו שהשרת יכול לטפל בשינויי push token.
מה כלול בעבודה
- יצירת הרחבת ActivityKit עם
expandedו-.dynamicIsland { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { Text(context.attributes.symbol).font(.headline) } DynamicIslandExpandedRegion(.trailing) { Text(context.state.change24h >= 0 ? "↑" : "↓") .foregroundColor(context.state.change24h >= 0 ? .green : .red) } DynamicIslandExpandedRegion(.center) { Text("$\(context.state.price, format: .number.precision(.fractionLength(2)))") .font(.title2) } } compactLeading: { Text(context.attributes.symbol).font(.caption2) } compactTrailing: { Text("$\(Int(context.state.price))").font(.caption2) } minimal: { Text(context.attributes.symbol.prefix(1)) } } - פריסת SwiftUI למסך נעילה, Dynamic Island (קומפקטי, מינימלי, מורחב)
- התחלה וסיום של Activity מהאפליקציה הראשית
- הגדרת עדכוני ActivityKit Push (דורש צד שרת)
- טיפול בנתונים לא עדכניים (
activity.update(_:)) - בדיקות באייפונים עם ובלי Dynamic Island
לוחות זמנים
2–3 ימים לחלק ה-UI עם עדכונים מקומיים. אינטגרציה עם שרת לעדכוני push – בתוספת 1–2 ימים. העלות מחושבת באופן אישי לאחר ניתוח הדרישות.
אנחנו מעריכים את הפרויקט שלכם – צרו קשר לייעוץ. יישמנו Live Activity ביותר מ-15 פרויקטים, כולל ארנקי קריפטו ומסופי מסחר. אנחנו מבטיחים עמידה בהנחיות בדיקת ה-App Store ואופטימיזציה של יעילות אנרגטית. הזמינו אב טיפוס של Live Activity היום – קבלו ייעוץ ממהנדס.







