Software Engineering & Digital Products for Global Enterprises since 2006
CMMi Level 3SOC 2ISO 27001
Menu
View all services
Staff Augmentation
Embed senior engineers in your team within weeks.
Dedicated Teams
A ring-fenced squad with PM, leads, and engineers.
Build-Operate-Transfer
We hire, run, and transfer the team to you.
Contract-to-Hire
Try the talent. Convert when you're ready.
ForceHQ
Skill testing, interviews and ranking — powered by AI.
RoboRingo
Build, deploy and monitor voice agents without code.
MailGovern
Policy, retention and compliance for enterprise email.
Vishing
Test and train staff against AI-driven voice attacks.
CyberForceHQ
Continuous, adaptive security training for every team.
IDS Load Balancer
Built for Multi Instance InDesign Server, to distribute jobs.
AutoVAPT.ai
AI agent for continuous, automated vulnerability and penetration testing.
Salesforce + InDesign Connector
Bridge Salesforce data into InDesign to design print catalogues at scale.
View all solutions
Banking, Financial Services & Insurance
Cloud, digital and legacy modernisation across financial entities.
Healthcare
Clinical platforms, patient engagement, and connected medical devices.
Pharma & Life Sciences
Trial systems, regulatory data, and field-force enablement.
Professional Services & Education
Workflow automation, learning platforms, and consulting tooling.
Media & Entertainment
AI video processing, OTT platforms, and content workflows.
Technology & SaaS
Product engineering, integrations, and scale for tech companies.
Retail & eCommerce
Shopify, print catalogues, web-to-print, and order automation.
View all industries
Blog
Engineering notes, opinions, and field reports.
Case Studies
How clients shipped — outcomes, stack, lessons.
White Papers
Deep-dives on AI, talent models, and platforms.
Portfolio
Selected work across industries.
View all resources
About Us
Who we are, our story, and what drives us.
Co-Innovation
How we partner to build new products together.
Careers
Open roles and what it's like to work here.
News
Press, announcements, and industry updates.
Leadership
The people steering MetaDesign.
Locations
Gurugram, Brisbane, Detroit and beyond.
Contact Us
Talk to sales, hiring, or partnerships.
Request TalentStart a Project
Mobile Development

Mixing Native Swift iOS Code and Flutter Code to Develop Mobile Apps

SS
Sukriti Srivastava
Technical Content Lead
April 13, 2023
14 min read
Mixing Native Swift iOS Code and Flutter Code to Develop Mobile Apps — Mobile Development | MetaDesign Solutions

Introduction: Why Combine Swift and Flutter

Mixing native Swift iOS code with Flutter unlocks the best of both worlds — Swift's type safety, direct access to Apple frameworks (UIKit, Core Data, Core Animation, ARKit, HealthKit), and native performance for computationally intensive tasks, combined with Flutter's cross-platform UI, hot-reload development speed, and rich widget library.

This hybrid approach is essential when: you have existing Swift codebases that need cross-platform UI layers, your app requires platform-specific APIs (Face ID, Apple Pay, WidgetKit) that Flutter plugins don't adequately support, or certain screens demand native iOS performance characteristics that Flutter's rendering engine cannot match (complex MapKit overlays, Metal-based rendering). Understanding the communication bridge between Dart and Swift is the foundation for successful hybrid development.

MethodChannel: Bidirectional Communication Bridge

Flutter's MethodChannel is the primary communication mechanism between Dart and Swift:

  • Channel Setup (Dart): Create a MethodChannel with a unique channel name — const channel = MethodChannel('com.example.app/native'). Invoke Swift methods with channel.invokeMethod('getBatteryLevel') and receive results as typed Dart objects.
  • Channel Setup (Swift): In AppDelegate.swift, register a FlutterMethodChannel with the matching name. Handle incoming calls in setMethodCallHandler — switch on call.method to dispatch to appropriate Swift functions.
  • Type Marshalling: Platform channels support standard types — int, double, String, bool, List, Map, and Uint8List. Complex objects must be serialised to Map<String, dynamic> on the Dart side and [String: Any] dictionaries in Swift.
  • Error Handling: Wrap Swift calls in do-catch blocks and return errors via result(FlutterError(code:, message:, details:)) — Dart receives these as PlatformException for structured error handling.
  • Async Patterns: All MethodChannel calls are asynchronous — Swift handlers can dispatch to background queues for heavy work and call result() when complete.

