Skip to content

Commit 5b94581

Browse files
feat(ios): handle private views (#524)
1 parent 476c29e commit 5b94581

File tree

12 files changed

+425
-8
lines changed

12 files changed

+425
-8
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#import <XCTest/XCTest.h>
2+
#import <OCMock/OCMock.h>
3+
#import <PrivateViewApi.h>
4+
#import <Flutter/Flutter.h>
5+
#import "FlutterPluginRegistrar+FlutterEngine.h"
6+
7+
8+
@interface MockFlutterPluginRegistrar : NSObject <FlutterPluginRegistrar>
9+
@end
10+
11+
@implementation MockFlutterPluginRegistrar
12+
13+
@end
14+
15+
16+
@interface PrivateViewApiTests : XCTestCase
17+
@property (nonatomic, strong) PrivateViewApi *api;
18+
@property (nonatomic, strong) id mockFlutterApi;
19+
@property (nonatomic, strong) id mockRegistrar;
20+
@property (nonatomic, strong) id mockFlutterViewController;
21+
@property (nonatomic, strong) id mockEngine;
22+
23+
@end
24+
25+
@implementation PrivateViewApiTests
26+
27+
#pragma mark - Setup / Teardown
28+
29+
- (void)setUp {
30+
[super setUp];
31+
32+
33+
self.mockFlutterApi = OCMClassMock([InstabugPrivateViewApi class]);
34+
35+
36+
MockFlutterPluginRegistrar *mockRegistrar = [[MockFlutterPluginRegistrar alloc] init];
37+
38+
self.mockRegistrar = OCMPartialMock(mockRegistrar);
39+
40+
self.mockEngine = OCMClassMock([FlutterEngine class]);
41+
OCMStub([self.mockRegistrar flutterEngine]).andReturn(self.mockEngine);
42+
43+
self.mockFlutterViewController = OCMClassMock([UIViewController class]);
44+
45+
OCMStub([self.mockEngine viewController]).andReturn(_mockFlutterViewController);
46+
47+
self.api = OCMPartialMock([[PrivateViewApi alloc] initWithFlutterApi:self.mockFlutterApi registrar: self.mockRegistrar]);
48+
}
49+
50+
- (void)tearDown {
51+
[self.mockFlutterApi stopMocking];
52+
[self.mockRegistrar stopMocking];
53+
[self.mockFlutterViewController stopMocking];
54+
[self.mockEngine stopMocking];
55+
56+
self.api = nil;
57+
58+
[super tearDown];
59+
}
60+
61+
#pragma mark - Tests
62+
63+
- (void)testMask_Success {
64+
XCTestExpectation *expectation = [self expectationWithDescription:@"Mask method success"];
65+
66+
CGSize imageSize = CGSizeMake(100, 100); // 100x100 pixels
67+
68+
// Step 2: Create the image using UIGraphicsImageRenderer
69+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize];
70+
71+
UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
72+
// Draw a red rectangle as an example
73+
[[UIColor redColor] setFill];
74+
CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height);
75+
UIRectFill(rect);
76+
}];
77+
78+
NSArray<NSNumber *> *rectangles = @[@10, @20, @30, @40];
79+
UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 30, 40)];
80+
81+
OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:rectangles, [NSNull null], nil])]);
82+
83+
84+
85+
OCMStub([self.mockFlutterViewController view]).andReturn(mockView);
86+
87+
88+
[self.api mask:screenshot completion:^(UIImage *result) {
89+
XCTAssertNotNil(result, @"Masked image should be returned.");
90+
[expectation fulfill];
91+
}];
92+
93+
[self waitForExpectationsWithTimeout:1 handler:nil];
94+
}
95+
96+
- (void)testMask_Error {
97+
XCTestExpectation *expectation = [self expectationWithDescription:@"Mask method with error"];
98+
99+
UIImage *screenshot = [UIImage new];
100+
FlutterError *error = [FlutterError errorWithCode:@"ERROR" message:@"Test error" details:nil];
101+
102+
OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], error, nil])]);
103+
104+
[self.api mask:screenshot completion:^(UIImage *result) {
105+
XCTAssertEqual(result, screenshot, @"Original screenshot should be returned on error.");
106+
[expectation fulfill];
107+
}];
108+
109+
[self waitForExpectationsWithTimeout:1 handler:nil];
110+
}
111+
112+
- (void)testGetFlutterViewOrigin_ValidView {
113+
UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 100, 100)];
114+
115+
OCMStub([self.mockFlutterViewController view]).andReturn(mockView);
116+
117+
UIWindow* testWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
118+
[testWindow addSubview:mockView];
119+
120+
CGPoint origin = [self.api getFlutterViewOrigin];
121+
122+
XCTAssertEqual(origin.x, 10);
123+
XCTAssertEqual(origin.y, 20);
124+
}
125+
126+
- (void)testGetFlutterViewOrigin_NilView {
127+
128+
OCMStub([self.mockFlutterViewController view]).andReturn(nil);
129+
//
130+
CGPoint origin = [self.api getFlutterViewOrigin];
131+
132+
XCTAssertEqual(origin.x, 0);
133+
XCTAssertEqual(origin.y, 0);
134+
}
135+
136+
- (void)testDrawMaskedImage {
137+
CGSize size = CGSizeMake(100, 100);
138+
UIGraphicsBeginImageContext(size);
139+
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
140+
UIGraphicsEndImageContext();
141+
142+
NSArray<NSValue *> *privateViews = @[
143+
[NSValue valueWithCGRect:CGRectMake(10, 10, 20, 20)],
144+
[NSValue valueWithCGRect:CGRectMake(30, 30, 10, 10)]
145+
];
146+
147+
UIImage *result = [self.api drawMaskedImage:screenshot withPrivateViews:privateViews];
148+
149+
XCTAssertNotNil(result);
150+
XCTAssertEqual(result.size.width, 100);
151+
XCTAssertEqual(result.size.height, 100);
152+
}
153+
154+
- (void)testConvertToRectangles_ValidInput {
155+
NSArray<NSNumber *> *rectangles = @[@10, @20, @30, @40];
156+
UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(5, 5, 100, 100)];
157+
OCMStub([self.mockFlutterViewController view]).andReturn(mockView);
158+
159+
160+
NSArray<NSValue *> *converted = [self.api convertToRectangles:rectangles];
161+
162+
XCTAssertEqual(converted.count, 1);
163+
CGRect rect = [converted[0] CGRectValue];
164+
XCTAssertTrue(CGRectEqualToRect(rect, CGRectMake(10, 20, 21, 21)));
165+
}
166+
167+
- (void)testConcurrentMaskCalls {
168+
XCTestExpectation *expectation = [self expectationWithDescription:@"Handle concurrent calls"];
169+
170+
CGSize imageSize = CGSizeMake(100, 100); // 100x100 pixels
171+
172+
// Step 2: Create the image using UIGraphicsImageRenderer
173+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize];
174+
175+
UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
176+
// Draw a red rectangle as an example
177+
[[UIColor redColor] setFill];
178+
CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height);
179+
UIRectFill(rect);
180+
}];
181+
182+
NSArray<NSNumber *> *rectangles = @[@10, @20, @30, @40];
183+
184+
185+
OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:rectangles, [NSNull null], nil])]);
186+
187+
188+
dispatch_group_t group = dispatch_group_create();
189+
190+
for (int i = 0; i < 5; i++) {
191+
dispatch_group_enter(group);
192+
193+
[self.api mask:screenshot completion:^(UIImage *result) {
194+
XCTAssertNotNil(result, @"Each call should return a valid image.");
195+
dispatch_group_leave(group);
196+
}];
197+
}
198+
199+
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
200+
[expectation fulfill];
201+
});
202+
203+
[self waitForExpectationsWithTimeout:2 handler:nil];
204+
}
205+
206+
@end

