# In-Depth Guide to work with platform channels by integrating 3rd Party SDK: IOS

In this article we will learn how method channels and event channels work. Also, how we will integrate 3rd party SDK in our Flutter app and use them to invoke native methods using method channels and stream data using event channels. In addition to that, we will structure the code and explore the prerequisites to start writing native code.

## Platform Channels

Platform channels are used to communicate with the native side using event streams or method calls. This communication is set up by calling platform-specific APIs from Dart code, and it provides a way to share data among platform and dart sides. These APIs can be called in the language supported by the specific platform, e.g., Java or Kotlin for Android, Swift or Objective-C for IOS.

### MethodChannel
They are used to invoke methods with or without arguments at the platform side.  And on the platform side, we receive the method calls and send back a result. The method call is asynchronous. On Android, we use `MethodChannel` and on IOS we use ` FlutterMethodChannel` for handling method calls.

### EventChannel 
They are used to stream data from platform to dart side. These stream requests are encoded into binary at the platform side and then we can receive these stream requests by subscribing to them. The stream data is then decoded into Dart.


![Flutter.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1647967765814/ZgWIBvADc.png)

The above flow diagram explains the way our Dart code communicates with native code and then we use the Dart SDK as a plugin inside our Flutter app. From the Dart SDK, through method calls, we invoke platform-specific APIs and through the event channel, we get the binary encoded streamed data. Our IOS side uses `FlutterMethodChannel` to implement `MethodChannel` and `FlutterEventChannel`
and `FlutterStreamHandler` to implement `EventChannel`.

## Getting Started
Let's create our Flutter App. We will be using [`CometChatSDK`](https://www.cometchat.com/) v3 to demonstrate the working of `Platform Channels`.


```
flutter create --org com.example --template=plugin --platforms=android,ios -a java flutter_comet_chat_sdk
``` 
Here, we have created a flutter plugin with Java support for Android and Swift for IOS.

### Project Structure

```
.
└── flutter_comet_chat_sdk/
    ├── android/
    │   ├── gradle
    │   ├── src/
    │   │   └── main/java/com/example/flutter_comet_chat_sdk/
    │   │       ├── FlutterCometChatSdkPlugin.java
    │   │       ├── EventChannelHelper.java
    │   │       ├── MethodChannelHandler.java
    │   │       └── Helper/
    │   └── build.gradle
    ├── example/
    │   ├── android
    │   ├── ios
    │   ├── lib/
    │   │   └── main.dart
    │   ├── test
    │   └── pubspec.yaml
    ├── ios/
    │   ├── Assets
    │   ├── Classes/
    │   │   ├── SwiftFlutterCometChatSdkPlugin.swift
    │   │   ├── FlutterCometChatSdkPlugin.m
    │   │   ├── FlutterCometChatSdkPlugin.h
    │   │   ├── EventChannelHandler.swift
    │   │   ├── Helpers/
    │   │   └── Listeners/
    │   └── flutter_comet_chat_sdk.podspec
    ├── lib/
    │   ├── src/
    │   │   ├── listener/
    │   │   ├── model/
    │   │   ├── comet_chat_dart.dart
    │   │   ├── comet_chat_constants.dart
    │   │   ├── comet_constants_keys.dart
    │   │   └── error_details.dart
    │   └── flutter_comet_chat_sdk.dart
    ├── test
    └── pubspec.yaml
``` 

We will use the android and ios folders in the root directory to write our Android and IOS native code respectively. Under the lib folder in the root directory, we will write Dart side implementation of method channels and event channels. In that file, we will subscribe to streams, add listeners, serialize data and add calls to invoke the method on the native side. Then under the example folder, we will test the Dart plugin methods.

### Integrating 3rd party SDK with IOS
We are using `CometChatSdk`, so we will add that to our IOS file. So as per the docs we will add the SDK as a dependency and set the platform version to 11.0.

Under the file `ios/flutter_comet_chat_sdk.podspec` add:
```
. . .
s.dependency 'CometChatPro', '3.0.900'
s.platform = :ios, '11.0'
. . .
```

Or you can add your `.framework` project file in the `ios` folder and under the `podspec` file add the path to the `framework` file.
```
. . .
s.platform = :ios, '11.0'
s.preserve_paths = 'ProjectName.framework'
s.xcconfig = { 'OTHER_LDFLAGS' => '-framework ProjectName' }
s.vendored_frameworks = 'ProjectName.framework'
. . 
```

