compileSdkVersion
targetSdkVersion
「ネイティブ広告ごとに別々の広告ユニットを作ることをお勧めします。それによって、個々のパフォーマンスを個別に分析できるため、適切なアクションを実行してどう改善すればよいかを洞察できるようになります」
「アプリのワークフローやユーザーのフローを損なうことなくネイティブ広告を組み込める場所を探しましょう」
「ネイティブ広告は直感的です。そのため、アプリの目立つ場所にネイティブ広告を表示するとよいでしょう。私たちは、広告バックエンド内の情報に基づいて、リアルタイムでユーザーのクリック率への影響を確認しました。短時間でネイティブ広告を調整し、広告から最大限の効果を得ることができたのはそのためです」
「FCM を見つけるまで、効果的な通知ソリューションを探すことはできませんでした。……機能が豊富でパフォーマンスも安定しており、導入も簡単な FCM は最高のソリューションです」 Alibaba.com モバイル、ディレクター、Zou Yu 氏
「Firebase Cloud Messaging は私たちの要件に完璧に一致しました」 AliExpress、ディレクター、Lijun Chen 氏
「初期実装はとても簡単でした。実際に 1 日で終わってしまったほどです」 デベロッパー、Filip Procházka 氏
var google = new firebase.auth.GoogleAuthProvider(); firebase.auth().signInWithPopup(google);
signInWithCustomToken
http://localhost:8080/instagram-callback
https:///instagram-callback
// Instagram OAuth 2 の設定 const credentials = { client: { id:YOUR_INSTAGRAM_CLIENT_ID, // 要変更 secret:YOUR_INSTAGRAM_CLIENT_SECRET, // 要変更 }, auth: { tokenHost: 'https://api.instagram.com', tokenPath: '/oauth/access_token' } }; const oauth2 = require('simple-oauth2').create(credentials);
/instagram-callback
app.get('/redirect', (req, res) => { // ランダムな状態検証 Cookie を生成 const state = req.cookies.state || crypto.randomBytes(20).toString('hex'); // localhost で安全でない Cookie を許可 const secureCookie = req.get('host').indexOf('localhost:') !== 0; res.cookie('state', state.toString(), {maxAge: 3600000, secure: secureCookie, httpOnly: true}); const redirectUri = oauth2.authorizationCode.authorizeURL({ redirect_uri: `${req.protocol}://${req.get('host')}/instagram-callback`, scope: 'basic', state: state }); res.redirect(redirectUri); });
function onSignInButtonClick() { // ポップアップで Auth フローをオープン window.open('/redirect', 'firebaseAuth', 'height=315,width=400'); };
code
state
app.get('/instagram-callback',(req, res) => { // state Cookie を受け取ったことを確認 if (!req.cookies.state) { res.status(400).send('State cookie not set or expired.Maybe you took too long to authorize.Please try again.'); // state Cookie が state パラメータと一致することを確認 } else if (req.cookies.state !== req.query.state) { res.status(400).send('State validation failed'); } // アクセス トークン用に認証コードを交換 oauth2.authorizationCode.getToken({ code: req.query.code, redirect_uri: `${req.protocol}://${req.get('host')}/instagram-callback` }).then(results => { // Instagram アクセス トークンとユーザー ID の取得が完了 const accessToken = results.access_token; const instagramUserID = results.user.id; const profilePic = results.user.profile_picture; const userName = results.user.full_name; // ... }); });
app.get('/instagram-callback', (req, res) => { // ... }).then(results => { // Instagram アクセス トークンとユーザー ID の取得が完了 const accessToken = results.access_token; const instagramUserID = results.user.id; const profilePic = results.user.profile_picture; const userName = results.user.full_name; // Firebase カスタム認証トークンの作成 const firebaseToken = createFirebaseToken(instagramUserID); // ログインを実行し、ユーザー プロフィールをアップデートする HTML ページの提供 res.send( signInFirebaseTemplate(firebaseToken, userName, profilePic, accessToken))); }); });
service-account.json
const firebase = require('firebase'); const serviceAccount = require('./service-account.json'); firebase.initializeApp({ serviceAccount: serviceAccount });
function createFirebaseToken(instagramID) { // ユーザーに割り当てる uid const uid = `instagram:${instagramID}`; // カスタム トークンの作成 return firebase.auth().createCustomToken(uid); }
app.get('/instagram-callback', (req, res) => { // ... // ログインを実行し、ユーザー プロフィールをアップデートする HTML ページの提供 res.send( signInFirebaseTemplate(firebaseToken, userName, profilePic, accessToken))); }); }); function signInFirebaseTemplate(token, displayName, photoURL, instagramAccessToken) { return ` <script src="https://www.gstatic.com/firebasejs/3.4.0/firebase.js"></script> <script src="promise.min.js"></script><!-- Promise Polyfill for older browsers --> <script> var token = '${token}'; var config = { apiKey:MY_FIREBASE_API_KEY, // 要変更 databaseURL:MY_DATABASE_URL // 要変更 }; // 一時 Firebase アプリにログインしてプロフィールをアップデート var tempApp = firebase.initializeApp(config, '_temp_'); tempApp.auth().signInWithCustomToken(token).then(function(user) { // Realtime Database に Instagram API アクセス トークンを保存 const tasks = [tempApp.database().ref('/instagramAccessToken/' + user.uid) .set('${instagramAccessToken}')]; // 必要に応じて displayname と photoURL をアップデート if ('${displayName}' !== user.displayName || '${photoURL}' !== user.photoURL) { tasks.push(user.updateProfile({displayName: '${displayName}', photoURL: '${photoURL}'})); } // 以上のタスクの完了を待機 return Promise.all(tasks).then(function() { // 一時 Firebase アプリを削除し、デフォルト Firebase アプリにログインし、ポップアップをクローズ var defaultApp = firebase.initializeApp(config); Promise.all([ defaultApp.auth().signInWithCustomToken(token), tempApp.delete()]).then(function() { window.close(); // 完了!ポップアップをクローズ }); }); }); </script>`; }
yoga|interval_training|running
"42"
"3.14159"
"Level_42"
{ "rules": { "chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()" } } } } }
chats//messages
members
userID
$chatID
user_abc
user_xyz
members/user_xyz
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).exists()" } } }
"lurker"
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" } } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() != 'lurker'" } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).exists()", ".write": "data.child(auth.uid).exists()" } } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner'" } } }
"chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner'" }, "pending": { "$uid": { ".write": "$uid === auth.uid" } } } }
pending/
"pending": { "$uid": { ".write": "$uid === auth.uid && !data.exists()" } }
"pending": { "$uid": { ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()" } }
pending
"pending": { ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'", ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'", "$uid": { ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()" } }
"members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')" }
/chats/chat_345/members
{ "user_zzz" : "owner" }
user_zzz
chat_456/messages/abc
chat_456
"user_zzz"
/chats/chat_987/members
{ "rules": { "chats": { "$chatID": { "messages": { ".read": "data.parent().child('members').child(auth.uid).exists()", ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'" }, "members": { ".read": "data.child(auth.uid).val() == 'owner'", ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')" }, "pending": { ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'", ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'", "$uid": { ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()" } } } } } }