example/ios/Podfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe
2727

2828
flutter_ios_podfile_setup
2929
target 'Runner' do
30+
pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec'
31+
3032
use_frameworks!
3133
use_modular_headers!
3234

example/ios/Podfile.lock

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,28 @@ PODS:
88

99
DEPENDENCIES:
1010
- Flutter (from `Flutter`)
11+
- Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec`)
1112
- instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`)
1213
- OCMock (= 3.6)
1314

1415
SPEC REPOS:
1516
trunk:
16-
- Instabug
1717
- OCMock
1818

1919
EXTERNAL SOURCES:
2020
Flutter:
2121
:path: Flutter
22+
Instabug:
23+
:podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec
2224
instabug_flutter:
2325
:path: ".symlinks/plugins/instabug_flutter/ios"
2426

2527
SPEC CHECKSUMS:
2628
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
27-
Instabug: 7a71890217b97b1e32dbca96661845396b66da2f
29+
Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc
2830
instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787
2931
OCMock: 5ea90566be239f179ba766fd9fbae5885040b992
3032

31-
PODFILE CHECKSUM: 8f7552fd115ace1988c3db54a69e4a123c448f84
33+
PODFILE CHECKSUM: f2e19aef9f983becf80950af8e2d9c1b8f57e7a2
3234

3335
COCOAPODS: 1.14.3

