Բոլոր դասերը
Մոբայլ հավելվածներՄիջին10 րոպե ընթերցումԻմ ստեկում

Firebase՝ push, Remote Config և Crashlytics

Հասեք օգտատերերին ծանուցումներով, փոխեք հավելվածի վարքագիծը առանց release-ի և իմացեք, երբ այն crash է լինում։

  • FCM / APNs
  • Remote Config
  • Crashlytics
  • Analytics
Քայլ 1/5
App-ը OS-ից device token է խնդրում՝ այս app-ի հասցեն այս հեռախոսում, և backend-ը պահում է այն օգտատիրոջ հետ։

30 վայրկյանում

Push ծանուցումները ձեր սերվերից գնում են Google-ի կամ Apple-ի push ծառայություն, որն էլ դրանք առաքում է կոնկրետ սարքի token-ին։ Firebase Cloud Messaging-ը (FCM) տալիս է մեկ API երկու պլատֆորմների համար։ Firebase-ն առաջարկում է նաև Remote Config՝ ֆունկցիաները միացնելու կամ force update անելու համար առանց նոր build release անելու, և Crashlytics ու Analytics՝ production-ում crash-երն ու օգտագործումը տեսնելու համար։

Ինչու եմ օգտագործում

  • Ես օգտագործում եմ Firebase, որովհետև մեկ ծառայությունը ծածկում է իմ release արած գրեթե յուրաքանչյուր հավելվածի գործառնական կարիքները՝ push, հեռավար անջատիչներ, crash հաշվետվություններ և analytics։
  • Push-ը հիմնական բիզնես ֆունկցիա է պատվերների և լոյալության հավելվածներում՝ պատվերի կարգավիճակ, պարգևներ, արշավներ, ուստի առաքմանը, թույլտվության պահին և tap-ի մշակմանը վերաբերվում եմ որպես պրոդուկտային աշխատանքի, ոչ թե հետին մտքի։
  • Remote Config-ը ինձ տալիս է անվտանգության փական․ force-update կամ maintenance էկրանը կարելի է միացնել րոպեների ընթացքում, երբ backend-ի փոփոխությունը կամ bug-ը դա պահանջում է։
  • Crashlytics-ը ինձ տեղեկացնում է խնդիրների մասին մինչ օգտատերերը review-ներ կգրեն՝ stack trace-երով, որոնք կապված են կոնկրետ build-ի հետ։

Ինչպես է աշխատում

01Ինչպես են իրականում աշխատում push ծանուցումները

Ձեր սերվերը երբեք ուղղակիորեն չի խոսում հեռախոսի հետ։ iOS-ում օպերացիոն համակարգը մեկ կապ է պահում APNs-ի (Apple Push Notification service) հետ, Android-ում Google Play services-ը մեկ կապ է պահում FCM-ի հետ։ Երբ ձեր հավելվածը մեկնարկում է, այն OS-ից խնդրում է device token՝ այս սարքի վրա այս հավելվածի հասցեն, և ուղարկում այն ձեր backend-ին՝ կապված օգտատիրոջ հետ։ Օգտատիրոջը ծանուցելու համար ձեր backend-ը կանչում է FCM HTTP v1 API-ն այդ token-ով․ FCM-ը Android-ում առաքում է ուղղակիորեն, իսկ iOS-ի համար փոխանցում է APNs-ին (դուք մեկ անգամ APNs auth key եք վերբեռնում Firebase)։ Token-ները փոխվում են՝ վերատեղադրումից, վերականգնումից հետո կամ պարբերաբար, ուստի լսեք token refresh-ը և թարմացրեք backend-ը։ Երբ FCM-ը token-ը հայտնում է որպես unregistered, ջնջեք այն։ Մեկ օգտատեր կարող է ունենալ մի քանի սարք, ուստի մեկ օգտատիրոջ համար պահեք շատ token-ներ։

  • Device token՝ հավելվածի մեկ տեղադրման հասցե, ոչ թե մարդու
  • APNs auth key (.p8)՝ վերբեռնված Firebase iOS առաքման համար
  • FCM HTTP v1 API՝ service account-ով․ legacy API-ն այլևս չկա
  • Topic-ներ՝ մեկ հաղորդագրություն ուղարկել շատ subscriber-ների (օր․՝ 'promo-hy')

02Թույլտվություններ և token-ի ստացում

iOS-ին ծանուցումներ ցույց տալու համար միշտ պետք է օգտատիրոջ թույլտվությունը, իսկ Android-ին Android 13-ից (API 33) պետք է POST_NOTIFICATIONS runtime թույլտվությունը։ Եթե օգտատերը սեղմում է «Don't allow», սովորաբար այլևս չեք կարող հարցնել․ նա պետք է գնա Settings։ Ուստի հարցրեք այն պահին, երբ արժեքն ակնհայտ է՝ առաջին պատվերից հետո («Ծանուցե՞լ, երբ ձեր սուրճը պատրաստ լինի»), ոչ թե առաջին էկրանին։ Համակարգային dialog-ից առաջ ցույց տվեք կարճ բացատրություն հավելվածի ներսում։ iOS-ն աջակցում է նաև provisional authorization, որը ծանուցումները լուռ առաքում է notification center՝ առանց prompt-ի։ React Native Firebase-ով flow-ը հետևյալն է՝ խնդրել թույլտվություն, ստանալ token-ը, ուղարկել այն ձեր API-ին պլատֆորմի և հավելվածի տարբերակի հետ և subscribe անել token refresh-ին։

