Live Activity با Expo: از کلاینت تا بکاند
خلاصهٔ کاملتر
Live Activity (یعنی همون کارت زندهای که روی Lock Screen و Dynamic Island آیفون نشون داده میشه) معمولاً برای سفارش غذا یا نتیجهی بازی استفاده میشه. تیم Expo تو این مقاله یه کاربرد آرومتر رو ساخته: کاربر یه رسید فوروارد میکنه و پردازشش روی سرور از چند ثانیه تا یه دقیقه طول میکشه. به جای spinner یا یه نوتیفیکیشن تکی، کاربر مراحل دریافت، پردازش و نتیجهی نهایی رو زنده میبینه.
جریان کار اینجوریه: Activity یا محلی شروع میشه یا با push-to-start (یعنی سرور خودش از راه دور Activity رو روی گوشی روشن میکنه). بعد iOS یه push token مخصوص همون Activity میسازه که با توکن معمولی دستگاه فرق داره و ممکنه عوض بشه. اپ این توکن رو به بکاند میده، بکاند رویدادهای خودش رو به ساختاری که ویجت میخواد تبدیل میکنه و از طریق APNs میفرسته. آپدیت رو خود iOS رندر میکنه و هیچ کدی از اپ اجرا نمیشه.
سمت کلاینت، ActivityKit برای کل عمر Activity فقط یه شکل ثابت از ContentState قبول میکنه. واسه همین اسکیمای مقاله یه هدر همیشگی داره (مرحله، پیشرفت، فرستنده) بهعلاوهی سه آبجکت اختیاری که هر بار فقط یکیشون پره. خود ویجت با @expo/ui/swift-ui به شکل یه کامپوننت React نوشته شده و expo-widgets اون رو به یه view واقعی SwiftUI کامپایل میکنه. یعنی دیگه فایل Swift جداگونهای نداری که نگهش داری.
push-to-start به iOS 17.2 به بالا نیاز داره، در حالی که خود ActivityKit از 16.1 هست. پس روی دستگاه قدیمیتر فقط باید این قابلیت غیرفعال بشه، نه اینکه اپ کرش کنه. تیم Expo یه فیلد attributes.activityId هم اضافه کرده تا توکن هر Activity به رکورد درست تو بکاند وصل بشه. یه listener سطح ماژول هم داره برای وقتی که Activity از راه دور شروع شده و اپ تو پسزمینهست ولی بسته نشده؛ بدون اون، توکن هیچوقت به سرور نمیرسه.
سمت بکاند یه جدول برای توکنها هست و دو endpoint که هر دو upsert سادهان؛ توکن تکراری کاری نمیکنه و توکن جدید یه ردیف تازه میسازه. یه تابع mapper رویدادهای دامنه رو به اسکیما تبدیل میکنه و موقع ارسال، وضعیت رو دوباره از دیتابیس میخونه تا اگه jobها نامرتب برسن، همیشه آخرین وضعیت درست فرستاده بشه. یه timestamp مثل startedAt هم جلوی ساخته شدن Activity تکراری موقع retry صف رو میگیره.
به گفتهی نویسندهها چند تا دام APNs کلی وقتشون رو گرفت. content-state در واقع یه wrapper با دو فیلد name و props هست و props باید کل اسکیما به شکل یه رشتهی JSON باشه؛ وگرنه APNs جواب 200 میده ولی Activity هیچوقت آپدیت نمیشه. برای رویداد start هم فیلد alert عملاً اجباریه. حالتهای نهایی هم با event از نوع update فرستاده میشن نه end، تا نتیجه روی Lock Screen بمونه.
نکات کلیدی:
- push-to-start به iOS 17.2 به بالا نیاز داره، ولی ActivityKit از iOS 16.1 در دسترسه
- push token هر Live Activity جداست، با توکن دستگاه فرق داره و ممکنه عوض بشه
- هدر apns-topic باید پسوند .push-type.liveactivity داشته باشه و apns-push-type: liveactivity هم جدا لازمه
- جواب 410 یا BadDeviceToken از APNs یعنی توکن مرده و باید پاک بشه
- توکنها به entitlement aps-environment بیلد وابستهان، نه به Debug یا Release
- بخشی از اصلاحات تیم تو PR #48589 به expo-widgets رفته