EventChannel: Streaming Data from Swift to Flutter

EventChannel enables continuous data streaming from native Swift to Flutter — ideal for sensor data, location updates, and real-time events:

  • Stream Setup: Create an EventChannel in Dart and listen with channel.receiveBroadcastStream().listen((event) { }). In Swift, implement FlutterStreamHandler protocol with onListen and onCancel callbacks.
  • Location Streaming: Use CLLocationManager in Swift to continuously stream GPS coordinates to Flutter — eventSink?(["lat": location.latitude, "lng": location.longitude]) pushes each update to the Dart stream.
  • Sensor Data: Stream accelerometer, gyroscope, or barometer data from CMMotionManager — Flutter receives typed sensor events at configurable intervals without polling overhead.
  • Lifecycle Management: onCancel must stop native resources (location manager, motion manager, timers) — preventing battery drain and memory leaks when Flutter disposes the stream.
  • Backpressure: Native code produces events regardless of Flutter's consumption rate — implement throttling or sampling in the Swift handler to prevent event queue overflow.

PlatformViews: Embedding Native iOS Views in Flutter

PlatformViews embed native UIKit views directly into Flutter's widget tree:

  • UiKitView Widget: Use UiKitView(viewType: 'native-mapview') in Dart to embed a native MKMapView, WKWebView, or custom UIKit component within the Flutter layout.
  • Factory Registration: Register a FlutterPlatformViewFactory in AppDelegate.swift — the factory creates and returns the native UIView subclass when Flutter requests it.
  • Hybrid Composition: Flutter 3.x uses hybrid composition mode on iOS — rendering native views within Flutter's compositing pipeline. This delivers correct z-ordering, gestures, and accessibility, but adds rendering overhead.
  • Performance Tradeoffs: Each PlatformView creates a separate iOS rendering surface — use sparingly. For maps, prefer the google_maps_flutter plugin which is already optimised. Reserve PlatformViews for truly unique native components.
  • Gesture Conflicts: Configure gestureRecognizers parameter to resolve conflicts between Flutter and native gesture systems — specify which gestures the native view should claim versus pass to Flutter.

Pigeon: Type-Safe Code Generation for Platform Channels

Flutter's Pigeon package eliminates manual MethodChannel boilerplate with type-safe code generation:

  • Schema Definition: Define API contracts in a Dart file using @HostApi() (Dart calls Swift) and @FlutterApi() (Swift calls Dart) annotations — Pigeon generates type-safe Swift and Dart code automatically.
  • Type Safety: Pigeon generates strongly typed method signatures — no more string-based method names, untyped argument dictionaries, or runtime type casting errors. Compile-time checks catch interface mismatches.
  • Complex Types: Define data classes with @ConfigurePigeon — Pigeon generates matching Swift structs and Dart classes with proper serialisation, supporting nested objects, enums, and nullable fields.
  • Async Support: Generated Swift protocols use completion handlers for async operations — Swift implementations call the completion with typed results or errors.
  • Migration Path: Migrate existing MethodChannel code incrementally — define the Pigeon schema matching your current API, generate code, and replace manual channel handlers with generated implementations.

Pigeon is the recommended approach for production apps with complex native interfaces — reducing bugs and maintenance overhead significantly.

Transform Your Publishing Workflow

Our experts can help you build scalable, API-driven publishing systems tailored to your business.

Book a free consultation

Flutter Plugin Architecture for Reusable Native Modules

Packaging native Swift code as Flutter plugins creates reusable, distributable modules:

  • Plugin Structure: Use flutter create --template=plugin --platforms=ios my_plugin — generates a plugin with Dart API, Swift implementation, and example app.
  • Federated Plugins: For multi-platform plugins, use the federated plugin pattern — separate packages for the platform interface (my_plugin_platform_interface), iOS implementation (my_plugin_ios), and app-facing package (my_plugin).
  • Swift Package Manager: Flutter 3.24+ supports Swift Package Manager for native dependency management — replacing CocoaPods with faster, Apple-native dependency resolution.
  • Platform Interface Pattern: Define an abstract MyPluginPlatform class extending PlatformInterface — platform implementations register themselves, enabling testability and platform-specific behavior.
  • Example App Testing: Every plugin includes an example app — test the plugin in a real Flutter app context with integration tests using flutter drive.

