diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index 0c71e3391dbb..ca17cc212d31 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -351,4 +351,41 @@ - (void)testEvaluateJavaScript { XCTAssertEqualObjects(returnValue, @"result"); XCTAssertNil(returnError); } + +- (void)testEvaluateJavaScriptReturnsNSErrorData { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + OCMStub([mockWebView + evaluateJavaScript:@"runJavaScript" + completionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{ + NSLocalizedDescriptionKey : + @"description" + }], + nil])]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostAPI = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSString __block *returnValue; + FlutterError __block *returnError; + [hostAPI evaluateJavaScriptForWebViewWithIdentifier:@0 + javaScriptString:@"runJavaScript" + completion:^(id result, FlutterError *error) { + returnValue = result; + returnError = error; + }]; + + XCTAssertNil(returnValue); + FWFNSErrorData *errorData = returnError.details; + XCTAssertTrue([errorData isKindOfClass:[FWFNSErrorData class]]); + XCTAssertEqualObjects(errorData.code, @0); + XCTAssertEqualObjects(errorData.domain, @"errorDomain"); + XCTAssertEqualObjects(errorData.localizedDescription, @"description"); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m index 66149dd9ed37..2962aadd1647 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -141,7 +141,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie if (!result || [result isKindOfClass:[NSString class]] || [result isKindOfClass:[NSNumber class]]) { returnValue = result; - } else { + } else if (![result isKindOfClass:[NSNull class]]) { NSString *className = NSStringFromClass([result class]); NSLog(@"Return type of evaluateJavaScript is not directly supported: %@. Returned " @"description of value.", @@ -151,7 +151,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie } else { flutterError = [FlutterError errorWithCode:@"FWFEvaluateJavaScriptError" message:@"Failed evaluating JavaScript." - details:error]; + details:FWFNSErrorDataFromNSError(error)]; } completion(returnValue, flutterError); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index c27fc3e73aee..4eb173238e71 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -908,11 +908,25 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { Future evaluateJavaScriptForInstances( WKWebView instance, String javaScriptString, - ) { - return evaluateJavaScript( - instanceManager.getIdentifier(instance)!, - javaScriptString, - ); + ) async { + try { + final Object? result = await evaluateJavaScript( + instanceManager.getIdentifier(instance)!, + javaScriptString, + ); + return result; + } on PlatformException catch (exception) { + if (exception.details is! NSErrorData) { + rethrow; + } + + throw PlatformException( + code: exception.code, + message: exception.message, + stacktrace: exception.stacktrace, + details: (exception.details as NSErrorData).toNSError(), + ); + } } /// Calls [setNavigationDelegate] with the ids of the provided object instances. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 05a77f56f851..b193fa8c1fac 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -573,6 +573,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return '""'; } return '(null)'; + } else if (value is bool) { + return value ? '1' : '0'; + } else if (value is double && value.truncate() == value) { + return value.truncate().toString(); } else if (value is List) { final List stringValues = []; for (final Object? listValue in value) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index d71a6c5e0838..1d6c1f0e67a1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -815,6 +816,30 @@ void main() { .thenAnswer((_) => Future.value('stopstop')); expect(webView.evaluateJavaScript('gogo'), completion('stopstop')); }); + + test('evaluateJavaScript returns NSError', () { + when(mockPlatformHostApi.evaluateJavaScript(webViewInstanceId, 'gogo')) + .thenThrow( + PlatformException( + code: '', + details: NSErrorData( + code: 0, + domain: 'domain', + localizedDescription: 'desc', + ), + ), + ); + expect( + webView.evaluateJavaScript('gogo'), + throwsA( + isA().having( + (PlatformException exception) => exception.details, + 'details', + isA(), + ), + ), + ); + }); }); group('WKUIDelegate', () { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 24155d2a05e1..905fdb542063 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -634,6 +634,42 @@ void main() { ); }); + testWidgets('evaluateJavascript with bool return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(true), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies bool + // is represented the way it is in Objective-C. + // `NSNumber.description` converts bool values to a 1 or 0. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('1'), + ); + }); + + testWidgets('evaluateJavascript with double return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(1.0), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies + // double is represented the way it is in Objective-C. If a double + // doesn't contain any decimal values, it gets truncated to an int. + // This should be happenning because NSNumber convertes float values + // with no decimals to an int when using `NSNumber.description`. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('1'), + ); + }); + testWidgets('evaluateJavascript with list return value', (WidgetTester tester) async { await buildWidget(tester);