inject method

String inject(
  1. String content
)

Injects custom deep linking logic into AppDelegate.swift. Returns the modified Swift code. Idempotent.

Implementation

String inject(String content) {
  if (content.contains('custom_deeplink') || content.contains('methodChannel')) {
    // Already injected/configured
    return content;
  }

  var modified = content;

  // 1. Inject state variables after the class declaration
  final classRegex = RegExp(r'class\s+AppDelegate\s*:[^{]*\{');
  final classMatch = classRegex.firstMatch(modified);
  if (classMatch == null) {
    throw CodeInjectionException('Could not locate class AppDelegate declaration in AppDelegate.swift');
  }

  final stateVars = '''

private var initialLink: String?
private var methodChannel: FlutterMethodChannel?
''';
  modified = modified.substring(0, classMatch.end) + stateVars + modified.substring(classMatch.end);

  // 2. Inject MethodChannel initialization in didFinishLaunchingWithOptions
  final launchRegex = RegExp(
    r'func\s+application\(\s*_\s+application:\s*UIApplication,\s*didFinishLaunchingWithOptions[^)]*\)\s*->\s*Bool\s*\{'
  );
  final launchMatch = launchRegex.firstMatch(modified);
  if (launchMatch == null) {
    throw CodeInjectionException('Could not locate didFinishLaunchingWithOptions method in AppDelegate.swift');
  }

  final methodChannelSetup = '''

  if let controller = window?.rootViewController as? FlutterViewController {
    methodChannel = FlutterMethodChannel(name: "custom_deeplink", binaryMessenger: controller.binaryMessenger)
    methodChannel?.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "getInitialLink" {
        result(self?.initialLink)
        self?.initialLink = nil
      } else {
        result(FlutterMethodNotImplemented)
      }
    })
  }
''';

  modified = modified.substring(0, launchMatch.end) + methodChannelSetup + modified.substring(launchMatch.end);

  // 3. Inject application(_:open:options:) and application(_:continue:restorationHandler:)
  final openUrlAndUserActivityMethods = '''

override func application(
  _ app: UIApplication,
  open url: URL,
  options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
  initialLink = url.absoluteString
  methodChannel?.invokeMethod("onLink", arguments: initialLink)
  return super.application(app, open: url, options: options)
}

override func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
  if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
    if let url = userActivity.webpageURL {
      initialLink = url.absoluteString
      methodChannel?.invokeMethod("onLink", arguments: initialLink)
    }
  }
  return super.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
''';

  final lastBraceIndex = modified.lastIndexOf('}');
  if (lastBraceIndex == -1) {
    throw CodeInjectionException('AppDelegate.swift is missing a closing brace.');
  }

  modified = modified.substring(0, lastBraceIndex) + openUrlAndUserActivityMethods + modified.substring(lastBraceIndex);

  return modified;
}