Skip to content

Commit 1d895c1

Browse files
bparrishMinesmvanbeusekom
authored andcommitted
[webview_flutter_wkwebview] Implement Objc side of Flutter Apis (flutter#5934)
1 parent 4a49156 commit 1d895c1

19 files changed

+877
-38
lines changed

packages/webview_flutter/webview_flutter_wkwebview/example/ios/Flutter/AppFrameworkInfo.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
<key>CFBundleVersion</key>
2222
<string>1.0</string>
2323
<key>MinimumOSVersion</key>
24-
<string>9.0</string>
24+
<string>11.0</string>
2525
</dict>
2626
</plist>

packages/webview_flutter/webview_flutter_wkwebview/example/ios/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Uncomment this line to define a global platform for your project
2-
# platform :ios, '9.0'
2+
# platform :ios, '11.0'
33

44
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@
614614
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
615615
GCC_WARN_UNUSED_FUNCTION = YES;
616616
GCC_WARN_UNUSED_VARIABLE = YES;
617-
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
617+
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
618618
MTL_ENABLE_DEBUG_INFO = YES;
619619
ONLY_ACTIVE_ARCH = YES;
620620
SDKROOT = iphoneos;
@@ -664,7 +664,7 @@
664664
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
665665
GCC_WARN_UNUSED_FUNCTION = YES;
666666
GCC_WARN_UNUSED_VARIABLE = YES;
667-
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
667+
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
668668
MTL_ENABLE_DEBUG_INFO = NO;
669669
SDKROOT = iphoneos;
670670
TARGETED_DEVICE_FAMILY = "1,2";

packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,7 @@
4141
</array>
4242
<key>UIViewControllerBasedStatusBarAppearance</key>
4343
<false/>
44+
<key>CADisableMinimumFrameDurationOnPhone</key>
45+
<true/>
4446
</dict>
4547
</plist>

packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
@import XCTest;
77
@import webview_flutter_wkwebview;
88

9+
#import <OCMock/OCMock.h>
10+
911
@interface FWFDataConvertersTests : XCTestCase
1012
@end
1113

@@ -43,4 +45,63 @@ - (void)testFWFWKUserScriptFromScriptData {
4345
XCTAssertEqual(userScript.injectionTime, WKUserScriptInjectionTimeAtDocumentStart);
4446
XCTAssertEqual(userScript.isForMainFrameOnly, NO);
4547
}
48+
49+
- (void)testFWFWKNavigationActionDataFromNavigationAction {
50+
WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]);
51+
52+
NSURLRequest *request =
53+
[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev/"]];
54+
OCMStub([mockNavigationAction request]).andReturn(request);
55+
56+
WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]);
57+
OCMStub([mockFrameInfo isMainFrame]).andReturn(YES);
58+
OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo);
59+
60+
FWFWKNavigationActionData *data =
61+
FWFWKNavigationActionDataFromNavigationAction(mockNavigationAction);
62+
XCTAssertNotNil(data);
63+
}
64+
65+
- (void)testFWFNSUrlRequestDataFromNSURLRequest {
66+
NSMutableURLRequest *request =
67+
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev/"]];
68+
request.HTTPMethod = @"POST";
69+
request.HTTPBody = [@"aString" dataUsingEncoding:NSUTF8StringEncoding];
70+
request.allHTTPHeaderFields = @{@"a" : @"field"};
71+
72+
FWFNSUrlRequestData *data = FWFNSUrlRequestDataFromNSURLRequest(request);
73+
XCTAssertEqualObjects(data.url, @"https://www.flutter.dev/");
74+
XCTAssertEqualObjects(data.httpMethod, @"POST");
75+
XCTAssertEqualObjects(data.httpBody.data, [@"aString" dataUsingEncoding:NSUTF8StringEncoding]);
76+
XCTAssertEqualObjects(data.allHttpHeaderFields, @{@"a" : @"field"});
77+
}
78+
79+
- (void)testFWFWKFrameInfoDataFromWKFrameInfo {
80+
WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]);
81+
OCMStub([mockFrameInfo isMainFrame]).andReturn(YES);
82+
83+
FWFWKFrameInfoData *targetFrameData = FWFWKFrameInfoDataFromWKFrameInfo(mockFrameInfo);
84+
XCTAssertEqualObjects(targetFrameData.isMainFrame, @YES);
85+
}
86+
87+
- (void)testFWFNSErrorDataFromNSError {
88+
NSError *error = [NSError errorWithDomain:@"domain"
89+
code:23
90+
userInfo:@{NSLocalizedDescriptionKey : @"description"}];
91+
92+
FWFNSErrorData *data = FWFNSErrorDataFromNSError(error);
93+
XCTAssertEqualObjects(data.code, @23);
94+
XCTAssertEqualObjects(data.domain, @"domain");
95+
XCTAssertEqualObjects(data.localizedDescription, @"description");
96+
}
97+
98+
- (void)testFWFWKScriptMessageDataFromWKScriptMessage {
99+
WKScriptMessage *mockScriptMessage = OCMClassMock([WKScriptMessage class]);
100+
OCMStub([mockScriptMessage name]).andReturn(@"name");
101+
OCMStub([mockScriptMessage body]).andReturn(@"message");
102+
103+
FWFWKScriptMessageData *data = FWFWKScriptMessageDataFromWKScriptMessage(mockScriptMessage);
104+
XCTAssertEqualObjects(data.name, @"name");
105+
XCTAssertEqualObjects(data.body, @"message");
106+
}
46107
@end

packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m

Lines changed: 172 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,38 @@ @interface FWFNavigationDelegateHostApiTests : XCTestCase
1212
@end
1313

1414
@implementation FWFNavigationDelegateHostApiTests
15+
/**
16+
* Creates a partially mocked FWFNavigationDelegate and adds it to instanceManager.
17+
*
18+
* @param instanceManager Instance manager to add the delegate to.
19+
* @param identifier Identifier for the delegate added to the instanceManager.
20+
*
21+
* @return A mock FWFNavigationDelegate.
22+
*/
23+
- (id)mockNavigationDelegateWithManager:(FWFInstanceManager *)instanceManager
24+
identifier:(long)identifier {
25+
FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc]
26+
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
27+
instanceManager:instanceManager];
28+
29+
[instanceManager addDartCreatedInstance:navigationDelegate withIdentifier:0];
30+
return OCMPartialMock(navigationDelegate);
31+
}
32+
33+
/**
34+
* Creates a mock FWFNavigationDelegateFlutterApiImpl with instanceManager.
35+
*
36+
* @param instanceManager Instance manager passed to the Flutter API.
37+
*
38+
* @return A mock FWFNavigationDelegateFlutterApiImpl.
39+
*/
40+
- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager {
41+
FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc]
42+
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
43+
instanceManager:instanceManager];
44+
return OCMPartialMock(flutterAPI);
45+
}
46+
1547
- (void)testCreateWithIdentifier {
1648
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
1749
FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc]
@@ -29,31 +61,156 @@ - (void)testCreateWithIdentifier {
2961

3062
- (void)testDidFinishNavigation {
3163
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
32-
FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc]
33-
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
34-
instanceManager:instanceManager];
3564

36-
FlutterError *error;
37-
[hostAPI createWithIdentifier:@0 error:&error];
38-
FWFNavigationDelegate *navigationDelegate =
39-
(FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0];
40-
id mockDelegate = OCMPartialMock(navigationDelegate);
65+
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
66+
identifier:0];
67+
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
68+
[self mockFlutterApiWithManager:instanceManager];
4169

42-
FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc]
43-
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
44-
instanceManager:instanceManager];
45-
id mockFlutterApi = OCMPartialMock(flutterAPI);
46-
47-
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterApi);
70+
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
4871

4972
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
5073
OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]);
5174
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
5275

