testAasaFile static method

Future<VerificationResult> testAasaFile(
  1. String domain,
  2. String? teamId,
  3. String? bundleIdentifier
)

Test Apple App Site Association (AASA) file

Implementation

static Future<VerificationResult> testAasaFile(
  String domain,
  String? teamId,
  String? bundleIdentifier,
) async {
  final url = Uri.parse(
    'https://$domain/.well-known/apple-app-site-association',
  );

  try {
    final response = await http.get(
      url,
      headers: {'Accept': 'application/json'},
    );

    // Check status code
    if (response.statusCode != 200) {
      return VerificationResult(
        checkName: 'AASA File Accessibility',
        status: VerificationStatus.error,
        message: 'AASA file returned status ${response.statusCode}',
        fixSuggestion:
            'Ensure the AASA file is accessible at https://$domain/.well-known/apple-app-site-association',
        details: {'statusCode': response.statusCode, 'url': url.toString()},
      );
    }

    // Check Content-Type
    final contentType = response.headers['content-type'] ?? '';
    if (!contentType.contains('application/json') &&
        !contentType.contains('application/pkcs7-mime')) {
      return VerificationResult(
        checkName: 'AASA File Content-Type',
        status: VerificationStatus.warning,
        message:
            'AASA file Content-Type is "$contentType" (expected application/json)',
        fixSuggestion:
            'Ensure the server returns Content-Type: application/json for the AASA file',
        details: {'contentType': contentType},
      );
    }

    // Parse JSON
    Map<String, dynamic> json;
    try {
      json = jsonDecode(response.body) as Map<String, dynamic>;
    } catch (e) {
      return VerificationResult(
        checkName: 'AASA File JSON Validity',
        status: VerificationStatus.error,
        message: 'AASA file is not valid JSON: $e',
        fixSuggestion: 'Ensure the AASA file is valid JSON',
      );
    }

    // Check applinks structure
    final applinks = json['applinks'] as Map<String, dynamic>?;
    if (applinks == null) {
      return VerificationResult(
        checkName: 'AASA File Structure',
        status: VerificationStatus.error,
        message: 'AASA file missing "applinks" key',
        fixSuggestion: 'Ensure the AASA file contains an "applinks" object',
      );
    }

    final details = applinks['details'] as List<dynamic>?;
    if (details == null || details.isEmpty) {
      return VerificationResult(
        checkName: 'AASA File Structure',
        status: VerificationStatus.error,
        message: 'AASA file missing "applinks.details" array',
        fixSuggestion: 'Ensure the AASA file contains applinks.details array',
      );
    }

    // Check if bundle identifier and team ID match
    if (teamId != null && bundleIdentifier != null) {
      final expectedAppId = '$teamId.$bundleIdentifier';
      bool foundMatch = false;

      for (final detail in details) {
        if (detail is Map<String, dynamic>) {
          final appId = detail['appID'] as String?;
          if (appId == expectedAppId) {
            foundMatch = true;
            break;
          }
        }
      }

      if (!foundMatch) {
        return VerificationResult(
          checkName: 'AASA File App ID Match',
          status: VerificationStatus.error,
          message: 'AASA file does not contain appID "$expectedAppId"',
          fixSuggestion:
              'Ensure the AASA file contains an entry with appID "$expectedAppId"',
          details: {'expectedAppId': expectedAppId},
        );
      }
    }

    return VerificationResult(
      checkName: 'AASA File Verification',
      status: VerificationStatus.success,
      message: 'AASA file is accessible and valid',
      details: {'url': url.toString(), 'domain': domain},
    );
  } catch (e) {
    return VerificationResult(
      checkName: 'AASA File Accessibility',
      status: VerificationStatus.error,
      message: 'Failed to fetch AASA file: $e',
      fixSuggestion:
          'Check network connectivity and ensure the domain is correctly configured',
    );
  }
}