Skip to content

Commit e8ea48a

Browse files
bparrishMinesmvanbeusekom
authored andcommitted
[webview_flutter_wkwebview] Implement one callback method for review of the design (flutter#5700)
1 parent ceb58b3 commit e8ea48a

34 files changed

+1343
-687
lines changed

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

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,40 @@
33
// found in the LICENSE file.
44

55
#import <XCTest/XCTest.h>
6+
67
@import webview_flutter_wkwebview;
8+
@import webview_flutter_wkwebview.Test;
79

810
@interface FWFInstanceManagerTests : XCTestCase
911
@end
1012

1113
@implementation FWFInstanceManagerTests
12-
- (void)testAddInstanceCreatedFromDart {
14+
- (void)testAddDartCreatedInstance {
1315
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
1416
NSObject *object = [[NSObject alloc] init];
1517

16-
[instanceManager addDartCreatedInstance:object withIdentifier:5];
17-
XCTAssertEqualObjects([instanceManager instanceForIdentifier:5], object);
18-
XCTAssertEqual([instanceManager identifierForInstance:object], 5);
18+
[instanceManager addDartCreatedInstance:object withIdentifier:0];
19+
XCTAssertEqualObjects([instanceManager instanceForIdentifier:0], object);
20+
XCTAssertEqual([instanceManager identifierWithStrongReferenceForInstance:object], 0);
1921
}
2022

21-
- (void)testRemoveInstance {
23+
- (void)testAddHostCreatedInstance {
2224
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
2325
NSObject *object = [[NSObject alloc] init];
24-
[instanceManager addDartCreatedInstance:object withIdentifier:5];
26+
[instanceManager addHostCreatedInstance:object];
2527

26-
[instanceManager removeInstance:object];
27-
XCTAssertNil([instanceManager instanceForIdentifier:5]);
28-
XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound);
28+
long identifier = [instanceManager identifierWithStrongReferenceForInstance:object];
29+
XCTAssertNotEqual(identifier, NSNotFound);
30+
XCTAssertEqualObjects([instanceManager instanceForIdentifier:identifier], object);
2931
}
3032

3133
- (void)testRemoveInstanceWithIdentifier {
3234
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
3335
NSObject *object = [[NSObject alloc] init];
34-
[instanceManager addDartCreatedInstance:object withIdentifier:5];
3536

36-
[instanceManager removeInstanceWithIdentifier:5];
37-
XCTAssertNil([instanceManager instanceForIdentifier:5]);
38-
XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound);
37+
[instanceManager addDartCreatedInstance:object withIdentifier:0];
38+
39+
XCTAssertEqualObjects([instanceManager removeInstanceWithIdentifier:0], object);
40+
XCTAssertEqual([instanceManager strongInstanceCount], 0);
3941
}
4042
@end

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ @interface FWFNavigationDelegateHostApiTests : XCTestCase
1414
@implementation FWFNavigationDelegateHostApiTests
1515
- (void)testCreateWithIdentifier {
1616
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
17-
FWFNavigationDelegateHostApiImpl *hostAPI =
18-
[[FWFNavigationDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager];
17+
FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc]
18+
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
19+
instanceManager:instanceManager];
1920

2021
FlutterError *error;
2122
[hostAPI createWithIdentifier:@0 error:&error];
@@ -25,4 +26,34 @@ - (void)testCreateWithIdentifier {
2526
XCTAssertTrue([navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]);
2627
XCTAssertNil(error);
2728
}
29+
30+
- (void)testDidFinishNavigation {
31+
FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
32+
FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc]
33+
initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
34+
instanceManager:instanceManager];
35+
36+
FlutterError *error;
37+
[hostAPI createWithIdentifier:@0 error:&error];
38+
FWFNavigationDelegate *navigationDelegate =
39+
(FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0];
40+
id mockDelegate = OCMPartialMock(navigationDelegate);
41+
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);
48+
49+
WKWebView *mockWebView = OCMClassMock([WKWebView class]);
50+
OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]);
51+
[instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
52+
53+
[mockDelegate webView:mockWebView didFinishNavigation:OCMClassMock([WKNavigation class])];
54+
OCMVerify([mockFlutterApi didFinishNavigationForDelegateWithIdentifier:@0
55+
webViewIdentifier:@1
56+
URL:@"https://flutter.dev/"
57+
completion:OCMOCK_ANY]);
58+
}
2859
@end

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ - (void)testDispose {
7676

7777
FlutterError *error;
7878
[hostAPI disposeObjectWithIdentifier:@0 error:&error];
79-
XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound);
79+
// Only the strong reference is removed, so the weak reference will remain until object is set to
80+
// nil.
81+
object = nil;
82+
XCTAssertFalse([instanceManager containsInstance:object]);
8083
XCTAssertNil(error);
8184
}
8285
@end

packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,6 @@ NSObject<FlutterMessageCodec> *FWFWKNavigationDelegateHostApiGetCodec(void);
303303

304304
@protocol FWFWKNavigationDelegateHostApi
305305
- (void)createWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error;
306-
- (void)setDidFinishNavigationForDelegateWithIdentifier:(NSNumber *)identifier
307-
functionIdentifier:(nullable NSNumber *)functionIdentifier
308-
error:(FlutterError *_Nullable *_Nonnull)error;
309306
@end
310307

311308
extern void FWFWKNavigationDelegateHostApiSetup(
@@ -317,7 +314,7 @@ NSObject<FlutterMessageCodec> *FWFWKNavigationDelegateFlutterApiGetCodec(void);
317314

318315
@interface FWFWKNavigationDelegateFlutterApi : NSObject
319316
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
320-
- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)functionIdentifier
317+
- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)identifier
321318
webViewIdentifier:(NSNumber *)webViewIdentifier
322319
URL:(nullable NSString *)url
323320
completion:(void (^)(NSError *_Nullable))completion;
@@ -343,13 +340,11 @@ NSObject<FlutterMessageCodec> *FWFNSObjectHostApiGetCodec(void);
343340
extern void FWFNSObjectHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
344341
NSObject<FWFNSObjectHostApi> *_Nullable api);
345342

346-
/// The codec used by FWFFunctionFlutterApi.
347-
NSObject<FlutterMessageCodec> *FWFFunctionFlutterApiGetCodec(void);
343+
/// The codec used by FWFNSObjectFlutterApi.
344+
NSObject<FlutterMessageCodec> *FWFNSObjectFlutterApiGetCodec(void);
348345

349-
@interface FWFFunctionFlutterApi : NSObject
346+
@interface FWFNSObjectFlutterApi : NSObject
350347
- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
351-
- (void)disposeFunctionWithIdentifier:(NSNumber *)identifier
352-
completion:(void (^)(NSError *_Nullable))completion;
353348
@end
354349
/// The codec used by FWFWKWebViewHostApi.
355350
NSObject<FlutterMessageCodec> *FWFWKWebViewHostApiGetCodec(void);

packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m

Lines changed: 15 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,32 +1150,6 @@ void FWFWKNavigationDelegateHostApiSetup(id<FlutterBinaryMessenger> binaryMessen
11501150
[channel setMessageHandler:nil];
11511151
}
11521152
}
1153-
{
1154-
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
1155-
initWithName:@"dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation"
1156-
binaryMessenger:binaryMessenger
1157-
codec:FWFWKNavigationDelegateHostApiGetCodec()];
1158-
if (api) {
1159-
NSCAssert(
1160-
[api respondsToSelector:@selector
1161-
(setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)],
1162-
@"FWFWKNavigationDelegateHostApi api (%@) doesn't respond to "
1163-
@"@selector(setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)",
1164-
api);
1165-
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
1166-
NSArray *args = message;
1167-
NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0);
1168-
NSNumber *arg_functionIdentifier = GetNullableObjectAtIndex(args, 1);
1169-
FlutterError *error;
1170-
[api setDidFinishNavigationForDelegateWithIdentifier:arg_identifier
1171-
functionIdentifier:arg_functionIdentifier
1172-
error:&error];
1173-
callback(wrapResult(nil, error));
1174-
}];
1175-
} else {
1176-
[channel setMessageHandler:nil];
1177-
}
1178-
}
11791153
}
11801154
@interface FWFWKNavigationDelegateFlutterApiCodecReader : FlutterStandardReader
11811155
@end
@@ -1222,7 +1196,7 @@ - (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)bina
12221196
}
12231197
return self;
12241198
}
1225-
- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionIdentifier
1199+
- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_identifier
12261200
webViewIdentifier:(NSNumber *)arg_webViewIdentifier
12271201
URL:(nullable NSString *)arg_url
12281202
completion:(void (^)(NSError *_Nullable))completion {
@@ -1232,7 +1206,7 @@ - (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionIde
12321206
binaryMessenger:self.binaryMessenger
12331207
codec:FWFWKNavigationDelegateFlutterApiGetCodec()];
12341208
[channel sendMessage:@[
1235-
arg_functionIdentifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null],
1209+
arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null],
12361210
arg_url ?: [NSNull null]
12371211
]
12381212
reply:^(id reply) {
@@ -1373,43 +1347,43 @@ void FWFNSObjectHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
13731347
}
13741348
}
13751349
}
1376-
@interface FWFFunctionFlutterApiCodecReader : FlutterStandardReader
1350+
@interface FWFNSObjectFlutterApiCodecReader : FlutterStandardReader
13771351
@end
1378-
@implementation FWFFunctionFlutterApiCodecReader
1352+
@implementation FWFNSObjectFlutterApiCodecReader
13791353
@end
13801354