ts
import { getMessaging, requestPermission, getToken, onTokenRefresh,
  AuthorizationStatus } from "@react-native-firebase/messaging";
import { PermissionsAndroid, Platform } from "react-native";
 
export async function registerForPush(api: Api) {
  const messaging = getMessaging();
  if (Platform.OS === "android" && Platform.Version >= 33) {
    await PermissionsAndroid.request("android.permission.POST_NOTIFICATIONS");
  }
  const status = await requestPermission(messaging);
  if (status === AuthorizationStatus.DENIED) return;
 
  const token = await getToken(messaging);
  await api.post("/devices", { token, platform: Platform.OS });
  onTokenRefresh(messaging, (t) => api.post("/devices", { token: t, platform: Platform.OS }));
}

03Notification և data հաղորդագրություններ, foreground և tap-եր

FCM հաղորդագրությունը կարող է կրել notification բլոկ (title, body), data բլոկ (ձեր սեփական key-value string-ները) կամ երկուսն էլ։ Notification հաղորդագրությունները OS-ը ավտոմատ ցուցադրում է, երբ հավելվածը background-ում է կամ փակված է։ Միայն data պարունակող հաղորդագրություններն առաքվում են ձեր կոդին, որը որոշում է, թե ինչ անել․ iOS-ում դրանք background push-եր են, որոնք համակարգը կարող է throttle անել կամ ուշացնել, ուստի երբեք մի հենվեք դրանց վրա օգտատիրոջը տեսանելի ծանուցումների համար։ Երբ հավելվածը foreground-ում է, OS-ը default-ով banner ցույց չի տալիս․ դուք այն ստանում եք onMessage-ում և որոշում՝ ցույց տալ in-app toast, թարմացնել badge կամ ցուցադրել local notification Notifee-ով։ Tap-երը մշակեք երեք վիճակում՝ foreground, background (onNotificationOpenedApp) և cold start (getInitialNotification)։ Data-ի մեջ դրեք deep-link route և navigate արեք դրանով։

json
{
  "message": {
    "token": "DEVICE_TOKEN",
    "notification": { "title": "Order ready", "body": "Your latte is waiting at the counter" },
    "data": { "type": "order_status", "orderId": "1042", "link": "app://orders/1042" },
    "android": { "priority": "high", "notification": { "channel_id": "orders" } },
    "apns": { "payload": { "aps": { "sound": "default" } } }
  }
}

04Հուսալի ուղարկում backend-ից

Push-երն ուղարկեք queue worker-ից, ոչ թե HTTP հարցման ներսում․ պատվերի կարգավիճակի փոփոխությունը job է գրում, և worker-ը կանչում է FCM-ը retry-ներով և backoff-ով։ Օգտագործեք Firebase Admin SDK-ն կամ HTTP v1 endpoint-ը service-account credential-ով, որը պահվում է որպես գաղտնիք սերվերում, երբեք հավելվածում։ Յուրաքանչյուր արշավ կամ հաղորդագրություն պահեք նաև ձեր սեփական տվյալների բազայում, որպեսզի հավելվածը կարողանա ցույց տալ in-app inbox․ push-երի հասնելը երաշխավորված չէ, և օգտատերերը մաքրում են դրանք։ Հարգեք լռության ժամերը և օգտատիրոջ նախընտրությունները (պատվերի թարմացումները՝ միացված, մարքեթինգը՝ անջատված), իսկ Android-ում ստեղծեք առանձին notification channel-ներ, որպեսզի օգտատերերը կարողանան անջատել ակցիաները՝ չկորցնելով պատվերի թարմացումները։ Մաքրեք այն token-ները, որոնք FCM-ը վերադարձնում է որպես unregistered։ Զանգվածային արշավների համար օգտագործեք topic-ներ կամ batch ուղարկումներ և պլանավորեք դրանք ձեր սեփական job համակարգով։

05Remote Config՝ force update, maintenance, feature flag-եր

Remote Config-ը Firebase-ում պահվող key-value պարամետրերի հավաքածու է, որը հավելվածը ստանում է runtime-ում։ Default-ները ներառում եք հավելվածի ներսում, մեկնարկի ժամանակ fetch և activate եք անում խելամիտ minimum fetch interval-ով և արժեքները կարդում որպես boolean-ներ, թվեր կամ string-ներ։ Պայմանները թույլ են տալիս թիրախավորել ըստ պլատֆորմի, հավելվածի տարբերակի, երկրի կամ օգտատերերի տոկոսի։ Երեք կիրառություն ինքնին արդարացնում են իրենց։ Force update՝ պահեք min_supported_version ըստ պլատֆորմի․ եթե տեղադրված տարբերակն ավելի ցածր է, ցույց տվեք արգելափակող էկրան store-ի հղումով, ինչը էական է, երբ հին հավելվածը կկոտրվեր նոր API-ի դեմ։ Maintenance mode՝ դրոշակ, որը միգրացիաների ժամանակ ցույց է տալիս բարեհամբույր էկրան։ Feature flag-եր և աստիճանական rollout՝ միացրեք նոր checkout-ը օգտատերերի 10%-ի համար, հետևեք Crashlytics-ին, ապա անցեք 100%-ի։ Տրամաբանությունը պահեք անվտանգ, եթե fetch-ը ձախողվի․ հավելվածը պետք է աշխատի default-ներով։

