Automating iOS App Review Monitoring with Google Cloud Functions and Slack Notifications
Introduction
Monitoring user feedback is essential for improving mobile applications. App Store reviews offer direct insights from users, helping developers address issues and enhance their app’s performance. In this post, we’ll walk through automating the retrieval of iOS App Store reviews and posting them to a Slack channel using the App Store Connect API and Node.js.
This automation ensures real-time updates for your team, allowing them to respond quickly to feedback and improve user satisfaction.
Requirements
Before diving into the implementation, ensure you have the following:
App Store Connect API Access
You need access to Apple’s App Store Connect API to fetch customer reviews.
Set up an API key from your App Store Connect account and obtain the issuer ID, key ID, and private key.
Documentation: Apple Developer
Slack Incoming Webhook URL
Set up an incoming webhook in Slack to send review notifications.
Node.js Environment
Ensure you have Node.js installed and a project set up to run JavaScript scripts.
Implementation
Install Dependencies
To interact with App Store Connect and send notifications, install the required packages:
npm install axios jsonwebtoken
axios
: For making HTTP requests.jsonwebtoken
: For generating authentication tokens for the App Store Connect API.
Fetching iOS App Reviews
Create a script that authenticates with the App Store Connect API and retrieves the latest app reviews.
const jwt = require('jsonwebtoken'); const axios = require('axios'); const { getConfig } = require('../config'); const SLACK_WEBHOOK_URL = ''; // Retrieve iOS credentials dynamically async function getIosCredentials() { const config = getConfig(); try { const iosConfig = JSON.parse(config.appStore.iosReviewConfig); return { issuerId: iosConfig.ISSUER_ID, keyId: iosConfig.KEY_ID, privateKey: iosConfig.PRIVATE_KEY_PATH, appId: iosConfig.APP_ID, }; } catch (error) { throw new Error('❌ Failed to parse iOS review configuration: ' + error.message); } } // Create a JWT token for App Store Connect async function createIosJwt() { const { issuerId, keyId, privateKey, appId } = await getIosCredentials(); const payload = { iss: issuerId, iat: Math.floor(Date.now() / 1000), // Issued at (current time) exp: Math.floor(Date.now() / 1000) + 1200, // Expires in 20 minutes aud: 'appstoreconnect-v1', scope: [`GET /v1/apps/${appId}/customerReviews`], }; return jwt.sign(payload, privateKey, { algorithm: 'ES256', header: { alg: 'ES256', kid: keyId, typ: 'JWT', }, }); } // Fetch recent iOS reviews from App Store Connect async function fetchRecentIosReviews() { try { const token = await createIosJwt(); const { appId } = await getIosCredentials(); console.log('✅ Generated JWT:', token); // Fetch customer reviews const response = await axios.get(`https://api.appstoreconnect.apple.com/v1/apps/${appId}/customerReviews`, { headers: { Authorization: `Bearer ${token}`, }, }); const reviews = response.data.data; const now = new Date(); const twoDaysAgo = new Date(now.setDate(now.getDate() - 1)); // Filter reviews from the last 2 days const recentReviews = reviews.filter(review => { const reviewDate = new Date(review.attributes.createdDate); return reviewDate >= twoDaysAgo; }); const formattedReviews = recentReviews.map(review => { const userComment = review.attributes; const reviewDateTime = new Date(userComment.createdDate).toLocaleString('en-GB'); return { text: `*New review for iOS SkyProtect App*\n` + `*Rating:* ${userComment.rating} :star:\n` + `*Title:* ${userComment.title}\n` + `*Comment:* ${userComment.body}\n` + `*Reviewer:* ${userComment.reviewerNickname}\n` + `*Date:* ${reviewDateTime}\n`, }; }); await sendToSlack(formattedReviews); } catch (error) { console.error('❌ Error fetching iOS reviews:', error.response ? error.response.data : error.message); } } // Send reviews to Slack async function sendToSlack(reviews) { for (const review of reviews) { const message = { text: review.text }; try { await axios.post(SLACK_WEBHOOK_URL, message); console.log('✅ Sent review to Slack'); } catch (error) { console.error('❌ Error sending to Slack:', error.message); } } } module.exports = { fetchRecentIosReviews, sendToSlack, };
How It Works
generateJWT()
: Generates a JSON Web Token (JWT) for authentication.fetchRecentReviews()
: Fetches customer reviews from App Store Connect.sendToSlack()
: Sends review details to a specified Slack channel.
Deployment
In the perfect world this will be deployed as a Google Cloud Function and you can use the cron scheduler to enqueue it.
Conclusion
Automating app review monitoring ensures your team stays informed and can respond to user feedback quickly. By leveraging Apple’s App Store Connect API and Slack, you streamline the process and eliminate the need for manual review checks.
You can extend this setup by:
Filtering reviews based on star ratings.
Automatically responding to certain types of feedback.
Logging review trends over time.
Would you like to see an implementation for real-time notifications using other platforms like Telegram or Microsoft Teams? Let me know in the comments!