example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
1717
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
1818
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
19-
9D381ECFBB01BD0E978EBDF2 /* Pods_InstabugTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */; };
19+
BEF638212CC82C7C004D29E9 /* PrivateViewApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */; };
20+
BEF638292CCA5E2B004D29E9 /* Pods_InstabugTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */; };
2021
CC080E112937B7DB0041170A /* InstabugApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC080E102937B7DB0041170A /* InstabugApiTests.m */; };
2122
CC198C61293E1A21007077C8 /* SurveysApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC198C60293E1A21007077C8 /* SurveysApiTests.m */; };
2223
CC359DB92937720C0067A924 /* ApmApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC359DB82937720C0067A924 /* ApmApiTests.m */; };
@@ -26,6 +27,7 @@
2627
CC9925D7293DFB03001FD3EE /* InstabugLogApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9925D6293DFB03001FD3EE /* InstabugLogApiTests.m */; };
2728
CC9925D9293DFD7F001FD3EE /* RepliesApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9925D8293DFD7F001FD3EE /* RepliesApiTests.m */; };
2829
CCADBDD8293CFED300AE5EB8 /* BugReportingApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCADBDD7293CFED300AE5EB8 /* BugReportingApiTests.m */; };
30+
EDD1293B2F5742BC05EDD9F6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */; };
2931
/* End PBXBuildFile section */
3032