ts
import { getRemoteConfig, setDefaults, setConfigSettings,
  fetchAndActivate, getValue } from "@react-native-firebase/remote-config";
import { Platform } from "react-native";
import DeviceInfo from "react-native-device-info";
 
export async function checkAppGate() {
  const rc = getRemoteConfig();
  await setDefaults(rc, { min_version_ios: "1.0.0", min_version_android: "1.0.0", maintenance: false });
  await setConfigSettings(rc, { minimumFetchIntervalMillis: 15 * 60 * 1000 });
  await fetchAndActivate(rc).catch(() => undefined); // offline: use cached/defaults
 
  const key = Platform.OS === "ios" ? "min_version_ios" : "min_version_android";
  if (getValue(rc, "maintenance").asBoolean()) return "maintenance";
  if (isLower(DeviceInfo.getVersion(), getValue(rc, key).asString())) return "force_update";
  return "ok";
}

06Crashlytics և Analytics

Crashlytics-ը հավաքում է native crash-երը և, React Native Firebase-ով, ձեր գրանցած JavaScript սխալները՝ խմբավորված ըստ issue-ի և կապված հավելվածի տարբերակի ու սարքի հետ։ Համոզվեք, որ symbol ֆայլերը (dSYM-ներ iOS-ում, mapping ֆայլեր Android-ում) վերբեռնվում են build-ի ժամանակ, այլապես stack trace-երը մնում են անընթեռնելի։ Ավելացրեք կոնտեքստ՝ սահմանեք ոչ անձնական user ID, ռիսկային քայլերից առաջ log արեք breadcrumb-ներ (վճարման սկիզբ, վճարումից վերադարձ) և գրանցեք կարևոր API ձախողումներից մշակված սխալները։ Հետևեք crash-free users տոկոսին ըստ release-ի․ staged rollout-ի ժամանակ այն ձեր ազդանշանն է կանգ առնելու։ Analytics-ը գրանցում է event-ներ, ինչպիսիք են screen_view, add_to_cart, begin_checkout և purchase։ Նախքան կոդ գրելը սահմանեք event-ների փոքր պլան՝ հետևողական անուններով, խուսափեք անձնական տվյալներ ուղարկելուց և հարգեք համաձայնությունը՝ անջատեք հավաքումը, մինչև օգտատերը համաձայնի, որտեղ օրենքը դա պահանջում է։

Օգտագործեք, երբ

  • Ցանկացած հավելված, որին պետք են transactional ծանուցումներ՝ պատվերի կարգավիճակ, ամրագրումներ, հիշեցումներ, անվտանգության ծանուցումներ։
  • Ձեզ պետք են հեռավար անջատիչներ՝ force update, maintenance mode, ֆունկցիաների աստիճանական rollout, առանց store release-ի։
  • Ուզում եք crash reporting և պրոդուկտային analytics՝ շատ քիչ setup-ով երկու պլատֆորմներում էլ։

Մի՛ օգտագործեք, երբ

  • Օգտատերերը հիմնականում Google Play services չունեցող սարքերի վրա են (որոշ շուկաներում)․ ձեզ պետք է Android push-ի այլընտրանքային պրովայդեր։
  • Տվյալների տեղակայման կամ գաղտնիության խիստ կանոնները արգելում են սարքի կամ օգտագործման տվյալներ ուղարկել Google-ին․ դիտարկեք self-hosted analytics և համապատասխան պրովայդեր։
  • Real-time in-app թարմացումներ, մինչ հավելվածը բաց է․ օգտագործեք WebSocket-ներ կամ SSE, push-ը live տվյալների ալիք չէ։

Այլընտրանքներ

  • Expo Notifications

    Միասնական push API և ծառայություն Expo հավելվածների համար․ ներքուստ օգտագործում է FCM և APNs։

  • OneSignal / Braze

    Hosted engagement պլատֆորմներ՝ սեգմենտավորմամբ, արշավներով և analytics-ով push-ի վրա։

  • Direct APNs + FCM

    Կանչեք Apple-ին և Google-ին ուղղակիորեն ձեր backend-ից՝ լիարժեք վերահսկողության համար և առանց լրացուցիչ vendor-ի։

  • Sentry

    Սխալների և արագագործության մոնիտորինգի այլընտրանք Crashlytics-ին՝ JS-ի և source map-երի ուժեղ աջակցությամբ։

Լավագույն պրակտիկաներ

  1. Ծանուցումների թույլտվություն խնդրեք իմաստալից պահին՝ նախապես կարճ բացատրությամբ։
  2. Մեկ օգտատիրոջ համար պահեք մի քանի token, թարմացրեք դրանք և ջնջեք այն token-ները, որոնք FCM-ը հայտնում է որպես unregistered։
  3. Ուղարկեք queue-ից retry-ներով․ պահեք in-app inbox, որովհետև push-երը երաշխավորված չեն։
  4. Օգտագործեք Android notification channel-ներ և օգտատիրոջ նախընտրություններ՝ transactional և մարքեթինգային հաղորդագրություններն առանձնացնելու համար։
  5. Ներառեք Remote Config default-ներ, որպեսզի հավելվածն աշխատի offline․ առաջին օրվանից օգտագործեք force-update և maintenance անջատիչ։
  6. Յուրաքանչյուր build-ում վերբեռնեք dSYM-ները և mapping ֆայլերը և հետևեք crash-free users-ին ըստ release-ի։
  7. Service-account key-երը պահեք միայն սերվերում և երբեք անձնական տվյալներ մի դրեք analytics event-ներում։

