Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Сервисы без сервера. Используем Firebase на 100% — Андрей Мухаметов, FunCorp

5d08ba0cd07942f2ddbf82e5b21ba5e7?s=47 FunCorp
February 15, 2020

Сервисы без сервера. Используем Firebase на 100% — Андрей Мухаметов, FunCorp

5d08ba0cd07942f2ddbf82e5b21ba5e7?s=128

FunCorp

February 15, 2020
Tweet

Transcript

  1. 1

  2. Сервисы без сервера Андрей Мухаметов, Deputy CTO 2

  3. 3 FunCorp: цифры и факты • Основана в 2004 •

    100+ сотрудников • Офисы LA, Лимассол, Москва • iFunny • Мемы и большое комьюнити с 2011 • Входит в топ 10 развлекательных приложений в США • 50кк установок в США, DAU 4,5кк, 370кк просмотров
  4. Задача • Быстрая проверка гипотез • Низкие трудозатраты • Условно

    бесплатно 4
  5. О чем расскажем • Почему без backend • Выбор платформы

    • Состав Firebase Platform • Строим сервисы • Допущения и ограничения 5
  6. Выбор платформы 1 6

  7. CloudKit vs 7

  8. Realm vs 8

  9. Parse vs 9

  10. Сервисы • Amazon Web Services • Microsoft Azure • Google

    Cloud Platform 10
  11. 11 Cost, from Android Web API Devops CloudKit 0$ ✅

    ✅ ❌ ✅ Realm 30$ ✅ ✅ ❌ ✅ Parse 0$ ✅ ✅ ✅ ❌ Firebase 0$ ✅ ✅ ✅ ✅
  12. Платформа 2 12

  13. Состав • mBaaS • Аналитика • Качество и отладка 13

  14. Data Firestore mBaaS 14 API Functions Storage Storage

  15. mBaaS • Auth • Hosting • ML Kit 15

  16. Качество • Crashlytics • App distribution • Latest Release •

    Test Lab • Perfomance 16
  17. Robert Downey Jr. Аналитика 17

  18. Строим сервис 3 18

  19. Проект 1: Kek 19 • Голосовой чат • Лента мемов

    • Комнаты на 7 человек • Загрузка UGC
  20. 20 Голосовой чат Firebase WebRTC Cервисы

  21. Jitsi • ReactNative • WebRTC • Поддержка видео и чатов

    • Бесплатный • Открытый код 21
  22. Задачи Backend • Парсить мемы • Сообщать о выпусках •

    Формировать комнаты • Работа с пользователями 22
  23. Инструменты • Inteliji IDEA • TypeScript(JavaScript) • Node.js • FireBase

    SDK 23
  24. import { generateAutoId, insertItem, insertNewIssue, } from "./firestore"; import {messageNewIssue}

    from "./messaging"; import * as parser from "./parser" export const parseNewIssue = functions.https.onRequest((request, response) => { console.log(`Starting parse iFunny API`); const date = new Date(); const startDate = date.getTime(); parser.parseiFunny(startDate, result => { //process result }); }); 24
  25. if (result) { const originalFeedId = generateAutoId(); insertNewIssue(result!, originalFeedId, ()

    => { messageNewIssue().then(() => { response.send(`ok`); }).catch((error => { console.log('Error new issue push', error); response.send(`ok`); })); }); } else { response.status(500); response.send(); } 25
  26. export const messageNewIssue = function () { let payload: MessagingPayload

    = { notification: { body:'New cool Memes are coming! It\'s gonna be loud!', title:'Hey!', badge: '20' } }; return new Promise((resolve, reject) => { config.adminApp().messaging().sendToTopic('newIssue',payload) .then(messageResponse => { resolve(); }) .catch(error => { reject(error); }); }); }; 26
  27. import * as admin from "firebase-admin"; export const adminApp =

    function (): admin.app.App { if (!admin.apps.length) { admin.initializeApp({ credential: admin.credential.applicationDefault(), databaseURL: "https://test-app.firebaseio.com", storageBucket:”test-app.appspot.com" }); } let app = admin.apps[0]; if (!app) { app = admin.app(); } return app; }; 27
  28. Messaging.messaging().delegate = self Messaging.messaging().subscribe(toTopic: “newIssue"); let authOptions: UNAuthorizationOptions = [.alert,

    .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: {_, _ in }) application.registerForRemoteNotifications() 28
  29. private let service = Auth.auth() func anonymLogin(nickname: String, completion: @escaping

    Closure.NetworkResult<User>) { service.signInAnonymously { (result, error) in if let error = error { completion(.failure(error)) } else if let result = result { let user = User(id: result.user.uid, name: nickname, email: "anonym", pic: "") self.currentUser = user completion(.success(user)) } } } 29
  30. private let collection = Firestore.firestore().collection("users") private var currentUser: User? {

    didSet { if let currentUser = currentUser { collection.document(currentUser.id).setData([ "name": currentUser.name ], merge: true) UserDefaults.standard.setStruct(currentUser, forKey: currentUser.id) } } } 30
  31. Отладка • Эмуляторы • Node.js • Terminal Firebase tools •

    IDEA/WebStorm 31
  32. Особенности отладки • Авторизация • Синхронизация окружения • Настройка IDE

    и Node.js 32
  33. Deploy 33 firebase login firebase deploy

  34. Аналитика 34

  35. Firebase • Время сессии • RR1 • Событие Speaker •

    Событие Content Upload 35
  36. Сколько стоит? 36

  37. Firestore Functions Analytics 37 Использование $0 $0 $0

  38. Проект 2: Аудиопранки • Стриминг аудио • Валидация покупки •

    Оптимизация Appsflyer • Пуши о выпусках 38
  39. Storage • Правила доступа • Download/Upload • Бесплатные лимиты 39

  40. Apple IAP • Валидация • WebHook • Хранение 40

  41. export const verifyReceipt = functions.https.onCall((data, context) => { const receipt

    = data.receipt; const productId = data.productId; const result = new Promise((resolve, reject) => { console.info('start buy for user ', context.auth); getPuchaseDataFromStore(receipt).then((purchases) => { getUserId(context).then((uid) => { //proccess transaction }).catch((reason) => { reject(reason); }); }).catch((reason) => { reject(reason); }); }); return result; }); 41
  42. type AppsflyerEvent = { appsflyer_id: string, idfa: string, customer_user_id: string,

    eventName: string, eventValue: string | undefined, eventCurrency: string | undefined, eventTime: string | undefined, af_events_api: string }; 42 Appsflyer
  43. const trackEvent = function(event: AppsflyerEvent) { const requestConfig: AxiosRequestConfig =

    { headers: { authentication: config.AppsflyerDevKey } }; return new Promise((resolve,reject) => { Axios.post('https://api2.appsflyer.com/inappevent/id' + config.AppId, event, requestConfig).then((response) => { console.log("OK\n", response); resolve(); }).catch(reason => { console.log("ERR\n",reason); reject(reason); }); }); }; 43
  44. Database vs 44

  45. Плюсы Realtime Database • Низкие задержки • Полные запросы •

    Онлайн статус 45
  46. Плюсы FireStore • Лучше масштабируется • Удобные запросы • Возможность

    распределения 46
  47. Firestore 47

  48. Cloud Messaging • Простой сервис • Аналитика • A/B тестирование

    • Кросс-платформенный 48
  49. Сколько стоит? 49

  50. 50

  51. Ограничения 4 51

  52. Технические • Профилирование • npm, node.js, TypeScript • Цена 52

  53. Продуктовые • Аналитика • Поддержка iOS • Зависимость 53

  54. Итоги • Подходит для MVP в одного • Удобная аналитика

    и сбор крешей • Условно бесплатно • Профилирование и логи • mBaaS на запуске проекта 54
  55. a.m@fun.co amyhametov job@fun.co we are hiring! :)