Skip to content

Integrate App Store Connect API wrapper for automated app metadata #6

@ebowwa

Description

@ebowwa

Summary

Integrate the existing App Store Connect API wrapper service to enhance the app-preview-generator with automated app metadata retrieval and management capabilities.

API Service Details

  • Service URL: http://91.98.132.37:8001
  • Health Check: http://91.98.132.37:8001/health
  • Monitoring: http://91.98.132.37:3001 (Uptime Kuma)

Available Endpoints

  • /auth - Initialize authentication with App Store Connect
  • /apps - List all apps
  • /apps/{app_id} - Get app details
  • /apps/{app_id}/localizations - Get/update app localizations
  • /apps/{app_id}/versions - Get app versions
  • /categories - Get available categories

Integration Benefits

  • Automatically fetch app metadata (name, description, screenshots)
  • Sync app store localizations with preview generator
  • Display current app store assets alongside generated previews
  • Enable direct upload of generated previews to App Store Connect
  • Bulk update localizations across multiple languages
  • Monitor app version states and metadata

Proposed Implementation

1. Authentication Module

// lib/appStoreConnect.ts
class AppStoreConnectClient {
  private baseUrl = process.env.ASC_API_URL || 'http://91.98.132.37:8001';
  
  async initialize() {
    const response = await fetch(`${this.baseUrl}/auth`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        key_id: process.env.ASC_KEY_ID,
        issuer_id: process.env.ASC_ISSUER_ID,
        private_key_content: process.env.ASC_PRIVATE_KEY
      })
    });
    return response.json();
  }
}

2. App Metadata Integration

// Fetch and display app metadata
async function getAppMetadata(appId: string) {
  const client = new AppStoreConnectClient();
  await client.initialize();
  
  // Get app details
  const appData = await fetch(`${API_BASE}/apps/${appId}`);
  
  // Get all localizations
  const localizations = await fetch(`${API_BASE}/apps/${appId}/localizations`);
  
  return {
    app: await appData.json(),
    localizations: await localizations.json()
  };
}

3. Preview Upload Feature

// Upload generated previews to App Store Connect
async function uploadPreview(appId: string, locale: string, previewData: any) {
  const response = await fetch(`${API_BASE}/apps/${appId}/localizations`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      app_id: appId,
      locale: locale,
      screenshots: previewData.screenshots,
      promotional_text: previewData.description
    })
  });
  return response.json();
}

4. Batch Localization Updates

async function batchUpdateLocalizations(appId: string, updates: LocalizationMap) {
  const results = [];
  
  for (const [locale, data] of Object.entries(updates)) {
    const response = await fetch(`${API_BASE}/apps/${appId}/localizations`, {
      method: 'POST',
      body: JSON.stringify({ app_id: appId, locale, ...data })
    });
    results.push({ locale, status: response.ok ? 'success' : 'failed' });
  }
  
  return results;
}

UI Components to Add

  1. App Selector Component

    • Dropdown to select from available apps
    • Display current app metadata
    • Show bundle ID and version info
  2. Localization Manager

    • View all available localizations
    • Edit metadata for each locale
    • Bulk update functionality
  3. Preview Comparison View

    • Side-by-side comparison of store vs generated previews
    • Highlight differences
    • One-click upload to replace
  4. Version Dashboard

    • Display all app versions
    • Show version states (Ready for Sale, In Review, etc.)
    • Track metadata changes

Environment Variables Required

# App Store Connect API Credentials
ASC_KEY_ID=your_key_id
ASC_ISSUER_ID=your_issuer_id
ASC_PRIVATE_KEY=your_private_key_content
ASC_API_URL=http://91.98.132.37:8001

Security Implementation

  1. Store credentials in .env.local (never commit)
  2. Create Next.js API routes as proxy endpoints
  3. Implement rate limiting on API routes
  4. Add request validation and sanitization
  5. Use server-side rendering for sensitive operations

Health Monitoring

// Add health check to monitor API availability
async function checkAPIHealth() {
  try {
    const response = await fetch(`${API_BASE}/health`);
    const data = await response.json();
    
    if (data.status === 'unhealthy') {
      // Reinitialize authentication
      await initializeAuth();
    }
    
    return data;
  } catch (error) {
    console.error('API health check failed:', error);
    return { status: 'error', message: error.message };
  }
}