Տարածված սխալներ

  • Push-ի թույլտվություն խնդրել հենց առաջին էկրանին և ընդմիշտ կորցնել օգտատերերի մեծ մասի համաձայնությունը։
  • Կարևոր ծանուցումների համար iOS-ում հենվել միայն data հաղորդագրությունների վրա, որոնք համակարգը կարող է ուշացնել կամ դեն նետել։
  • Անտեսել foreground հաղորդագրությունները, այնպես որ բաց հավելվածով օգտատերերը երբեք չեն տեսնում ծանուցումը։
  • Չմշակել cold-start tap-ը, այնպես որ ծանուցման վրա սեղմելը բացում է գլխավոր էկրանը՝ պատվերի փոխարեն։
  • Release անել առանց force-update անջատիչի, հետո չկարողանալ կանգնեցնել հին build-երը, որոնք կանչում են փոխված API։

Խորը վերլուծություն

07Առաքման ներքին մեխանիզմը․ priority, TTL, collapsing և Doze

Առաքումը ձևավորվում է էներգիայի կառավարմամբ։ Android-ի Doze mode-ը անգործ հեռախոսների համար network-ի հասանելիությունը խմբավորում է batch-երով․ normal priority FCM հաղորդագրությունները սպասում են maintenance window-ի, իսկ high priority հաղորդագրությունները կարող են արթնացնել սարքը։ Google-ը կարող է իջեցնել այն հավելվածների առաջնահերթությունը, որոնց high priority հաղորդագրությունները չեն հանգեցնում տեսանելի notification-ի, ուստի high priority օգտագործեք միայն այն alert-ների համար, որոնք օգտատերը տեսնում է։ iOS-ում apns-priority 10-ը նշանակում է առաքել հիմա, իսկ 5-ը՝ առաքել հարմար պահին․ background (content-available) push-երը պետք է օգտագործեն priority 5 և սահմանափակվում են համակարգի կողմից։ Յուրաքանչյուր հաղորդագրություն ունի կյանքի տևողություն․ android.ttl-ը և apns-expiration header-ը որոշում են, թե որքան ժամանակ է ծառայությունը փորձում offline սարքին հասնել։ Collapse key-ը (Android) կամ apns-collapse-id-ն (iOS) հին չառաքված հաղորդագրությունները փոխարինում են ամենանորով, ուստի online վերադարձող հեռախոսը ցույց է տալիս մեկ «զամբյուղը թարմացվել է» հաղորդագրություն, ոչ թե տասը։ Payload-ները պահեք փոքր․ FCM-ը մերժում է մոտ 4 KB-ից մեծ հաղորդագրությունները։

json
{
  "message": {
    "token": "DEVICE_TOKEN",
    "data": { "type": "cart_sync", "cartId": "c_981" },
    "android": { "priority": "normal", "ttl": "3600s", "collapse_key": "cart" },
    "apns": {
      "headers": {
        "apns-priority": "5",
        "apns-push-type": "background",
        "apns-collapse-id": "cart"
      },
      "payload": { "aps": { "content-available": 1 } }
    }
  }
}

08Մեծ ծավալով ուղարկում․ batching, սխալներ և quota-ներ

Firebase Admin SDK-ն sendEachForMulticast-ով մեկ կանչով ուղարկում է մինչև 500 token-ից բաղկացած ցուցակ և վերադարձնում մեկ արդյունք յուրաքանչյուր token-ի համար՝ նույն հերթականությամբ։ Դասակարգեք յուրաքանչյուր ձախողում։ registration-token-not-registered-ը նշանակում է, որ հավելվածը ջնջվել է կամ token-ը rotate է եղել․ ջնջեք այն։ invalid-argument-ը նշանակում է, որ ձեր payload-ը սխալ է․ շտկեք կոդը, retry մի արեք։ Quota-exceeded-ը (HTTP 429) և unavailable կամ internal սխալները (503, 500) ժամանակավոր են․ retry արեք exponential backoff-ով և հարգեք Retry-After header-ը։ Գոյություն ունեն project-ի մակարդակի quota-ներ, ուստի մեծ campaign-ները պետք է անցնեն queue-ով, որը կարգավորում է ուղարկման տեմպը, այլ ոչ թե loop-ով, որը ամեն ինչ միանգամից կրակում է։ Տեմպի կարգավորումը պաշտպանում է նաև ձեր սեփական backend-ը․ եթե 200,000 մարդ նույն վայրկյանին ստանան promotion, շատերը նույն րոպեին կբացեն հավելվածը։

ts
import { getMessaging } from "firebase-admin/messaging";
 
export async function sendToUser(tokens: string[], title: string, body: string, link: string) {
  const res = await getMessaging().sendEachForMulticast({
    tokens, // up to 500 per call
    notification: { title, body },
    data: { link },
  });
 
  const dead = res.responses
    .map((r, i) => (r.error?.code === "messaging/registration-token-not-registered" ? tokens[i] : null))
    .filter((t): t is string => t !== null);
 
  await removeTokens(dead); // keep the token table clean
  return { sent: res.successCount, failed: res.failureCount };
}