1381-
@interface FWFFunctionFlutterApiCodecWriter : FlutterStandardWriter
1355+
@interface FWFNSObjectFlutterApiCodecWriter : FlutterStandardWriter
13821356
@end
1383-
@implementation FWFFunctionFlutterApiCodecWriter
1357+
@implementation FWFNSObjectFlutterApiCodecWriter
13841358
@end
13851359

1386-
@interface FWFFunctionFlutterApiCodecReaderWriter : FlutterStandardReaderWriter
1360+
@interface FWFNSObjectFlutterApiCodecReaderWriter : FlutterStandardReaderWriter
13871361
@end
1388-
@implementation FWFFunctionFlutterApiCodecReaderWriter
1362+
@implementation FWFNSObjectFlutterApiCodecReaderWriter
13891363
- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data {
1390-
return [[FWFFunctionFlutterApiCodecWriter alloc] initWithData:data];
1364+
return [[FWFNSObjectFlutterApiCodecWriter alloc] initWithData:data];
13911365
}
13921366
- (FlutterStandardReader *)readerWithData:(NSData *)data {
1393-
return [[FWFFunctionFlutterApiCodecReader alloc] initWithData:data];
1367+
return [[FWFNSObjectFlutterApiCodecReader alloc] initWithData:data];
13941368
}
13951369
@end
13961370

1397-
NSObject<FlutterMessageCodec> *FWFFunctionFlutterApiGetCodec() {
1371+
NSObject<FlutterMessageCodec> *FWFNSObjectFlutterApiGetCodec() {
13981372
static dispatch_once_t sPred = 0;
13991373
static FlutterStandardMessageCodec *sSharedObject = nil;
14001374
dispatch_once(&sPred, ^{
1401-
FWFFunctionFlutterApiCodecReaderWriter *readerWriter =
1402-
[[FWFFunctionFlutterApiCodecReaderWriter alloc] init];
1375+
FWFNSObjectFlutterApiCodecReaderWriter *readerWriter =
1376+
[[FWFNSObjectFlutterApiCodecReaderWriter alloc] init];
14031377
sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter];
14041378
});
14051379
return sSharedObject;
14061380
}
14071381

1408-
@interface FWFFunctionFlutterApi ()
1382+
@interface FWFNSObjectFlutterApi ()
14091383
@property(nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;
14101384
@end
14111385

1412-
@implementation FWFFunctionFlutterApi
1386+
@implementation FWFNSObjectFlutterApi
14131387

14141388
- (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)binaryMessenger {
14151389
self = [super init];
@@ -1418,17 +1392,6 @@ - (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)bina
14181392
}
14191393
return self;
14201394
}
1421-
- (void)disposeFunctionWithIdentifier:(NSNumber *)arg_identifier
1422-
completion:(void (^)(NSError *_Nullable))completion {
1423-
FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
1424-
messageChannelWithName:@"dev.flutter.pigeon.FunctionFlutterApi.dispose"
1425-
binaryMessenger:self.binaryMessenger
1426-
codec:FWFFunctionFlutterApiGetCodec()];
1427-
[channel sendMessage:@[ arg_identifier ?: [NSNull null] ]
1428-
reply:^(id reply) {
1429-
completion(nil);
1430-
}];
1431-
}
14321395
@end
14331396
@interface FWFWKWebViewHostApiCodecReader : FlutterStandardReader
14341397
@end

packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,30 @@
66

77
NS_ASSUME_NONNULL_BEGIN
88

