מדוע Push שקט נכשל: מקרים אמיתיים
Push שקט הוא כלי עוצמתי לסנכרון רקע, אך היישום שלו מלא במלכודות. לפי סטטיסטיקות, הגדרה לא נכונה של push שקט מובילה ל-40% מהתראות שהוחמצו באנדרואיד ועד 60% ב-iOS בתרחישים מורכבים. אחד הלקוחות שלנו, שירות משלוחים עם 500,000 משתמשים, נתקל בבעיה שבה לאחר עדכון לאנדרואיד 13, התראות רקע הפסיקו להעיר את האפליקציה — מצב Doze עם הגבלות חדשות חסם הודעות בעדיפות גבוהה בגלל היעדר קריאה ל-setForegroundAsync ב-worker מואץ. הגדרנו מחדש את הטיפול והוספנו את הקריאה — הבעיה נפתרה. במאמר זה, נפרק כיצד להגדיר באופן אמין סנכרון רקע ללא אינטראקציה של המשתמש ונראה כיצד להימנע מטעויות נפוצות. הניסיון שלנו: 10+ שנים בפיתוח מובייל, 50+ פרויקטים עם תשתית push.
Push שקט ב-iOS: פרטים טכניים
ב-iOS, push שקט דורש את הדגל content-available: 1 ב-payload ואת מצב הרקע "Remote notifications" המופעל ב-Xcode Capabilities.
APNs payload:
{
"aps": {
"content-available": 1
},
"sync_type": "messages",
"last_known_id": "msg_8823"
}אין { "aps": { "content-available": 1 }, "sync_type": "messages", "last_known_id": "msg_8823" } , אין alert — קריאת רקע טהורה. טיפול ב-sound:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let syncType = userInfo["sync_type"] as? String else {
completionHandler(.noData)
return
}
Task {
do {
let hasNewData = try await SyncManager.shared.sync(type: syncType)
completionHandler(hasNewData ? .newData : .noData)
} catch {
completionHandler(.failed)
}
}
}נקודה קריטית: iOS נותן כ-30 שניות לביצוע. אם AppDelegate לא נקרא, המשימה מסתיימת בכוח. iOS גם לא מבטיח מסירה כשהסוללה נמוכה (Low Power Mode) ואחרי שהאפליקציה נסגרה בכוח. סגירה בכוח חוסמת לחלוטין push שקט עד שהאפליקציה מופעלת ידנית — זו התנהגות מתועדת של iOS ואי אפשר לעקוף אותה, כפי שמתועד על ידי אפל.
Push שקט באנדרואיד: FCM Data Message ו-WorkManager
באנדרואיד, התפקיד של push שקט מבוצע על ידי FCM Data Message — הוא מגיע ל-func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { guard let syncType = userInfo["sync_type"] as? String else { completionHandler(.noData) return } Task { do { let hasNewData = try await SyncManager.shared.sync(type: syncType) completionHandler(hasNewData ? .newData : .noData) } catch { completionHandler(.failed) } } } ללא קשר למצב האפליקציה (אם לא נהרג על ידי Doze של המערכת).
class AppFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
val syncType = message.data["sync_type"] ?: return
val workRequest = OneTimeWorkRequestBuilder<SyncWorker>()
.setInputData(workDataOf("sync_type" to syncType))
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(applicationContext).enqueue(workRequest)
}
}completionHandler מבקש ביצוע מיידי. באנדרואיד 12+, בתוך worker מואץ, יש לקרוא ל-FirebaseMessagingService.onMessageReceived, אחרת פעולה ארוכה עלולה לגרום ל-ANR.
מצב Doze מגביל פעילות רקע. הודעות FCM בעדיפות גבוהה עוקפות את Doze אם מציינים class AppFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(message: RemoteMessage) { val syncType = message.data["sync_type"] ?: return val workRequest = OneTimeWorkRequestBuilder<SyncWorker>() .setInputData(workDataOf("sync_type" to syncType)) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() WorkManager.getInstance(applicationContext).enqueue(workRequest) } } ב-payload:
{
"message": {
"token": "device_fcm_token",
"android": {
"priority": "HIGH"
},
"data": {
"sync_type": "messages",
"payload": "{...}"
}
}
} מידע נוסף על מצב Doze
מצב Doze מופעל כשהמכשיר במצב מנוחה ולא בטעינה. הודעות בעדיפות גבוהה עוקפות אותו, אבל אם הבקשות תכופות מדי, המערכת עלולה להתחיל להתעלם מהן — יש לשמור על מרווחים של לפחות 10 דקות בין push שקטים.איך לעדכן Badge בלי להודיע למשתמש?
מקרה נפוץ הוא עדכון מספר ה-badge בלי להציג התראה. ב-iOS דרך push שקט:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let badge = userInfo["badge"] as? Int {
UNUserNotificationCenter.current().setBadgeCount(badge) { _ in }
}
completionHandler(.newData)
}באנדרואיד, אין API אוניברסלי — השתמשו בספריית ShortcutBadger או ב-setExpedited(), אבל התמיכה תלויה ב-launcher (Samsung, Xiaomi, Huawei).
מגבלות ומכסות
iOS 13+ הציג את BGTaskScheduler ומכסת עיבוד רקע. אם אפליקציה מבקשת ביצוע רקע לעתים קרובות מדי ללא תועלת למשתמש, המערכת תגביל את הקריאות. קריאה ל-setForegroundAsync() כשאין נתונים חדשים היא קריטית לפעולה תקינה של מערכת המכסות. באנדרואיד, קיים מנגנון דומה — WorkManager מגביל משימות תכופות. יש להקפיד על מרווחים ולהשתמש ב-exponential backoff בשגיאות.
השוואת פלטפורמות
| תכונה | iOS | אנדרואיד |
|---|---|---|
| מנגנון | Push שקט (content-available) | FCM Data Message |
| ביצוע רקע | מצבי רקע + מגבלת 30 שניות | WorkManager + מואץ |
| סגירה בכוח | לא נמסר | נמסר (אם לא נהרג על ידי המערכת) |
| מצב Doze | הגבלה לפי מכסה | עדיפות גבוהה עוקפת Doze |
| עדכון Badge | android.priority: "HIGH" |
ShortcutBadger / תלוי ב-launcher |
טעויות נפוצות ופתרונות
| שגיאה | פתרון |
|---|---|
| Push שקט לא נמסר ב-iOS אחרי סגירה בכוח | הסבירו למשתמש שהאפליקציה צריכה להיות מופעלת ידנית |
| FCM Data Message לא מעיר את האפליקציה באנדרואיד 12+ | הוסיפו { "message": { "token": "device_fcm_token", "android": { "priority": "HIGH" }, "data": { "sync_type": "messages", "payload": "{...}" } } } ב-worker מואץ |
| Badge לא מתעדכן באנדרואיד Xiaomi | השתמשו ב-ShortcutBadger עם תמיכה מפורשת ל-Xiaomi |
| מכסת ביצוע רקע חרגה ב-iOS | הפחיתו את תדירות ה-push השקט לפעם אחת בכל 5-10 דקות |
מה כלול בשירות הגדרת ה-Push השקט שלנו
כשאתם מזמינים את השירות שלנו, אתם מקבלים:
- עיצוב ארכיטקטורה לתשתית push (APNs + FCM)
- יישום handlers ב-iOS ואנדרואיד עם כיסוי מקרי קצה
- הגדרת מצבי רקע, WorkManager, נושאים בעדיפות גבוהה
- אינטגרציה של עדכון מונה badge בשתי הפלטפורמות
- תיעוד והדרכת צוות
- תמיכה לאחר השקה (חודש אחד)
למה לסמוך על ההגדרה שלנו?
אנחנו מתמחים בפיתוח מובייל עם ניסיון של למעלה מ-10 שנים. יישמנו יותר מ-50 פרויקטים עם תשתית push, כולל אפליקציות בעומס גבוה עם מיליוני משתמשים. הפתרונות שלנו עוברים את App Store Review ו-Google Play Console ללא בעיות. אנחנו משתמשים בגרסאות העדכניות ביותר של Swift, Kotlin, Flutter — המחסנית נבחרת לפי הפרויקט שלכם.
לוחות זמנים ועלות
הגדרת push שקט לפלטפורמה אחת (iOS או אנדרואיד) אורכת 3–5 ימי עסקים. פתרון חוצה פלטפורמות אורך 5–9 ימים. העלות מחושבת באופן אישי לפי מורכבות האינטגרציה והמחסנית הנדרשת. אנחנו מציעים הערכת פרויקט חינמית — פשוט צרו קשר.
קבלו ייעוץ — פנו אלינו במייל או דרך הטופס באתר שלנו. נעזור לכם להגדיר push שקט כך שהנתונים יתעדכנו אפילו בתנאים הקשים ביותר.