Also, under the `example/ios/Podfile` add the platform version as 11.0 as the SDK supports versions equal or above that. To support running simulator with `Comet Chat` we need to exclude arm64 i386.
```
. . .
platform :ios, '11.0'
. . .
. . .

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
    target.build_configurations.each do |build_configuration|
      build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386'
    end
  end
end
```

Now, go to `example/ios` run `pod install` and then open the `example/ios` folder in Xcode and run the app once. We will use Xcode to run and write swift code. Sometimes the print statements mentioned in the swift code don't show in VS code terminal. So it's better to use Xcode to run and debug while working with IOS native side.

So, we have opened the ios folder under the example folder not the ios in the root directory. But the code for SDK needs to be written in the root ios/Classes folder only. We can access the folder under the Pods folder in Xcode. On the left side, in Xcode, we have Runner and Pods folder. If we go inside

**`Pods -> Development Pods -> flutter_comet_chat_sdk ->  .. -> .. -> example -> iOS -> .symlinks
-> plugins -> flutter_comet_chat_sdk -> iOS -> Classes ->`**

There we can find the Classes folder with the `SwiftFlutterCometChatSdk.swift` file. In this folder, we will implement our platform channel. Or for easy access, you can drag and drop the `ios/Classes` folder in the Xcode project and add it as a reference.

Now the setup is ready, we will use some of the `Comet Chat` SDK methods to see how we can implement our Platform channel.

### Creating MethodChannel

We will define our `FlutterMethodChannel` inside `SwiftFlutterCometChatSdk.swift` file. To interact with the host platform and set up Platform channels we need to implement the interface `FlutterPlugin`. It provides a method to set up and register the plugin. Under the register method, we will set up the `FlutterMethodChannel` and `FlutterEventChannels`. 

The register method provides `FlutterPluginRegistrar` which provides a method to add `MethodChannel` and set up communication between Dart and IOS. Also, the `FlutterPluginRegistrar` method `messenger()`, returns a `FlutterBinaryMessenger` which helps in asynchronous message passing between Dart and IOS using binary messages. These binary messages are used by the event channels to stream data. 

The `addMethodCallDelegate` registers the `MethodChannel` to receive method calls from the Dart side.

Lastly, we have the `handle` method that is invoked when `MethodChannel` is defined and the native side is ready to receive method calls.

```
import Flutter
import UIKit
import CometChatPro

public class SwiftFlutterCometChatSdkPlugin: NSObject, FlutterPlugin {
    public override init() {
        super.init()
    }
    
  public static func register(with registrar: FlutterPluginRegistrar) {
      let instance = SwiftFlutterCometChatSdkPlugin()
      instance.setupChannel(registrar: registrar)
  }
    
  private func setupChannel(registrar: FlutterPluginRegistrar){
      let channel = FlutterMethodChannel(name: "plugins.flutter.io/comet_chat_dart", binaryMessenger: registrar.messenger())
      registrar.addMethodCallDelegate(self, channel: channel)
    }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
      let args = call.arguments as? [String: Any]

      switch call.method {
      case "initialize":
          result("success")
      default:
          result(FlutterMethodNotImplemented)
      }
  }
}
```

Here we have created our `MethodChannel` with the name `plugins.flutter.io/comet_chat_dart`. The same name we will be using on the Dart side.

On the Dart side, we have the `src` folder and `flutter_comet_chat_sdk.dart` file. Under the `flutter_comet_chat_sdk.dart` we will define the exports for the plugin and In the file `src/comet_chat_dart.dart` we will set up our `MethodChannel` and `EventChannel`. 

First, we will define the `MethodChannel` and then use the invoke method to call the native methods.

```
import 'dart:async';
import 'dart:convert';

/// Comet Chat utility package.
///
/// This is a preliminary API.
import 'package:flutter/services.dart';
import 'package:flutter_comet_chat_sdk/flutter_comet_chat_sdk.dart';

/// Entry point for the CometChat Dart.
class FlutterCometChatDart {
  static const MethodChannel channel =
      MethodChannel('plugins.flutter.io/comet_chat_dart');

  init() async {
    try {
      dynamic res = await FlutterCometChatDart.channel.invokeMethod('initialize');
      return res;
    } on PlatformException catch (e) {
      throw FlutterCometChatDart.convertException(e);
    }
  }

  static Exception convertException(PlatformException err) {
    return ErrorDetails(
        errorCode: err.code, errorDescription: err.message ?? "");
  }
}

```