Testing Hybrid Swift-Flutter Applications

Hybrid apps require multi-layer testing strategies:

  • Dart Unit Tests: Mock platform channels with TestDefaultBinaryMessengerBinding — test Dart code in isolation without native dependencies. Verify method invocations, argument serialisation, and error handling.
  • Swift Unit Tests: Test native Swift code with XCTest — verify Core Data operations, API clients, and business logic independently from Flutter. Run with xcodebuild test.
  • Integration Tests: Use integration_test package to test the full Dart-to-Swift communication path — verify that platform channel calls produce correct results on real iOS devices or simulators.
  • Pigeon Contract Tests: With Pigeon-generated code, write tests against the generated protocol — ensuring Swift implementations satisfy the contract defined in the Dart schema.
  • CI/CD Pipeline: Configure GitHub Actions or Codemagic with macOS runners — build the Flutter app, run Dart tests, execute XCTest suites, and perform integration tests on iOS Simulator.

Production Patterns and MDS Hybrid Development Services

Production hybrid apps follow established architectural patterns:

  • Add-to-App: Embed Flutter as a module in an existing Swift app — FlutterEngine initialises in AppDelegate and FlutterViewController presents Flutter screens. Ideal for gradually migrating Swift apps to Flutter.
  • Flutter-First: Start with a Flutter app and use platform channels for iOS-specific features — Face ID via LocalAuthentication, Apple Pay via PassKit, WidgetKit for home screen widgets, and ARKit for augmented reality.
  • Module Boundaries: Define clear boundaries — Flutter owns UI and business logic, Swift owns platform-specific integrations. Use a service locator pattern in Dart to abstract native dependencies.
  • Memory Management: Monitor memory usage across both runtimes — Flutter's Dart VM and Swift's ARC can accumulate memory independently. Use Instruments (Xcode) and Flutter DevTools Memory Profiler together.
  • App Store Compliance: Hybrid apps must meet Apple's review guidelines — ensure native components use Apple's standard APIs, respect user privacy (ATT framework), and implement proper data handling per App Store requirements.

MDS specialises in hybrid Swift-Flutter architectures — delivering apps that leverage native iOS capabilities while maintaining cross-platform efficiency and shared codebases.

FAQ

Frequently Asked Questions

Common questions about this topic, answered by our engineering team.

Communication uses Flutter's MethodChannel (for request-response calls) and EventChannel (for streaming data). Create channels with matching names in both Dart and Swift, use invokeMethod for bidirectional calls, and handle typed responses. For production apps, use the Pigeon package for type-safe code generation that eliminates string-based method names and untyped arguments.

Mix Swift with Flutter when your app needs platform-specific APIs (Face ID, Apple Pay, ARKit, WidgetKit) that Flutter plugins don't adequately support, when you have existing Swift codebases to gradually migrate, when certain screens require native iOS performance (Metal rendering, complex MapKit overlays), or when regulatory compliance requires native security implementations.

Pigeon is Flutter's code generation tool for type-safe platform channels. You define API contracts in Dart with @HostApi annotations, and Pigeon generates matching Swift protocols and Dart classes with proper serialisation. It eliminates manual string-based method names, untyped dictionaries, and runtime casting errors — providing compile-time safety for native interfaces.

Use PlatformViews with the UiKitView widget — register a FlutterPlatformViewFactory in AppDelegate.swift that creates and returns native UIView subclasses. Flutter's hybrid composition mode renders native views within the Flutter compositing pipeline. Use sparingly due to rendering overhead, and configure gestureRecognizers to resolve gesture conflicts.

Use multi-layer testing: Dart unit tests with mocked platform channels, Swift XCTest suites for native logic, integration tests with the integration_test package for full Dart-to-Swift paths, and Pigeon contract tests against generated protocols. Run all layers in CI/CD with macOS runners (GitHub Actions or Codemagic) targeting iOS Simulator.

Discussion

Join the Conversation

Ready when you are

Let's build something great together.

A 30-minute call with a principal engineer. We'll listen, sketch, and tell you whether we're the right partner — even if the answer is no.

Talk to a strategist
Need help with your project? Let's talk.
Book a call