09Notification-ների գաղտնիություն և անվտանգություն

Notification-ը ցուցադրվում է lock screen-ին, արտացոլվում է ժամացույցներին և երբեմն կարդացվում բարձրաձայն, ուստի դրա տեքստին վերաբերվեք որպես հրապարակային։ Երբեք title-ում կամ body-ում մի դրեք միանգամյա կոդեր, մնացորդներ, առողջության մանրամասներ կամ ամբողջական հասցեներ․ գրեք «Ձեր պատվերը թարմացում ունի» և մանրամասները ցույց տվեք հավելվածի ներսում՝ authentication-ից հետո։ iOS-ում Notification Service Extension-ը կարող է decrypt անել encrypt արված payload-ը կամ ներբեռնել rich media՝ նախքան ցուցադրումը (mutable-content), ինչը թույլ է տալիս Apple-ի և Google-ի սերվերներով ուղարկել ավելի քիչ զգայուն տվյալներ։ Push-եր ուղարկող service account-ի credential-ը հզոր է․ տվեք նրան միայն անհրաժեշտ Firebase Cloud Messaging role-երը, պահեք secret store-ում և rotate արեք։ Device token-ները secret չեն, բայց դրանք օգտատիրոջ հետ կապված անձնական տվյալներ են․ unlink արեք դրանք sign-out-ի ժամանակ և ջնջեք, երբ account-ը ջնջվում է։

10Remote Config-ի ներքին կառուցվածքը և թակարդները

Remote Config-ը client-side կոնֆիգուրացիա է, ոչ թե անվտանգության սահման․ արժեքները ներբեռնվում են սարք և այնտեղ կարող են կարդացվել կամ փոփոխվել, ուստի երբեք մի պահեք դրանում secret-ներ և մի օգտագործեք այն թույլտվություններ տալու համար՝ access-ը պետք է պարտադրի սերվերը։ SDK-ն cache է անում արժեքները և սահմանափակում fetch-երը․ default minimum fetch interval-ը 12 ժամ է, ուստի այն իջեցրեք գիտակցաբար և խուսափեք յուրաքանչյուր էկրանում fetch անելուց։ Որոշեք, թե ինչպես են արժեքներն ակտիվանում․ fetchAndActivate-ը գործարկման ժամանակ պարզ է, բայց կարող է փոխել UI-ը դրա հայտնվելուց հետո․ background-ում fetch անելը և հաջորդ գործարկմանը ակտիվացնելը խուսափում է flicker-ից։ Real-time Remote Config-ը կարող է փոփոխություններ push անել աշխատող հավելվածներին, երբ ձեզ պետք են արագ kill-switch-եր։ Տոկոսային պայմանները կայուն են յուրաքանչյուր app installation-ի համար, ուստի նույն օգտատերը մնում է նույն խմբում։ Յուրաքանչյուր publish ստեղծում է template version, որը կարող եք rollback անել, ինչը օգտակար է, երբ վատ արժեքը ինչ-որ բան է կոտրում։

Իրական իրավիճակներ

01

iOS push-երը դադարեցին production-ին անցնելուց հետո

Խնդիր

Android-ը ստանում է notification-ներ, և iOS-ը աշխատում էր development build-երում, բայց TestFlight-ի և App Store-ի օգտատերերը ոչինչ չեն ստանում։ Backend-ի log-երը ցույց են տալիս, որ FCM-ը ընդունում է request-ները։

Ինչպես լուծել

Անցեք iOS-ի շղթայով քայլ առ քայլ։ Համոզվեք, որ APNs authentication key-ը (.p8) upload է արված Firebase project-ի կարգավորումներում՝ ճիշտ Key ID-ով և Team ID-ով, և որ Firebase iOS app-ի bundle ID-ն ճշգրիտ համընկնում է ship արված հավելվածի հետ։ Ստուգեք, որ Push Notifications capability-ն միացված է, որպեսզի production entitlement-ը լինի ստորագրված build-ում։ Համոզվեք, որ հավելվածը թույլտվություն է խնդրում և գրանցվում remote notification-ների համար նախքան FCM token-ը կարդալը, և որ backend-ում պահված token-ը եկել է production build-ից, ոչ թե հին debug install-ից։ Վերջում Firebase console-ից test հաղորդագրություն ուղարկեք մեկ թարմ token-ի՝ backend-ի խնդիրները կոնֆիգուրացիայի խնդիրներից առանձնացնելու համար։

02

Campaign push-ը վայր գցեց backend-ը

Խնդիր

Marketing-ը 18:00-ին բոլոր օգտատերերին զեղչի push ուղարկեց։ Երկու րոպեի ընթացքում տասնյակ հազարավոր մարդիկ բացեցին հավելվածը, API-ի response time-երը պայթեցին, և շատ օգտատերեր առաջարկի փոխարեն սխալներ տեսան։

Ինչպես լուծել