This is how we are invoking the method `init` defined with the key `initialize` on the IOS side. 

### How to pass arguments from MethodChannels?

Let's see how we can pass function arguments from the Dart side to the native side. Also, we will send back the result from the native to the Dart side using `MethodChannel`.

So, on the native side, we will define a helper method `Helpers/LoginLogoutHelper.swift` to handle the authentication for `Comet Chat`. The initialize method has two arguments `appId` and `appSettings`. The `appId` is a String so we can directly share it through `MethodChannel`, but `appSettings` is an object of class `AppSettings` so for that we need to create it in the form of key-value pair and then we will map those key values into `AppSettings` class at Dart side.

The `initialize` method provides two callbacks based on success or error and we are returning the status based on the callback. Later, we will use this to pass as a `FlutterResult`.

```
import Foundation
import CometChatPro

public class LoginLogoutHelper {
    public init() {

    }
    
    public func initialize(appId: String?, appSettings:  Dictionary<String, Any>?) -> Any {
        let mySettings: AppSettings = Helper.getAppSettings(value: appSettings)
        var result: Any = "success";
  
        if let tAppId = appId {
            CometChat.init(appId: tAppId ,appSettings: mySettings, onSuccess: { (isSuccess) in
                      if (isSuccess) {
                          print("CometChat Pro SDK intialise successfully.")
                          result = true
                      }
                  }) { (error) in
                          print("CometChat Pro SDK failed intialise with error: \(error.errorDescription)")
                      result = error.errorDescription
                  }
        }
        return result
    }
}

```

**`Helpers/Helper.swift`**  
Adding the method to get key-value data for `AppSettings` object.
```
import CometChatPro
import Foundation

public enum Helper {
    static func getAppSettings(value: [String: Any]?) -> AppSettings {
        if value != nil {
            let region = value?["region"] as? String
            let subscriptionType = value?["subscriptionType"] as? String
            let roles = value?["roles"] as? [String]

            if let tRegion = region,
               let tSubscriptionType = subscriptionType
            {
                var appSettings = AppSettings.AppSettingsBuilder().setRegion(region: tRegion).build()

                switch tSubscriptionType {
                case "ALL_USERS":
                    appSettings = AppSettings.AppSettingsBuilder().subscribePresenceForAllUsers().setRegion(region: tRegion).build()
                case "FRIENDS":
                    appSettings = AppSettings.AppSettingsBuilder().subscribePresenceForFriends().setRegion(region: tRegion).build()
                case "ROLES":
                    appSettings = AppSettings.AppSettingsBuilder().subcribePresenceForRoles(roles: roles ?? []).setRegion(region: tRegion).build()
                case "NONE":
                    appSettings = AppSettings.AppSettingsBuilder().setRegion(region: tRegion).build()
                default:
                    return appSettings
                }
                return appSettings
            }
        }
        return AppSettings.AppSettingsBuilder().subscribePresenceForAllUsers().setRegion(region: "US").build()
    }
}

```

Now, we will call the initialize method in our handle method. Also, we are mapping the arguments coming from the Dart side with their respective key values.

```
. . .
private var loginLogoutHelper: LoginLogoutHelper?

 public override init() {
        super.init()
        loginLogoutHelper = LoginLogoutHelper()
    }
    
. . .

public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
      let args = call.arguments as? [String: Any]

      switch call.method {
      case "initialize":
          let appId = args?["appId"] as? String
          let appSettings = args?["appSettings"] as? Dictionary<String, Any>
          
          let res = loginLogoutHelper?.initialize(appId: appId, appSettings: appSettings)
          result(res)
. . .
. . .
```

On the Dart side, we will define the `initialize` method, pass the required arguments, define the model classes and use the result passed by the method channel.

```
init(String appId, AppSettings appSettings,
      CallbackListener callbackListener) async {
    assert(appId != '');
    assert(appSettings.region != '');

    try {
      dynamic res = await FlutterCometChatDart.channel.invokeMethod(
          'initialize', {'appId': appId, 'appSettings': appSettings.toJson()});

      if (res == true) {
        callbackListener.onSuccess(true);
      } else {
        callbackListener.onError(res);
      }
    } on PlatformException catch (e) {
      throw FlutterCometChatDart.convertException(e);
    }
  }
```