5376
[mockDelegate webView:mockWebView didFinishNavigation:OCMClassMock([WKNavigation class])];
54-
OCMVerify([mockFlutterApi didFinishNavigationForDelegateWithIdentifier:@0
77+
OCMVerify([mockFlutterAPI didFinishNavigationForDelegateWithIdentifier:@0
5578
webViewIdentifier:@1
5679
URL:@"https://flutter.dev/"
5780
completion:OCMOCK_ANY]);
5881
}
82+
83+
- (void)testDidStartProvisionalNavigation {
84+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
85+
86+
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
87+
identifier:0];
88+
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
89+
[self mockFlutterApiWithManager:instanceManager];
90+
91+
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
92+
93+
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
94+
OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]);
95+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
96+
97+
[mockDelegate webView:mockWebView
98+
didStartProvisionalNavigation:OCMClassMock([WKNavigation class])];
99+
OCMVerify([mockFlutterAPI
100+
didStartProvisionalNavigationForDelegateWithIdentifier:@0
101+
webViewIdentifier:@1
102+
URL:@"https://flutter.dev/"
103+
completion:OCMOCK_ANY]);
104+
}
105+
106+
- (void)testDecidePolicyForNavigationAction {
107+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
108+
109+
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
110+
identifier:0];
111+
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
112+
[self mockFlutterApiWithManager:instanceManager];
113+
114+
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
115+
116+
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
117+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
118+
119+
WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]);
120+
OCMStub([mockNavigationAction request])
121+
.andReturn([NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev"]]);
122+
123+
WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]);
124+
OCMStub([mockFrameInfo isMainFrame]).andReturn(YES);
125+
OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo);
126+
127+
OCMStub([mockFlutterAPI
128+
decidePolicyForNavigationActionForDelegateWithIdentifier:@0
129+
webViewIdentifier:@1
130+
navigationAction:
131+
[OCMArg isKindOfClass:[FWFWKNavigationActionData
132+
class]]
133+
completion:
134+
([OCMArg
135+
invokeBlockWithArgs:
136+
[FWFWKNavigationActionPolicyEnumData
137+
makeWithValue:
138+
FWFWKNavigationActionPolicyEnumCancel],
139+
[NSNull null], nil])]);
140+
141+
WKNavigationActionPolicy __block callbackPolicy = -1;
142+
[mockDelegate webView:mockWebView
143+
decidePolicyForNavigationAction:mockNavigationAction
144+
decisionHandler:^(WKNavigationActionPolicy policy) {
145+
callbackPolicy = policy;
146+
}];
147+
XCTAssertEqual(callbackPolicy, WKNavigationActionPolicyCancel);
148+
}
149+
150+
- (void)testDidFailNavigation {
151+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
152+
153+
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
154+
identifier:0];
155+
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
156+
[self mockFlutterApiWithManager:instanceManager];
157+
158+
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
159+
160+
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
161+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
162+
163+
[mockDelegate webView:mockWebView
164+
didFailNavigation:OCMClassMock([WKNavigation class])
165+
withError:[NSError errorWithDomain:@"domain" code:0 userInfo:nil]];
166+
OCMVerify([mockFlutterAPI
167+
didFailNavigationForDelegateWithIdentifier:@0
168+
webViewIdentifier:@1
169+
error:[OCMArg isKindOfClass:[FWFNSErrorData class]]
170+
completion:OCMOCK_ANY]);
171+
}
172+
173+
- (void)testDidFailProvisionalNavigation {
174+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
175+
176+
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
177+
identifier:0];
178+
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
179+
[self mockFlutterApiWithManager:instanceManager];
180+
181+
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
182+
183+
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
184+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
185+
186+
[mockDelegate webView:mockWebView
187+
didFailProvisionalNavigation:OCMClassMock([WKNavigation class])
188+
withError:[NSError errorWithDomain:@"domain" code:0 userInfo:nil]];
189+
OCMVerify([mockFlutterAPI
190+
didFailProvisionalNavigationForDelegateWithIdentifier:@0
191+
webViewIdentifier:@1
192+
error:[OCMArg isKindOfClass:[FWFNSErrorData
193+
class]]
194+
completion:OCMOCK_ANY]);
195+
}
196+
197+
- (void)testWebViewWebContentProcessDidTerminate {
198+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
199+
200+
FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
201+
identifier:0];
202+
FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
203+
[self mockFlutterApiWithManager:instanceManager];
204+
205+
OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
206+
207+
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
208+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
209+
210+
[mockDelegate webViewWebContentProcessDidTerminate:mockWebView];
211+
OCMVerify([mockFlutterAPI
212+
webViewWebContentProcessDidTerminateForDelegateWithIdentifier:@0
213+
webViewIdentifier:@1
214+
completion:OCMOCK_ANY]);
215+
}
59216
@end

packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,37 @@ @interface FWFObjectHostApiTests : XCTestCase
1212
@end
1313

1414
@implementation FWFObjectHostApiTests
15+
/**
16+
* Creates a partially mocked FWFObject and adds it to instanceManager.
17+
*
18+
* @param instanceManager Instance manager to add the delegate to.
19+
* @param identifier Identifier for the delegate added to the instanceManager.
20+
*
21+
* @return A mock FWFObject.
22+
*/
23+
- (id)mockObjectWithManager:(FWFInstanceManager *)instanceManager identifier:(long)identifier {
24+
FWFObject *object =
25+
[[FWFObject alloc] initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
26+
instanceManager:instanceManager];
27+
28+
[instanceManager addDartCreatedInstance:object withIdentifier:0];
29+
return OCMPartialMock(object);
30+
}
31+
32+
/**
33+
* Creates a mock FWFObjectFlutterApiImpl with instanceManager.
34+
*
35+
* @param instanceManager Instance manager passed to the Flutter API.
36+
*
37+
* @return A mock FWFObjectFlutterApiImpl.
38+
*/
39+
- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager {
40+
FWFObjectFlutterApiImpl *flutterAPI = [[FWFObjectFlutterApiImpl alloc]
41+
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
42+
instanceManager:instanceManager];
43+
return OCMPartialMock(flutterAPI);
44+
}
45+
1546
- (void)testAddObserver {
1647
NSObject *mockObject = OCMClassMock([NSObject class]);
1748

@@ -82,4 +113,34 @@ - (void)testDispose {
82113
XCTAssertFalse([instanceManager containsInstance:object]);
83114
XCTAssertNil(error);
84115
}
116+
117+
- (void)testObserveValueForKeyPath {
118+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
119+
120+
FWFObject *mockObject = [self mockObjectWithManager:instanceManager identifier:0];
121+
FWFObjectFlutterApiImpl *mockFlutterAPI = [self mockFlutterApiWithManager:instanceManager];
122+
123+
OCMStub([mockObject objectApi]).andReturn(mockFlutterAPI);
124+
125+
NSObject *object = [[NSObject alloc] init];
126+
[instanceManager addDartCreatedInstance:object withIdentifier:1];
127+
128+
[mockObject observeValueForKeyPath:@"keyPath"
129+
ofObject:object
130+
change:@{NSKeyValueChangeOldKey : @"key"}
131+
context:nil];
132+
OCMVerify([mockFlutterAPI
133+
observeValueForObjectWithIdentifier:@0
134+
keyPath:@"keyPath"
135+
objectIdentifier:@1
136+
changeKeys:[OCMArg checkWithBlock:^BOOL(
137+
NSArray<FWFNSKeyValueChangeKeyEnumData *>
138+
*value) {
139+
return value[0].value == FWFNSKeyValueChangeKeyEnumOldValue;
140+
}]
141+
changeValues:[OCMArg checkWithBlock:^BOOL(id value) {
142+
return [@"key" isEqual:value[0]];
143+
}]
144+
completion:OCMOCK_ANY]);
145+
}
85146
@end

0 commit comments

Comments
 (0)