Կարգավորեք ուղարկման տեմպը։ Campaign-ները դրվում են queue-ի մեջ, որն ուղարկում է batch-երով, օրինակ՝ րոպեում մի քանի հազար սարք, կամ ըստ ժամային գոտու, այլ ոչ թե մեկ պայթյունով։ Landing էկրանը դարձրեք էժան․ առաջարկի content-ը cache է արված Redis-ում կամ CDN-ում, և հավելվածը ցույց է տալիս cache-ված content-ը, մինչ թարմ տվյալները բեռնվում են։ Մեծ ուղարկումներից առաջ load test արեք campaign-ի ճանապարհը։ Համաձայնեցրեք backend-ի հետ՝ autoscaling կամ լրացուցիչ capacity՝ տաքացված պլանավորված campaign-ից առաջ, և rate limit-ներ, որոնք նրբորեն են դեգրադացվում։ Վերջում յուրաքանչյուր campaign պահեք ձեր տվյալների բազայում՝ իր կարգավիճակով, որպեսզի դադարեցված կամ մասնակի ուղարկված campaign-ը կարողանա շարունակվել՝ առանց կրկնօրինակներ ուղարկելու։

03

Օգտատերերը նույն notification-ը ստանում են երկու անգամ

Խնդիր

Որոշ օգտատերեր ստանում են երկու նույնական «պատվերը պատրաստ է» notification։ Դա վերաբերում է նրանց, ովքեր վերատեղադրել են հավելվածը կամ ունեն երկու սարք, և երբեմն տեղի է ունենում backend-ի deploy-երից հետո։

Ինչպես լուծել

Պարզեք, թե որ տեսակի կրկնօրինակն է։ Վերատեղադրումները ստեղծում են նոր token, մինչդեռ հինը կարող է որոշ ժամանակ դեռ վավեր լինել, ուստի token-ները պահեք ըստ installation ID-ի և փոխարինեք հին տողը, երբ սարքը նորից գրանցվում է։ Deploy-երը և retry-ները առաջացնում են երկրորդ տեսակը․ queue-ի job-ը, որը timeout է լինում այն բանից հետո, երբ FCM-ն արդեն ընդունել է հաղորդագրությունը, retry է արվում և նորից ուղարկում։ Յուրաքանչյուր notification-ի տվեք կայուն ID, գրանցեք այն որպես ուղարկված նախքան job-ը acknowledge անելը և skip արեք, եթե այն արդեն գոյություն ունի։ Օգտագործեք այդ ID-ին հավասար collapse key կամ apns-collapse-id, որպեսզի նույնիսկ եթե երկու պատճեն հասնեն սարքին, նորը փոխարինի հինին, այլ ոչ թե դրանք կուտակվեն։

Արագ ուղեցույց

FCM HTTP v1 send endpoint
POST https://fcm.googleapis.com/v1/projects/PROJECT_ID/messages:send
OAuth scope ուղարկելու համար
https://www.googleapis.com/auth/firebase.messaging
Android 13+ manifest permission
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Batch ուղարկում մեկ օգտատիրոջ սարքերին
getMessaging().sendEachForMulticast({ tokens, notification, data }) // max 500
Սարքերը բաժանորդագրել topic-ի
getMessaging().subscribeToTopic(tokens, "promo-hy")
Սխալ, որը նշանակում է՝ ջնջել token-ը
messaging/registration-token-not-registered
Փոխարինել հին չառաքված հաղորդագրությունները
android.collapse_key / apns-collapse-id header
Սահմանափակել, թե որքան է FCM-ը retry անում
"android": { "ttl": "3600s" } / apns-expiration header
Ցույց տալ iOS banner-ը foreground-ում (native)
completionHandler([.banner, .list, .sound])
Remote Config-ի fetch interval-ը dev-ում
setConfigSettings(rc, { minimumFetchIntervalMillis: 0 }) // never in production

Իմ իրական նախագծերում

  • Իմ ռեստորանային, կենդանիների խանութի, մարզասրահի և սուրճի հավելվածներն օգտագործում են Firebase Messaging, Analytics, Crashlytics և Remote Config, դրանց մեծ մասը՝ Notifee-ով foreground ծանուցումների համար։
  • Remote Config-ը իմ հավելվածներում ղեկավարում է force update-ը և maintenance mode-ը․ backend-ներն ուղարկում են FCM push արշավներ և պահում in-app ծանուցումների inbox։
  • Մարզասրահի CRM-ը ուղարկում է պլանավորված push արշավներ, իսկ ռեստորանի և մարզասրահի ադմին վահանակներն օգտագործում են web push աշխատակիցների ծանուցումների համար։
  • Խաղի LiveOps console-ի համար կառուցեցի Remote Config խմբագիր՝ պատմությամբ և diff-երով, ինչպես նաև FCM push խաղացողներին․ POS SaaS-ը ուղարկում է FCM և APNs push, ներառյալ iOS Live Activities։

Հարցեր և պատասխաններ

Q1Ո՞րն է տարբերությունը FCM-ի notification և data հաղորդագրությունների միջև։

Notification հաղորդագրությունն ունի title և body, որոնք օպերացիոն համակարգը ավտոմատ ցույց է տալիս, երբ հավելվածը background-ում է կամ փակված․ այն ցուցադրելու համար ձեր կոդը պետք չէ։ Data հաղորդագրությունը կրում է միայն ձեր key-value payload-ը և առաքվում է հավելվածին, որը որոշում է, թե ինչ անել։ Android-ում data հաղորդագրությունները հուսալի են high priority-ով, iOS-ում դրանք դառնում են լուռ background push-եր, որոնք համակարգը կարող է throttle անել։ Գործնականում ես ուղարկում եմ երկու բլոկն էլ՝ notification՝ տեսանելի ծանուցման համար, և data՝ տիպով, ID-ներով և deep-link route-ով՝ tap-ը մշակելու համար։

Q2Ինչո՞ւ օգտատերերը չեն տեսնում իմ ծանուցումը, երբ հավելվածը բաց է։

Որովհետև foreground-ում օպերացիոն համակարգը հաղորդագրությունը փոխանցում է հավելվածին՝ banner ցույց տալու փոխարեն․ ենթադրությունն այն է, որ հավելվածն այն կներկայացնի կոնտեքստում։ React Native Firebase-ով այն ստանում եք onMessage listener-ում։ Այնտեղից որոշում եք՝ թարմացնել պատվերի էկրանը, եթե այն տեսանելի է, ցույց տալ in-app toast կամ ցուցադրել local notification Notifee-ով՝ ճիշտ Android channel-ով։ iOS-ում կարող եք նաև կարգավորել presentation options, որպեսզի banner-ները ցույց տրվեն foreground-ում։ Սա բացահայտ մշակելը push-ի նախագծման մաս է, ոչ թե bug fix։

Q3Ինչպե՞ս եք իրականացնում force update մոբայլ հավելվածում։

Remote Config-ում պահում եմ նվազագույն աջակցվող տարբերակն ըստ պլատֆորմի՝ հավելվածի ներսում անվտանգ default-ով։ Մեկնարկի ժամանակ հավելվածը fetch և activate է անում config-ը, semantic version comparison-ով համեմատում իր տարբերակը նվազագույնի հետ, և եթե այն ավելի ցածր է, ցույց է տալիս արգելափակող էկրան՝ store-ի էջի կոճակով։ Ավելի մեղմ «recommended version» key-ը կարող է ցույց տալ փակվող prompt։ Backend-ը նույնպես կարող է չաջակցվող client-ների համար կոնկրետ սխալ վերադարձնել՝ որպես պաշտպանության երկրորդ գիծ։ Սա թույլ է տալիս ինձ անվտանգ փոխել API-ները՝ առանց հին build-երի լուռ կոտրվելու։

Q4Push token-ների կառավարում․ ի՞նչը կարող է սխալ գնալ։

Token-ները նույնականացնում են հավելվածի տեղադրումը, ոչ թե օգտատիրոջը, և փոխվում են վերատեղադրումից, տվյալների վերականգնումից կամ rotation-ից հետո։ Եթե մեկ օգտատիրոջ համար պահում եք միայն մեկ token, երկրորդ սարքը դադարում է հաղորդագրություններ ստանալ։ Եթե երբեք չեք ջնջում հնացած token-ները, շարունակում եք ուղարկել «մեռած» հասցեների և աղավաղում եք առաքման metric-ները։ Ես պահում եմ token-ները ըստ սարքի՝ պլատֆորմով, հավելվածի տարբերակով և վերջին ակտիվության ամսաթվով, թարմացնում եմ refresh-ի և sign-in-ի ժամանակ, sign-out-ի ժամանակ հեռացնում եմ token-ը, որպեսզի հեռախոսի հաջորդ օգտատերը private հաղորդագրություններ չստանա, և ջնջում եմ այն token-ները, որոնք FCM-ը հայտնում է որպես unregistered։

Q5Ինչպե՞ս կանեիք ռիսկային ֆունկցիայի անվտանգ rollout։

Դրեք այն Remote Config flag-ի հետևում, որը default-ով անջատված է։ Release արեք build-ը՝ ֆունկցիան թաքցված, ապա միացրեք այն ներքին թեստավորողների համար պայմանով, հետո իրական օգտատերերի փոքր տոկոսի համար։ Հետևեք Crashlytics-ի crash-free տոկոսին, error log-երին և Analytics-ի հիմնական event-ներին, օրինակ՝ checkout-ի ավարտին՝ համեմատած control group-ի հետ։ Եթե ինչ-որ բան սխալ է գնում, անջատեք flag-ը րոպեների ընթացքում՝ առանց նոր release-ի։ Երբ 100%-ում կայուն է, հեռացրեք flag-ը և հին code path-ը, որպեսզի flag-երը չկուտակվեն որպես տեխնիկական պարտք։

Սենիոր մակարդակի հարցեր

S1Նախագծեք notification համակարգ մեկ միլիոն օգտատեր ունեցող հավելվածի համար։

Ես առանձնացնում եմ intent-ը առաքումից։ Բիզնես event-ները՝ պատվերը պատրաստ է, ամրագրման հիշեցում, campaign, տվյալների բազայում ստեղծում են notification record-ներ՝ type-ով, օգտատիրոջով, payload-ով, priority-ով և կայուն ID-ով։ Preference service-ը յուրաքանչյուր օգտատիրոջ և channel-ի համար որոշում է՝ ուղարկե՞լ, թե՞ ոչ (transactional-ը միացված է, marketing-ը՝ opt-in, quiet hours, frequency cap-եր)։ Ընդունված notification-ները գնում են ըստ priority-ի բաժանված queue-ներ, որպեսզի մեծ campaign-ը երբեք չուշացնի «պատվերը պատրաստ է» alert-ը։ Worker-ները FCM-ի միջոցով batch-երով fan-out են անում օգտատիրոջ ակտիվ token-ներին, դասակարգում սխալները, ջնջում մեռած token-ները և ժամանակավոր ձախողումները retry անում backoff-ով։ Յուրաքանչյուր record հայտնվում է նաև in-app inbox-ում, քանի որ push-ը best-effort է։ Metric-ները ներառում են queue lag, ուղարկման հաջողություն, բացումներ և opt-out-ի տոկոսներ։ Trade-off․ սա ավելի շատ շարժվող մասեր է, քան FCM-ն ուղղակի կանչելը, բայց տալիս է fairness, auditability և անվտանգ retry-ներ։

S2Կարո՞ղ եք երաշխավորել, որ push notification-ը կառաքվի ճիշտ մեկ անգամ։

Ոչ։ Ճանապարհն անցնում է ձեր queue-ով, FCM-ով, APNs-ով կամ Android transport-ով և սարքով, որը կարող է լինել offline, Doze-ում կամ ջնջված․ դրանցից ոչ մեկը ձեզ end-to-end երաշխիքներ չի տալիս։ Այն, ինչ կարող եք կառուցել, at-least-once ուղարկումն է՝ deduplication-ով։ Սերվերում յուրաքանչյուր notification ունի կայուն ID․ worker-ը հաջողությունը գրանցում է idempotent, որպեսզի timeout-ից հետո retry-ները նորից չուղարկեն։ Սարքում collapse ID-ն կամ հավելվածի սեփական ստուգումը notification ID-ով թաքցնում է կրկնօրինակները։ Այն ամենի համար, ինչն իսկապես կարևոր է՝ անվտանգության alert, պատվերի կարգավիճակ, source of truth-ը in-app state-ն ու inbox-ն են, գումարած fallback channel, օրինակ՝ SMS կամ email, երբ բիզնեսին դա պետք է։ Push-ը հավելվածը բացելու հուշում է, ոչ թե record։

S3Firebase Remote Config, թե՞ առանձին feature-flag ծառայություն։

Remote Config-ը հիանալի է միայն mobile-ի switch-երի համար՝ force update, maintenance mode, պարզ տոկոսային rollout-ներ և Firebase Analytics-ին կապված A/B test-եր՝ գրեթե առանց infrastructure-ի։ Դրա սահմանափակումները երևում են, երբ flag-երը պետք է հետևողականորեն evaluate լինեն mobile-ում, web-ում և backend-ում նույն օգտատիրոջ համար, երբ ձեզ պետք է targeting ըստ ձեր սեփական բիզնես attribute-ների, approval workflow-ներ, audit trail-եր template version-ներից դուրս, կամ զգայուն կանոնների server-side evaluation։ Այդ դեպքում ավելի լավ է համապատասխանում առանձին flag ծառայությունը՝ open source կամ կոմերցիոն, կամ ձեր API-ով մատուցվող փոքր ներքին flags աղյուսակը։ Տարածված hybrid տարբերակ․ Remote Config՝ հավելվածի մակարդակի gate-երի համար, որոնք պետք է աշխատեն մինչև login-ը կամ երբ ձեր API-ն անհասանելի է, և backend flag-եր՝ բիզնես feature-ների համար, որպեսզի սերվերը մնա հեղինակությունը այն հարցում, թե ինչ կարող է անել օգտատերը։

Բառարան

APNs
Apple Push Notification service․ Apple-ի սերվերները, որոնք բոլոր remote notification-ները հասցնում են iPhone-ներին, iPad-ներին և Mac-երին։
FCM
Firebase Cloud Messaging․ Google-ի ծառայությունը, որն առաքում է Android-ին ուղղակիորեն, իսկ iOS-ին՝ APNs-ի միջոցով։
Device token
Մեկ սարքի վրա հավելվածի մեկ installation-ի հասցեն, որն օգտագործում է push ծառայությունը հաղորդագրություններ առաքելու համար։
Topic
Անվանված խումբ, որին սարքերը բաժանորդագրվում են, որպեսզի մեկ հաղորդագրությունը հասնի բոլոր բաժանորդներին՝ առանց token-ներ թվարկելու։
Collapse key
Նույնացուցիչ, որը push ծառայությանն ասում է, որ նոր հաղորդագրությունը փոխարինում է նույն key-ով հին չառաքվածներին։
Notification channel
Android-ի կատեգորիա՝ իր ձայնով և կարևորությամբ, որը օգտատերերը կարող են առանձին անջատել։
Doze
Android-ի էներգախնայողության ռեժիմ, որը հետաձգում է network-ի ակտիվությունը անգործ սարքերում՝ ազդելով normal priority հաղորդագրությունների վրա։
Notification Service Extension
iOS app extension, որը կարող է փոփոխել notification-ը նախքան ցուցադրումը, օրինակ՝ decrypt անել տեքստը կամ կցել նկարներ։
dSYM
iOS-ի debug symbol ֆայլ, որը crash report-ներում հիշողության հասցեները վերածում է ընթեռնելի function-ների անունների և տողերի։
Crash-free users
Օգտատերերի տոկոսը, որոնք որոշակի ժամանակահատվածում crash չեն ունեցել․ Crashlytics-ում release-ի առողջության հիմնական metric-ը։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։