// Run health check every 5 minutes
setInterval(checkAPIHealth, 5 * 60 * 1000);

CI/CD Integration

# .github/workflows/sync-metadata.yml
name: Sync App Store Metadata

on:
  push:
    branches: [main]
    paths:
      - 'metadata/**'

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Initialize ASC Auth
        run: |
          curl -X POST ${{ secrets.ASC_API_URL }}/auth \
            -H "Content-Type: application/json" \
            -d '{
              "key_id": "${{ secrets.ASC_KEY_ID }}",
              "issuer_id": "${{ secrets.ASC_ISSUER_ID }}",
              "private_key_content": "${{ secrets.ASC_PRIVATE_KEY }}"
            }'
      
      - name: Update Localizations
        run: |
          for file in metadata/*.json; do
            locale=$(basename $file .json)
            curl -X POST ${{ secrets.ASC_API_URL }}/apps/${{ env.APP_ID }}/localizations \
              -H "Content-Type: application/json" \
              -d @$file
          done

Error Handling Strategy

class APIError extends Error {
  constructor(public status: number, message: string) {
    super(message);
  }
}

async function safeAPICall<T>(
  endpoint: string,
  options?: RequestInit
): Promise<T> {
  try {
    const response = await fetch(`${API_BASE}${endpoint}`, options);
    
    if (!response.ok) {
      throw new APIError(response.status, await response.text());
    }
    
    return await response.json();
  } catch (error) {
    if (error instanceof APIError) {
      // Handle specific API errors
      if (error.status === 401) {
        await initializeAuth();
        return safeAPICall(endpoint, options); // Retry
      }
      throw error;
    }
    
    // Network or other errors
    console.error('API call failed:', error);
    throw new Error('Failed to connect to App Store Connect API');
  }
}

Testing Strategy

  1. Mock API responses for unit tests
  2. Integration tests with test App Store Connect account
  3. E2E tests for critical workflows
  4. Load testing for batch operations

Deliverables

  • API client module with authentication
  • UI components for app metadata management
  • Localization editor with batch update
  • Preview upload functionality
  • Health monitoring and error handling
  • CI/CD pipeline for automated sync
  • Documentation and usage examples

Real-World Use Cases

1. Bulk Update App Localizations

async function updateAppLocalizations(appId) {
  // Initialize auth first
  await initializeAuth();

  // Update Spanish localization
  const response = await axios.post(
    `${API_BASE}/apps/${appId}/localizations`,
    {
      app_id: appId,
      locale: 'es-ES',
      name: 'Mi Aplicación',
      subtitle: 'Subtítulo en español',
      description: 'Descripción completa en español...',
      keywords: 'palabra1,palabra2,palabra3',
      promotional_text: 'Texto promocional',
      whats_new: 'Novedades de esta versión'
    }
  );

  console.log('Localization updated:', response.data);
}

2. Monitor All Your Apps

async function getAllAppsInfo() {
  const response = await axios.get(`${API_BASE}/apps?limit=100`);

  for (const app of response.data.apps) {
    console.log(`App: ${app.attributes.name}`);
    console.log(`Bundle ID: ${app.attributes.bundleId}`);
    console.log(`---`);
  }
}

3. Export All Localizations

import requests
import json

def export_all_localizations(app_id):
    # Get all localizations
    response = requests.get(f'{API_BASE}/apps/{app_id}/localizations')
    localizations = response.json()['localizations']

    # Save to file
    with open(f'app_{app_id}_localizations.json', 'w') as f:
        json.dump(localizations, f, indent=2)

    print(f"Exported {len(localizations)} localizations")
    return localizations

4. Automated Version Management

async function getLatestVersion(appId) {
  const response = await axios.get(`${API_BASE}/apps/${appId}/versions`);
  const versions = response.data.versions;

  // Get the latest version
  const latest = versions.sort((a, b) =>
    new Date(b.attributes.createdDate) - new Date(a.attributes.createdDate)
  )[0];

  console.log('Latest version:', latest.attributes.versionString);
  console.log('Status:', latest.attributes.appStoreState);
  return latest;
}

References

Success Metrics

  • Reduce manual metadata updates by 90%
  • Enable one-click preview uploads
  • Support 10+ language localizations
  • Achieve < 2s response time for metadata fetches
  • 99.9% uptime for integration features

Support

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions