Platform Channel Development for Flutter App (iOS)

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Platform Channel Development for Flutter App (iOS)
Complex
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Flutter Platform Channel Development for iOS

Flutter covers 80–90% of tasks through Dart packages. But when you need direct access to native iOS APIs — CoreNFC, NetworkExtension for VPN, AVFoundation with custom session settings, PassKit for Wallet passes — Dart wrappers either don't exist or are outdated by 2 SDK versions. That's when you write a Platform Channel by hand.

Three channel types and when to use each

Flutter provides three mechanisms for communicating with native code.

MethodChannel — request/response. Dart calls a method, Swift responds once. Suitable for most tasks: biometrics, Keychain operations, one-time system API calls. The most common type.

EventChannel — data stream from native code to Dart. Used for subscriptions: sensor data via CoreMotion, Bluetooth connection status via CoreBluetooth, network interface changes via NWPathMonitor. Data flows continuously while Dart listens.

BasicMessageChannel — arbitrary two-way messaging with custom codec. Rare case, needed when the standard StandardMessageCodec (supports String, int, double, List, Map, Uint8List) isn't enough.

Mixing types without reason is unwise: I've seen projects using EventChannel where a single MethodChannel call would suffice — this creates unnecessary subscriptions and memory leaks if StreamController isn't cleaned up when the channel is destroyed.

Most common failure points

Threads and FlutterResult

FlutterResult — an Objective-C callback that Flutter passes to Swift for responding. Main rule: call it exactly once. Call it twice — runtime crash with Call to FlutterResult callback after it has been released.

Typical trap with AVCaptureSession: method launches capture asynchronously, result arrives through completion on background queue. If you don't dispatch result via DispatchQueue.main.async, Flutter sometimes receives the response on an unexpected thread — behavior is unpredictable, bug doesn't always reproduce.

channel.setMethodCallHandler { [weak self] call, result in
  guard call.method == "startCapture" else {
    result(FlutterMethodNotImplemented)
    return
  }
  self?.session.startRunning(completion: { success, error in
    DispatchQueue.main.async {
      if let error = error {
        result(FlutterError(code: "CAPTURE_ERROR",
                            message: error.localizedDescription,
                            details: nil))
      } else {
        result(success)
      }
    }
  })
}

Serialization via StandardMessageCodec

StandardMessageCodec can handle Uint8List, which saves you when transferring small binary data (image preview, encrypted payload). But for objects more complex than a dictionary — manual serialization is still needed. Attempting to pass Data directly without converting to FlutterStandardTypedData causes silent failure: Dart receives null instead of data.

EventChannel and memory leaks

FlutterEventSink must be nulled in onCancel:

final class SensorStreamHandler: NSObject, FlutterStreamHandler {
  private var motionManager = CMMotionManager()
  private var eventSink: FlutterEventSink?

  func onListen(withArguments arguments: Any?,
                eventSink events: @escaping FlutterEventSink) -> FlutterError? {
    eventSink = events
    motionManager.startAccelerometerUpdates(to: .main) { [weak self] data, _ in
      guard let data = data else { return }
      self?.eventSink?(["x": data.acceleration.x, "y": data.acceleration.y])
    }
    return nil
  }

  func onCancel(withArguments arguments: Any?) -> FlutterError? {
    motionManager.stopAccelerometerUpdates()
    eventSink = nil  // critical: without this, there will be accesses to freed object
    return nil
  }
}

Skip eventSink = nil — you'll get EXC_BAD_ACCESS in a few minutes when motionManager tries to send data to an already-destroyed channel.

How we build the channel

Start with defining the contract: method, arguments, return type, error codes. Document in comments on both sides synchronously. This is critical for teams where iOS and Flutter developers are different people.

On the Dart side, wrap MethodChannel in a separate service class with typed API:

class BiometricService {
  static const _channel = MethodChannel('com.app/biometric');

  Future<bool> authenticate(String reason) async {
    try {
      return await _channel.invokeMethod<bool>('authenticate', {'reason': reason}) ?? false;
    } on PlatformException catch (e) {
      if (e.code == 'BIOMETRIC_UNAVAILABLE') return false;
      rethrow;
    }
  }
}

Direct calls to MethodChannel from widgets is an antipattern: lose type safety, error handling spreads throughout the tree.

Test with MockMethodCallHandler in unit tests on Dart side and via XCTest on Swift side. Isolated testing of each part accelerates debugging many times over.

What's included in the work

  • Designing channel contract (method names, argument types, error codes)
  • Implementing Swift handler with proper thread safety
  • Dart service with typed public API
  • Handling edge cases: device doesn't support feature, user denied permission
  • Unit tests for both sides
  • Real device verification (not just simulator — many iOS APIs aren't available in simulator)

Timeline

3–5 days. Simple MethodChannel for one system call — 2–3 days with tests. EventChannel with continuous data stream and lifecycle management — 4–5 days. Cost calculated individually after requirements analysis.