3133
/* Begin PBXContainerItemProxy section */
@@ -78,6 +80,9 @@
7880
B03C8370EEFE061BDDDA1DA1 /* Pods-InstabugUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugUITests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugUITests/Pods-InstabugUITests.debug.xcconfig"; sourceTree = "<group>"; };
7981
BA5633844585BB93FE7BCCE7 /* Pods-InstabugTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugTests.profile.xcconfig"; path = "Target Support Files/Pods-InstabugTests/Pods-InstabugTests.profile.xcconfig"; sourceTree = "<group>"; };
8082
BE26C80C2BD55575009FECCF /* IBGCrashReporting+CP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IBGCrashReporting+CP.h"; sourceTree = "<group>"; };
83+
BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateViewApiTests.m; sourceTree = "<group>"; };
84+
BEF6382C2CCA6176004D29E9 /* instabug_flutter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = instabug_flutter.framework; sourceTree = BUILT_PRODUCTS_DIR; };
85+
BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8186
BF9025BBD0A6FD7B193E903A /* Pods-InstabugTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugTests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugTests/Pods-InstabugTests.debug.xcconfig"; sourceTree = "<group>"; };
8287
C090017925D9A030006F3DAE /* InstabugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstabugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
8388
C090017D25D9A031006F3DAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -102,14 +107,15 @@
102107
buildActionMask = 2147483647;
103108
files = (
104109
65C88E6E8EAE049E32FF2F52 /* Pods_Runner.framework in Frameworks */,
110+
EDD1293B2F5742BC05EDD9F6 /* Pods_Runner.framework in Frameworks */,
105111
);
106112
runOnlyForDeploymentPostprocessing = 0;
107113
};
108114
C090017625D9A030006F3DAE /* Frameworks */ = {
109115
isa = PBXFrameworksBuildPhase;
110116
buildActionMask = 2147483647;
111117
files = (
112-
9D381ECFBB01BD0E978EBDF2 /* Pods_InstabugTests.framework in Frameworks */,
118+
BEF638292CCA5E2B004D29E9 /* Pods_InstabugTests.framework in Frameworks */,
113119
);
114120
runOnlyForDeploymentPostprocessing = 0;
115121
};
@@ -135,6 +141,8 @@
135141
54C1C903B090526284242B67 /* Frameworks */ = {
136142
isa = PBXGroup;
137143
children = (
144+
BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */,
145+
BEF6382C2CCA6176004D29E9 /* instabug_flutter.framework */,
138146
853739F5879F6E4272829F47 /* Pods_Runner.framework */,
139147
71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */,
140148
F5446C0D3B2623D9BCC7CCE3 /* Pods_InstabugUITests.framework */,
@@ -194,6 +202,7 @@
194202
C090017A25D9A031006F3DAE /* InstabugTests */ = {
195203
isa = PBXGroup;
196204
children = (
205+
BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */,
197206
CC198C60293E1A21007077C8 /* SurveysApiTests.m */,
198207
CC78720A2938D1C5008CB2A5 /* Util */,
199208
CC080E102937B7DB0041170A /* InstabugApiTests.m */,
@@ -457,6 +466,7 @@
457466
CC080E112937B7DB0041170A /* InstabugApiTests.m in Sources */,
458467
CC198C61293E1A21007077C8 /* SurveysApiTests.m in Sources */,
459468
CCADBDD8293CFED300AE5EB8 /* BugReportingApiTests.m in Sources */,
469+
BEF638212CC82C7C004D29E9 /* PrivateViewApiTests.m in Sources */,
460470
CC9925D9293DFD7F001FD3EE /* RepliesApiTests.m in Sources */,
461471
206286ED2ABD0A1F00925509 /* SessionReplayApiTests.m in Sources */,
462472
CC9925D2293DEB0B001FD3EE /* CrashReportingApiTests.m in Sources */,
@@ -802,6 +812,7 @@
802812
"@loader_path/Frameworks",
803813
);
804814
MTL_FAST_MATH = YES;
815+
ONLY_ACTIVE_ARCH = YES;
805816
PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugTests;
806817
PRODUCT_NAME = "$(TARGET_NAME)";
807818
SWIFT_OBJC_BRIDGING_HEADER = "InstabugTests/InstabugTests-Bridging-Header.h";
@@ -835,6 +846,7 @@
835846
"@loader_path/Frameworks",
836847
);
837848
MTL_FAST_MATH = YES;
849+
ONLY_ACTIVE_ARCH = YES;
838850
PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugTests;
839851
PRODUCT_NAME = "$(TARGET_NAME)";
840852
SWIFT_OBJC_BRIDGING_HEADER = "InstabugTests/InstabugTests-Bridging-Header.h";

ios/Classes/InstabugFlutterPlugin.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import "RepliesApi.h"
1010
#import "SessionReplayApi.h"
1111
#import "SurveysApi.h"
12+
#import "PrivateViewApi.h"
1213

1314
@implementation InstabugFlutterPlugin
1415

@@ -17,11 +18,14 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
1718
InitBugReportingApi([registrar messenger]);
1819
InitCrashReportingApi([registrar messenger]);
1920
InitFeatureRequestsApi([registrar messenger]);
20-
InitInstabugApi([registrar messenger]);
21+
PrivateViewApi* privateViewApi = InitPrivateViewApi([registrar messenger],registrar);
22+
InitInstabugApi([registrar messenger],privateViewApi);
2123
InitInstabugLogApi([registrar messenger]);
2224
InitRepliesApi([registrar messenger]);
2325
InitSessionReplayApi([registrar messenger]);
2426
InitSurveysApi([registrar messenger]);
27+
28+
2529
}
2630

2731
@end

ios/Classes/Modules/InstabugApi.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#import "InstabugPigeon.h"
2+
#import "PrivateViewApi.h"
23

3-
extern void InitInstabugApi(id<FlutterBinaryMessenger> messenger);
4+
extern void InitInstabugApi(id<FlutterBinaryMessenger> _Nonnull messenger, PrivateViewApi * _Nonnull api);
45

56
@interface InstabugApi : NSObject <InstabugHostApi>
7+
@property (nonatomic, strong) PrivateViewApi* _Nonnull privateViewApi;
68

79
- (UIImage *)getImageForAsset:(NSString *)assetName;
810
- (UIFont *)getFontForAsset:(NSString *)assetName error:(FlutterError *_Nullable *_Nonnull)error;

ios/Classes/Modules/InstabugApi.m

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
#import "IBGNetworkLogger+CP.h"
66
#import "InstabugApi.h"
77
#import "ArgsRegistry.h"
8+
#import "../Util/Instabug+CP.h"
89

910
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16)) / 255.0 green:((float)((rgbValue & 0xFF00) >> 8)) / 255.0 blue:((float)(rgbValue & 0xFF)) / 255.0 alpha:((float)((rgbValue & 0xFF000000) >> 24)) / 255.0];
1011

11-
extern void InitInstabugApi(id<FlutterBinaryMessenger> messenger) {
12+
extern void InitInstabugApi(id<FlutterBinaryMessenger> _Nonnull messenger, PrivateViewApi * _Nonnull privateViewApi) {
1213
InstabugApi *api = [[InstabugApi alloc] init];
14+
api.privateViewApi = privateViewApi;
1315
InstabugHostApiSetup(messenger, api);
1416
}
1517

@@ -53,6 +55,14 @@ - (void)initToken:(NSString *)token invocationEvents:(NSArray<NSString *> *)invo
5355

5456
[Instabug setSdkDebugLogsLevel:resolvedLogLevel];
5557
[Instabug startWithToken:token invocationEvents:resolvedEvents];
58+
59+
[Instabug setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) {
60+
[self.privateViewApi mask:screenshot completion:^(UIImage * _Nonnull maskedImage) {
61+
if (maskedImage != nil) {
62+
completion(maskedImage);
63+
}
64+
}];
65+
}];
5666
}
5767

5868
- (void)showWithError:(FlutterError *_Nullable *_Nonnull)error {

0 commit comments

Comments
 (0)