addUrlScheme method

OperationResult addUrlScheme(
  1. String scheme
)

Adds a URL scheme to the Info.plist for deep linking.

URL schemes allow other apps to open your app using a custom URL. For example, myapp://path/to/content would open your app.

Parameters

  • scheme: The URL scheme to register (e.g., myapp, flutter-example)

Returns

  • Success if the scheme was added or already exists
  • Failure if the plist could not be found or modified

Example

final manager = IosManager('/path/to/project');

// Add a URL scheme
final result = manager.addUrlScheme('myapp');

// Now your app can be opened with: myapp://some/path

Plist Changes

This adds the following structure to Info.plist:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

Implementation

OperationResult addUrlScheme(String scheme) {
  final plistFile = File(_infoPlistPath);

  if (!plistFile.existsSync()) {
    return const Failure(
      'Could not find Info.plist. '
      'Are you in a Flutter project root directory?',
    );
  }

  try {
    String content = plistFile.readAsStringSync();

    // Check if CFBundleURLTypes already exists
    if (content.contains('<key>CFBundleURLTypes</key>')) {
      // Check if this specific scheme already exists
      if (content.contains('<string>$scheme</string>')) {
        return Success('URL scheme "$scheme" already exists.');
      }

      // Find the CFBundleURLSchemes array and add to it
      final schemesPattern = RegExp(
        r'(<key>CFBundleURLSchemes</key>\s*<array>)',
        multiLine: true,
      );

      if (schemesPattern.hasMatch(content)) {
        content = content.replaceFirst(
          schemesPattern,
          '\$1\n\t\t\t\t<string>$scheme</string>',
        );
        plistFile.writeAsStringSync(content);
        return Success('Successfully added URL scheme "$scheme".');
      }
    }

    // CFBundleURLTypes doesn't exist, so add it from scratch
    final dictClosePattern = RegExp(r'</dict>\s*</plist>\s*$');
    final match = dictClosePattern.firstMatch(content);

    if (match == null) {
      return const Failure(
        'Invalid Info.plist format: could not find closing </dict> tag.',
      );
    }

    // Build the complete CFBundleURLTypes structure
    final urlTypesBlock = '''
\t<key>CFBundleURLTypes</key>
\t<array>
\t\t<dict>
\t\t\t<key>CFBundleURLSchemes</key>
\t\t\t<array>
\t\t\t\t<string>$scheme</string>
\t\t\t</array>
\t\t</dict>
\t</array>
''';

    content = content.replaceFirst(
      dictClosePattern,
      '$urlTypesBlock</dict>\n</plist>\n',
    );

    plistFile.writeAsStringSync(content);
    return Success('Successfully added URL scheme "$scheme".');
  } on FileSystemException catch (e) {
    return Failure('Failed to update Info.plist: ${e.message}');
  }
}