For `AppSettings` class we need to define an `AppSettingsBuilder` class that will set the values that are under `AppSettings` class. Basically, the builder class will have set methods and a build method that will return the object of `AppSettings` class after setting the field values.
Check the `AppSettings` code [`here`](https://github.com/vishalsh2299/flutter_comet_chat_sdk/tree/main/lib/src/model/app_settings).

Then we have a `CallbackListener` class that has methods `onSuccess` and `onError` and based on the response from `invokeMethod` we are calling the `onSuccess` or `onError` method.
```
class CallbackListener {
  Function onSuccess;
  Function onError;
  CallbackListener({required this.onSuccess, required this.onError});
}
``` 

So, now the MethodChannel is ready, we can use this method under our example flutter app.
```
 AppSettings appSettings = (AppSettingsBuilder()
          ..subscribePresenceForAllUsers()
          ..setRegion("US"))
        .build();

    _cometChat.init(
      Constant.appID,
      appSettings,
      CallbackListener(
        onSuccess: (dynamic value) {
          print("SUCCESSFULLY INITIALIZED $value");
        },
        onError: (dynamic error) {
          print("FAILED TO INITIALIZE $error");
        },
      ),
    );
```


### Creating EventChannel

We will create an `EventChannelHandler` class that will handle our event channel methods. To implement this we will use `FlutterStreamHandler`. This `FlutterStreamHandler` exposes the event stream to the Dart side.

Now, we have to override two methods- `onListen` and `onCancel`. Under onListen method, we will set the `FlutterEventSink` with the passed event and in `onCancel` method, we will set the event sink to nil.

Also, there is an `init` method to initialize the `FlutterEventChannel` with the passed name and binary messenger. Then we will register the stream handler with the name on this channel using `setStreamHandler` method.

Since we are defining this as a generic class for all the `EventChannels`, we will define two more methods - success and error to set the value of the event sink using the object of `EventChannelHandler` class.

```
import Foundation
import Flutter

public class EventChannelHandler: NSObject, FlutterStreamHandler {
    private var eventSink: FlutterEventSink?
    
    public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        self.eventSink = events
        return nil
    }
    
    public func onCancel(withArguments arguments: Any?) -> FlutterError? {
        eventSink = nil
        return nil
    }
    
    public init(id: String, messenger: FlutterBinaryMessenger) {
        super.init()
        let eventChannel = FlutterEventChannel(name: id, binaryMessenger: messenger)
        eventChannel.setStreamHandler(self)
    }
    
    
    public func success(event: Any?) throws {
        if eventSink != nil {
            eventSink?(event)
        }
    }
    
    public func error(code: String, message: String?, details: Any? = nil) {
        if eventSink != nil {
            eventSink?(FlutterError(code: code, message: message, details: details))
        }
    }
    
}

```


### How to transfer data through EventChannel?

Now, we will see how we can transfer data through streams. So, first, we will define an `EventChannel` and subscribe to the streams on the Dart side.

Create an object of `EventChannelHandler` class.
```
private var loginEventTrigger: EventChannelHandler?
```

Then under setupChannel method add the declaration for loginEventTrigger with the name as id and pass the binary messenger.
```
loginEventTrigger = EventChannelHandler(
          id: "plugins.flutter.io/login_event_trigger",
          messenger: registrar.messenger()
      )
```

To use this trigger we will add one more method from `Comet Chat` SDK, the login method. So under the handle method we will add one more case value, calling the `selectLoginMethod` defined in our `LoginLogutHelper` class.

```
case "login":
          let uid = args?["UID"] as? String
          let authKey = args?["authKey"] as? String
          let authToken = args?["authToken"] as? String
          
          loginLogoutHelper?.selectLoginMethod(authToken: authToken, authKey: authKey, UID: uid)
          result("success")
```

The `selectLoginMethod` will decide whether the user wants to login using `authKey` or `authToken`. (The `authKey` and `authToken` can be generated by creating an app on [`Comet Chat Pro website`](https://app.cometchat.com/app/20568047e8d91d52/quick-start), use v3 version for creating the app as we are using that as reference in this project).

Here, I will use the login method with `authToken` to demonstrate the flow for transferring data through our event channel. For full code, you can refer to [`this repo`](https://github.com/vishalsh2299/flutter_comet_chat_sdk).

We are calling the `Comet Chat` login method and it gives callbacks based on success or error. Under the `onSuccess callback`, we are getting the user. Now, we will convert this user in key-value type (Dictionary in case of Swift), then pass the Dictionary in string form. In the stream, we are passing the event with the `loginSuccess` or `loginFailure` key. On the Dart side we will use this key to get the user or error from the event stream.

```
private func loginWithAuthToken(tAuthToken: String) {
        if CometChat.getLoggedInUser() == nil {
          
          CometChat.login(authToken: tAuthToken , onSuccess: { (user) in
              do {
                  try self.eventChannelHandler?.success(event: ["loginSuccess": Helper.userToJson(user: user).jsonStringRepresentation])
              } catch {
                  self.eventChannelHandler?.error(code: "loginFailure", message: error.localizedDescription)
              }

          }) { (error) in
            print("Login failed with error: " + error.errorDescription);
              do {
                  try self.eventChannelHandler?.success(event: ["loginFailure" : error.errorDescription])
              } catch {
                  self.eventChannelHandler?.error(code: "loginFailure", message: error.localizedDescription)
              }
          }
          
        }
    }
    
```

Extension of `jsonStringRepresentation` to convert Dictionary to String.
```
extension Dictionary {
    var jsonStringRepresentation: String? {
        guard let theJSONData = try? JSONSerialization.data(withJSONObject: self,
                                                            options: [.prettyPrinted]) else {
            return nil
        }

        return String(data: theJSONData, encoding: .ascii)
    }
}
```

Defining `EventChannel` and `StreamSubscription` on the Dart side.
```
static const EventChannel loginEventTrigger =
      EventChannel('plugins.flutter.io/login_event_trigger');

late StreamSubscription _loginEventsubscription;
```

Subscribing to the event stream to receive data.
```
Stream<dynamic> loginEventTrigger() {
    return FlutterCometChatDart.loginEventTrigger.receiveBroadcastStream();
  }
```

Since this is a trigger stream so we will cancel the stream subscription once the event data is received. 
Method to cancel the subscription -
```
cancelSubscription(StreamSubscription streamSubscription) {
    return () {
      streamSubscription.cancel();
    };
  }
```

Now, in the login method, we will first do the method call then listen to the stream, and once we have used the `listen()` method to fetch the data we will cancel the subscription.

```
login(
      {String? authKey,
      String? uid,
      String? authToken,
      required CallbackListener callbackListener}) {
    assert((uid != '' && authKey != '') || (authToken != ''));

    _auth.login(authKey: authKey, uid: uid, authToken: authToken);

    _loginEventsubscription = _auth.loginEventTrigger().listen((event) {
      if (event.containsKey(CometConstantsKeys.LOGIN_SUCCESS)) {
        var data = jsonDecode(event[CometConstantsKeys.LOGIN_SUCCESS]);
        callbackListener.onSuccess(User.fromJson(data));
      }
      if (event.containsKey(CometConstantsKeys.LOGIN_FAILURE)) {
        callbackListener.onError(event);
      }
    });

    cancelSubscription(_loginEventsubscription);
  }
```
Here, we are using the same keys `login_success` and `login_failure` to get the data from the event stream. Then we have decoded the `JSON String` and serialized it with the `User` class. To get the object data on the Dart side we have added a `fromJson` method under the `User` class that will map all the keys in the JSON to the values of the `User` class.

```
 User.fromJson(Map<dynamic, dynamic> json) {
    avatar = json['avatar'];
    blockedByMe = json['blockedByMe'];
    credits = json['credits'];
    hasBlockedMe = json['hasBlockedMe'];
    lastActiveAt = json['lastActiveAt'];
    link = json['link'];
    metadata = json['metadata'];
    name = json['name'];
    role = json['role'];
    status = getUserStatus(json['status']);
    statusMessage = json['statusMessage'];
    uid = json['uid'];
  }
```
Now the method is ready we can use it in our example app.
```
 _cometChat.login(
                  uid: "superhero2",
                  authToken: Constant.authToken,
                  callbackListener: CallbackListener(
                    onSuccess: (User user) {
                      print("User $user");
                    },
                    onError: (e) {
                      print("Error $e");
                    },
                  ),
                );
```


Till now, we have defined and used `MethodChannel`, `EventChannel`. Let's look at another example for `EventChannel` by defining `LoginListener`.

According to the `Comet Chat` docs, `LoginListener` class needs to implement `CometChatLoginDelegate` and add `CometChat.logindelegate = self` in the `init` state of this class.

The `LoginListener` will then override 4 methods - `onLoginSuccess`, `onLoginFailed`, `onLogoutSuccess`, and `onLogoutFailed`. Similar to the `login` method here in the listener we will convert the user object into `Dictionary` then into `String` and pass it to the event channel success method or in case of error pass it to the error method with the right key.

```
public class LoginListener: CometChatLoginDelegate {
    private var eventChannelHandler: EventChannelHandler?
    
    public init(handler: EventChannelHandler?) {
        self.eventChannelHandler = handler
        CometChat.logindelegate = self
    }
    
    public func onLoginSuccess(user: User) {
        do {
            try eventChannelHandler?.success(
                event: ["loginSuccess":  Helper.userToJson(user: user).jsonStringRepresentation]
            )
        } catch {
            eventChannelHandler?.error(
                code: "loginFailure",
                message: error.localizedDescription
            )
        }
    }
. . .
. . .

```

Let's use this listener in our method call. First, declare the `EventChannelHandler` and `LoginListener` object.
```
private var loginEventListener: EventChannelHandler?
private var loginListener: LoginListener?
```

Then under `setupChannel` method declare the variable with the name used for this `EventChannel` and binary messenger.
```
 loginEventListener = EventChannelHandler(
          id: "plugins.flutter.io/login_event_listener",
          messenger: registrar.messenger()
      )
```

Under the `handle` method, we will define the `add_login_listener` method call and instantiate the `LoginListener` class.
```
 case "add_login_listeners":
          loginListener = LoginListener(handler: loginEventListener)
          result("success")
```

Coming on the Dart side, we will declare another event channel and subscribe to the stream of the event.
```
static const EventChannel loginEventListener =
      EventChannel("plugins.flutter.io/login_event_listener");
```

Subscribing to the event channel stream of login listener.
```
  Stream<dynamic> loginEventListener() {
    return FlutterCometChatDart.loginEventListener.receiveBroadcastStream();
  }
```

Now under the `addLoginListener` method, we will listen to the event stream and pass the data to the `callback` method. Here, we have used `LoginListener` as a callback which has 4 methods same as the `LoginListener` on the `Swift` side.

```
addLoginListener(String uniqueID, LoginListener loginListener) async {
    assert(uniqueID != '');
        try {
      await FlutterCometChatDart.channel
          .invokeMethod('add_login_listeners', {"uniqueId": uniqueID});

      _auth.loginEventListener().listen((event) {
        if (event.containsKey(CometConstantsKeys.LOGIN_FAILURE)) {
          loginListener.loginFailure(event);
        }
        if (event.containsKey(CometConstantsKeys.LOGIN_SUCCESS)) {
          var data = jsonDecode(event[CometConstantsKeys.LOGIN_SUCCESS]);
          loginListener.loginSuccess(User.fromJson(data));
        }
        if (event.containsKey(CometConstantsKeys.LOGOUT_FAILURE)) {
          loginListener.logoutFailure(event[CometConstantsKeys.LOGOUT_FAILURE]);
        }
        if (event.containsKey(CometConstantsKeys.LOGOUT_SUCCESS)) {
          loginListener.logoutSuccess(event[CometConstantsKeys.LOGOUT_SUCCESS]);
        }
      });
    } on PlatformException catch (e) {
      throw FlutterCometChatDart.convertException(e);
    }
  }
```

### What's next?
In this tutorial, we learned how to set up `Platform` channels for IOS and implemented the 3rd party SDK. Then used the Dart plugin in our example app to access the native methods. In the next tutorial, we will implement the Platform channels and integrate the `Comet Chat` SDK for Android.

### References
- [`Github code link`](https://github.com/vishalsh2299/flutter_comet_chat_sdk)
- [`Platform channel Flutter docs`](https://docs.flutter.dev/development/platform-integration/platform-channels#architecture)
- [`Docs for creating a plugin in Flutter`](https://docs.flutter.dev/development/packages-and-plugins/developing-packages)
- [`Docs for classes and interface used on native side to define platform channel`](https://api.flutter.dev/objcdoc/Protocols/FlutterPlugin.html)
- [`Comet Chat docs for classes used for demonstration in this article`](https://docs.cometchat.io/ios/v2.0/swiftdocs/Classes/AppSettings/AppSettingsBuilder.html)
- [`Comet Chat swift docs`](https://www.cometchat.com/docs/swift-chat-ui-kit/overview)