9+
typedef void (^FWFOnDeallocCallback)(long identifier);
10+
911
/**
10-
* Maintains instances to intercommunicate with Dart objects.
12+
* Maintains instances used to communicate with the corresponding objects in Dart.
1113
*
1214
* When an instance is added with an identifier, either can be used to retrieve the other.
15+
*
16+
* Added instances are added as a weak reference and a strong reference. When the strong reference
17+
* is removed with `removeStrongReferenceWithIdentifier:` and the weak reference is deallocated,
18+
* the `deallocCallback` is made with the instance's identifier. However, if the strong reference is
19+
* removed and then the identifier is retrieved with the intention to pass the identifier to Dart
20+
* (e.g. calling `identifierForInstance:identifierWillBePassedToFlutter:` with
21+
* `identifierWillBePassedToFlutter` set to YES), the strong reference to the instance is recreated.
22+
* The strong reference will then need to be removed manually again.
23+
*
24+
* Accessing and inserting to an InstanceManager is thread safe.
1325
*/
1426
@interface FWFInstanceManager : NSObject
27+
@property(readonly) FWFOnDeallocCallback deallocCallback;
28+
- (instancetype)initWithDeallocCallback:(FWFOnDeallocCallback)callback;
1529
// TODO(bparrishMines): Pairs should not be able to be overwritten and this feature
16-
// should be replaced with a call to clear the manager in the event of a hot restart
17-
// instead.
30+
// should be replaced with a call to clear the manager in the event of a hot restart.
1831
/**
19-
* Adds a new instance to the manager that was instantiated by Dart.
32+
* Adds a new instance that was instantiated from Dart.
2033
*
2134
* If an instance or identifier has already been added, it will be replaced by the new values. The
2235
* Dart InstanceManager is considered the source of truth and has the capability to overwrite stored
@@ -28,43 +41,57 @@ NS_ASSUME_NONNULL_BEGIN
2841
- (void)addDartCreatedInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier;
2942

3043
/**
31-
* Removes the instance paired with a given identifier from the manager.
32-
*
33-
* @param instanceIdentifier The identifier paired to an instance.
44+
* Adds a new instance that was instantiated from the host platform.
3445
*
35-
* @return The removed instance if the manager contains the given instanceIdentifier, otherwise
36-
* nil.
46+
* @param instance The instance to be stored.
47+
* @return The unique identifier stored with instance.
3748
*/
38-
- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier;
49+
- (long)addHostCreatedInstance:(nonnull NSObject *)instance;
3950

4051
/**
41-
* Removes the instance from the manager.
52+
* Removes `instanceIdentifier` and its associated strongly referenced instance, if present, from
53+
* the manager.
4254
*
43-
* @param instance The instance to be removed from the manager.
55+
* @param instanceIdentifier The identifier paired to an instance.
4456
*
45-
* @return The identifier of the removed instance if the manager contains the given instance,
46-
* otherwise NSNotFound.
57+
* @return The removed instance if the manager contains the given instanceIdentifier, otherwise
58+
* nil.
4759
*/
48-
- (long)removeInstance:(NSObject *)instance;
60+
- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier;
4961

5062
/**
51-
* Retrieves the instance paired with identifier.
63+
* Retrieves the instance associated with identifier.
5264
*
5365
* @param instanceIdentifier The identifier paired to an instance.
5466
*
55-
* @return The paired instance if the manager contains the given instanceIdentifier,
67+
* @return The instance associated with `instanceIdentifier` if the manager contains the value,
5668
* otherwise nil.
5769
*/
5870
- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier;
5971

6072
/**
6173
* Retrieves the identifier paired with an instance.
6274
*
75+
* If the manager contains `instance`, as a strong or weak reference, the strong reference to
76+
* `instance` will be recreated and will need to be removed again with
77+
* `removeInstanceWithIdentifier:`.
78+
*
79+
* This method also expects the Dart `InstanceManager` to have, or recreate, a weak reference to the
80+
* instance the identifier is associated with once it receives it.
81+
*
6382
* @param instance An instance that may be stored in the manager.
6483
*
65-
* @return The paired identifer if the manager contains the given instance, otherwise NSNotFound.
84+
* @return The identifier associated with `instance` if the manager contains the value, otherwise
85+
* NSNotFound.
86+
*/
87+
- (long)identifierWithStrongReferenceForInstance:(nonnull NSObject *)instance;
88+
89+
/**
90+
* Returns whether this manager contains the given `instance`.
91+
*
92+
* @return Whether this manager contains the given `instance`.
6693
*/
67-
- (long)identifierForInstance:(NSObject *)instance;
94+
- (BOOL)containsInstance:(nonnull NSObject *)instance;
6895
@end
6996

7097
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)