Fix a crash when setting clipboardData to null on iOS (#32413)

diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm
index 257cb44..23264b5 100644
--- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm
+++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm
@@ -267,8 +267,9 @@
 
 - (void)setClipboardData:(NSDictionary*)data {
   UIPasteboard* pasteboard = [UIPasteboard generalPasteboard];
-  if (data[@"text"]) {
-    pasteboard.string = data[@"text"];
+  id copyText = data[@"text"];
+  if ([copyText isKindOfClass:[NSString class]]) {
+    pasteboard.string = copyText;
   } else {
     pasteboard.string = @"null";
   }
diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm
index 23c7d2f..f5fbdf3 100644
--- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm
+++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm
@@ -16,37 +16,71 @@
 
 @implementation FlutterPlatformPluginTest
 
-- (void)testHasStrings {
+- (void)testClipboardHasCorrectStrings {
+  [UIPasteboard generalPasteboard].string = nil;
   FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
   std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory =
       std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(engine);
   FlutterPlatformPlugin* plugin =
       [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()];
 
-  // Set some string to the pasteboard.
-  __block bool calledSet = false;
+  XCTestExpectation* setStringExpectation = [self expectationWithDescription:@"setString"];
   FlutterResult resultSet = ^(id result) {
-    calledSet = true;
+    [setStringExpectation fulfill];
   };
   FlutterMethodCall* methodCallSet =
-      [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setClipboardData"
+      [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setData"
                                         arguments:@{@"text" : @"some string"}];
   [plugin handleMethodCall:methodCallSet result:resultSet];
-  XCTAssertEqual(calledSet, true);
+  [self waitForExpectationsWithTimeout:1 handler:nil];
 
-  // Call hasStrings and expect it to be true.
-  __block bool called = false;
-  __block bool value;
+  XCTestExpectation* hasStringsExpectation = [self expectationWithDescription:@"hasStrings"];
   FlutterResult result = ^(id result) {
-    called = true;
-    value = result[@"value"];
+    XCTAssertTrue([result[@"value"] boolValue]);
+    [hasStringsExpectation fulfill];
   };
   FlutterMethodCall* methodCall =
       [FlutterMethodCall methodCallWithMethodName:@"Clipboard.hasStrings" arguments:nil];
   [plugin handleMethodCall:methodCall result:result];
+  [self waitForExpectationsWithTimeout:1 handler:nil];
 
-  XCTAssertEqual(called, true);
-  XCTAssertEqual(value, true);
+  XCTestExpectation* getDataExpectation = [self expectationWithDescription:@"getData"];
+  FlutterResult getDataResult = ^(id result) {
+    XCTAssertEqualObjects(result[@"text"], @"some string");
+    [getDataExpectation fulfill];
+  };
+  FlutterMethodCall* methodCallGetData =
+      [FlutterMethodCall methodCallWithMethodName:@"Clipboard.getData" arguments:@"text/plain"];
+  [plugin handleMethodCall:methodCallGetData result:getDataResult];
+  [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testClipboardSetDataToNullDoNotCrash {
+  [UIPasteboard generalPasteboard].string = nil;
+  FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil];
+  std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory =
+      std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(engine);
+  FlutterPlatformPlugin* plugin =
+      [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()];
+
+  XCTestExpectation* setStringExpectation = [self expectationWithDescription:@"setData"];
+  FlutterResult resultSet = ^(id result) {
+    [setStringExpectation fulfill];
+  };
+  FlutterMethodCall* methodCallSet =
+      [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setData"
+                                        arguments:@{@"text" : [NSNull null]}];
+  [plugin handleMethodCall:methodCallSet result:resultSet];
+
+  XCTestExpectation* getDataExpectation = [self expectationWithDescription:@"getData"];
+  FlutterResult result = ^(id result) {
+    XCTAssertEqualObjects(result[@"text"], @"null");
+    [getDataExpectation fulfill];
+  };
+  FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.getData"
+                                                                    arguments:@"text/plain"];
+  [plugin handleMethodCall:methodCall result:result];
+  [self waitForExpectationsWithTimeout:1 handler:nil];
 }
 
 - (void)testPopSystemNavigator {
@@ -66,14 +100,14 @@
   id navigationControllerMock = OCMPartialMock(navigationController);
   OCMStub([navigationControllerMock popViewControllerAnimated:YES]);
   // Set some string to the pasteboard.
-  __block bool calledSet = false;
+  XCTestExpectation* navigationPopCalled = [self expectationWithDescription:@"SystemNavigator.pop"];
   FlutterResult resultSet = ^(id result) {
-    calledSet = true;
+    [navigationPopCalled fulfill];
   };
   FlutterMethodCall* methodCallSet =
       [FlutterMethodCall methodCallWithMethodName:@"SystemNavigator.pop" arguments:@(YES)];
   [plugin handleMethodCall:methodCallSet result:resultSet];
-  XCTAssertEqual(calledSet, true);
+  [self waitForExpectationsWithTimeout:1 handler:nil];
   OCMVerify([navigationControllerMock popViewControllerAnimated:YES]);
 }