7

I am trying to use PlatformViews in Flutter to show Swift code natively in my Flutter app, however my app is crashing with my current code.

This is my AppDelegate currently where I am invoking my method channel:

import Foundation

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, TJPlacementDelegate {
    var p = TJPlacement()
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let channelName = "NativeView"
        let rootViewController : FlutterViewController = window?.rootViewController as! FlutterViewController
        let methodChannel = FlutterMethodChannel(name: channelName, binaryMessenger: rootViewController as! FlutterBinaryMessenger)
        methodChannel.setMethodCallHandler {(call: FlutterMethodCall, result: FlutterResult) -> Void in
            if (call.method == "setDebugEnabled") {
                let isDebug = call.arguments as! Bool
                Tapjoy.setDebugEnabled(isDebug)
            } 
        }
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

This is my Dart implementation for the native code:

 import 'package:flutter/material.dart';
 import 'tapjoy.dart';

    class Home extends StatefulWidget {
      @override
      _HomeState createState() => _HomeState();
    }

    class _HomeState extends State<Home> {
      @override
      void initState() {
        callTapjoy();
        super.initState();
      }

      Widget build(context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: UiKitView(viewType: 'NativeView'),
          ),
        );
      }

      void callTapjoy() {
        Tapjoy.setDebugEnabled(true);
      }
    }

//My code in tapjoy.dart
    class Tapjoy {
      static const MethodChannel _channel = const MethodChannel('NativeView');
          static void setDebugEnabled(bool isDebug) {
        _channel.invokeMethod('setDebugEnabled', {"isDebug": isDebug});
      } 
    } 

My app crashes and shows me an error in the debug console:

Could not cast value of type '__NSDictionaryM' (0x7fff87a61d78) to 'NSNumber' (0x7fff87b1eb08).
2020-04-29 16:56:42.985269+0530 Runner[18484:224162] Could not cast value of type '__NSDictionaryM' (0x7fff87a61d78) to 'NSNumber' (0x7fff87b1eb08).

enter image description here

Arnav
  • 1,404
  • 2
  • 19
  • 38
  • 1
    Hey arnav. Im not ios guy but I know method channel . I can't see method channel implementation in your code. Can you show us. Beside please follow this blog to implement method channel if you haven't implemented yet. https://stablekernel.com/article/flutter-platform-channels-quick-start/ – vivek yadav Apr 29 '20 at 10:51
  • Hey vivek, I've updated my question. Thanks! – Arnav Apr 29 '20 at 11:36

1 Answers1

14

You are passing a Map from Dart to native: {"isDebug": isDebug}, so you need extract the parameter from the map/dictionary at the Swift end.

  if let args = call.arguments as? Dictionary<String, Any>,
    let isDebug = args["isDebug"] as? Bool {
      // please check the "as" above  - wasn't able to test
      // handle the method

    result(nil)
  } else {
    result(FlutterError.init(code: "errorSetDebug", message: "data or format error", details: nil))
  }

Alternatively, just pass the boolean from the Dart end, without first putting it into a map.

_channel.invokeMethod('setDebugEnabled', isDebug);
Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • Thanks a lot! This worked like a charm, I had the same problem but this time to pass parameters from Swift code to Flutter. Could you please answer this too? https://stackoverflow.com/questions/61507418/how-to-put-arguments-from-swift-native-code-to-flutter – Arnav Apr 29 '20 at 17:06
  • Hey richard, can you help with this? https://stackoverflow.com/questions/62175368/which-view-controller-should-i-pass-in-my-swift-native-code-for-a-flutter-plugin – Arnav Jun 03 '20 at 16:11
  • This was really helpful! You can also alternatively use Swift's `guard` functionality to exit the current scope too, such as: `guard let isDebug = call.arguments as? Bool else { }` – Aaron Krauss Feb 08 '23 at 18:45