Skip to content

Commit c6bd77f

Browse files
committed
Use cinterop with stub headers instead of Flutter cocoapod dependency
This way a KMP project using this library does not have to declare Flutter as a pod dependency. The Flutter symbols are then eventually resolved when building the final Flutter app which has the Flutter binaries.
1 parent c99002b commit c6bd77f

File tree

11 files changed

+123
-68
lines changed

11 files changed

+123
-68
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## unreleased
44

5+
- Use cinterop with stub headers instead of Flutter cocoapod dependency
6+
57
## v0.1.0-rc.3
68

79
- Fix conflicts with same method names across different modules on iOS by prefixing method names

example/FlutterKmpExample.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ Pod::Spec.new do |spec|
88
spec.summary = 'Shared Kotlin code for flutter-kmp example'
99
spec.vendored_frameworks = 'build/cocoapods/framework/flutterkmpexample.framework'
1010
spec.libraries = 'c++'
11-
spec.ios.deployment_target = '11.0'
12-
spec.dependency 'Flutter'
11+
12+
1313

1414
if !Dir.exist?('build/cocoapods/framework/flutterkmpexample.framework') || Dir.empty?('build/cocoapods/framework/flutterkmpexample.framework')
1515
raise "

example/build.gradle.kts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,11 @@ kotlin {
3333
homepage = "https://github.com/voize-gmbh/flutter-kmp"
3434
summary = "Shared Kotlin code for flutter-kmp example"
3535
baseName = "flutterkmpexample"
36-
}
37-
38-
// We can not use a specific version here, because the podspec generated by this KMP project
39-
// will be referenced by the Flutter plugin podspec and therefore the Flutter target app.
40-
// The target app also depends on Flutter but the Flutter.podspec it uses has a stub version (1.0.0).
41-
// So specifying a version here will create a conflict and instead we have to rely on the assumption
42-
// that the interop for the Flutter in the Cocoapods registry is compatible with the Flutter version used in the target app.
43-
pod("Flutter")
4436

45-
ios.deploymentTarget = "11.0"
37+
// without setting the framework to static, you will get errors during linking
38+
// that symbols e.g. for _OBJC_CLASS_$_FlutterError are missing
39+
isStatic = true
40+
}
4641
}
4742

4843
sourceSets {

example/flutter/example/ios/Podfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ pod 'FlutterKmpExample', :path => '../../../FlutterKmpExample.podspec'
3232
flutter_ios_podfile_setup
3333

3434
target 'Runner' do
35-
use_frameworks!
35+
# use_frameworks needs to be disabled because the KMP project produces a static library.
36+
# Otherwise you get the following error:
37+
# [!] The 'Pods-Runner' target has transitive dependencies that include statically linked binaries
38+
# use_frameworks!
3639
use_modular_headers!
3740

3841
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

example/flutter/example/ios/Podfile.lock

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ PODS:
33
- flutter_kmp_example (0.0.1):
44
- Flutter
55
- FlutterKmpExample
6-
- FlutterKmpExample (0.1.0):
7-
- Flutter
6+
- FlutterKmpExample (0.1.0)
87
- integration_test (0.0.1):
98
- Flutter
109

@@ -27,9 +26,9 @@ EXTERNAL SOURCES:
2726
SPEC CHECKSUMS:
2827
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
2928
flutter_kmp_example: cef86282548c3343df655b50da93fb23981c5cfd
30-
FlutterKmpExample: 602f302c485b58e590be3d655f462b4962cdc4e3
29+
FlutterKmpExample: 353b38380378eaf5b71cee500a84dae03fa23f7d
3130
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
3231

33-
PODFILE CHECKSUM: 53af147c3b0053711becea2df6d246d410fe58ba
32+
PODFILE CHECKSUM: f0f94c29f034beaf74fec4fb487a5217cd1d58cc
3433

3534
COCOAPODS: 1.16.2

example/flutter/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
006073E245D621291EBBB998 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CDE66A62BBA07E3AE5C5429 /* Pods_RunnerTests.framework */; };
1110
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
12-
206231127C31445336DDE1DB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F1AD645DB26993E19F0D8C8 /* Pods_Runner.framework */; };
1311
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
1412
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
1513
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
1614
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
1715
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
1816
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
17+
C1936D0039B65844EC5CA124 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 685F6077958C4350FF9DFF6A /* libPods-Runner.a */; };
18+
C9E959614061D6948972AB93 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B383076F90ABE4E6305BF5A /* libPods-RunnerTests.a */; };
1919
/* End PBXBuildFile section */
2020

2121
/* Begin PBXContainerItemProxy section */
@@ -44,16 +44,16 @@
4444
/* Begin PBXFileReference section */
4545
00218362643D29004B31737F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
4646
0A32F207720B77AD414F285A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
47-
0CDE66A62BBA07E3AE5C5429 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4847
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
4948
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
5049
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
5150
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
51+
3B383076F90ABE4E6305BF5A /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5252
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
53+
685F6077958C4350FF9DFF6A /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
5354
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
5455
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5556
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
56-
8F1AD645DB26993E19F0D8C8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5757
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
5858
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
5959
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -72,15 +72,15 @@
7272
isa = PBXFrameworksBuildPhase;
7373
buildActionMask = 2147483647;
7474
files = (
75-
006073E245D621291EBBB998 /* Pods_RunnerTests.framework in Frameworks */,
75+
C9E959614061D6948972AB93 /* libPods-RunnerTests.a in Frameworks */,
7676
);
7777
runOnlyForDeploymentPostprocessing = 0;
7878
};
7979
97C146EB1CF9000F007C117D /* Frameworks */ = {
8080
isa = PBXFrameworksBuildPhase;
8181
buildActionMask = 2147483647;
8282
files = (
83-
206231127C31445336DDE1DB /* Pods_Runner.framework in Frameworks */,
83+
C1936D0039B65844EC5CA124 /* libPods-Runner.a in Frameworks */,
8484
);
8585
runOnlyForDeploymentPostprocessing = 0;
8686
};
@@ -90,8 +90,8 @@
9090
11B9E75F12A0C9BACF0999C2 /* Frameworks */ = {
9191
isa = PBXGroup;
9292
children = (
93-
8F1AD645DB26993E19F0D8C8 /* Pods_Runner.framework */,
94-
0CDE66A62BBA07E3AE5C5429 /* Pods_RunnerTests.framework */,
93+
685F6077958C4350FF9DFF6A /* libPods-Runner.a */,
94+
3B383076F90ABE4E6305BF5A /* libPods-RunnerTests.a */,
9595
);
9696
name = Frameworks;
9797
sourceTree = "<group>";
@@ -198,7 +198,6 @@
198198
97C146EC1CF9000F007C117D /* Resources */,
199199
9705A1C41CF9048500538489 /* Embed Frameworks */,
200200
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
201-
51D6551D7D0BBD8C3EB7BB8F /* [CP] Embed Pods Frameworks */,
202201
);
203202
buildRules = (
204203
);
@@ -308,23 +307,6 @@
308307
shellPath = /bin/sh;
309308
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
310309
};
311-
51D6551D7D0BBD8C3EB7BB8F /* [CP] Embed Pods Frameworks */ = {
312-
isa = PBXShellScriptBuildPhase;
313-
buildActionMask = 2147483647;
314-
files = (
315-
);
316-
inputFileListPaths = (
317-
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
318-
);
319-
name = "[CP] Embed Pods Frameworks";
320-
outputFileListPaths = (
321-
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
322-
);
323-
runOnlyForDeploymentPostprocessing = 0;
324-
shellPath = /bin/sh;
325-
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
326-
showEnvVarsInLog = 0;
327-
};
328310
74898FE0835FA79AEE6DF9BF /* [CP] Check Pods Manifest.lock */ = {
329311
isa = PBXShellScriptBuildPhase;
330312
buildActionMask = 2147483647;

flutter-kmp-ksp/src/main/kotlin/de/voize/flutterkmp/ksp/processor/IOSKotlinModuleGenerator.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ class IOSKotlinModuleGenerator {
138138
private val ExperimentalForeignApi = ClassName("kotlinx.cinterop", "ExperimentalForeignApi")
139139
private val NSObject = ClassName("platform.darwin", "NSObject")
140140

141-
private val FlutterResult = ClassName("cocoapods.Flutter", "FlutterResult")
142-
private val FlutterMethodCall = ClassName("cocoapods.Flutter", "FlutterMethodCall")
143-
private val FlutterMethodChannel = ClassName("cocoapods.Flutter", "FlutterMethodChannel")
144-
private val FlutterEventChannel = ClassName("cocoapods.Flutter", "FlutterEventChannel")
145-
private val FlutterPluginRegistrar = ClassName("cocoapods.Flutter", "FlutterPluginRegistrarProtocol")
146-
private val FlutterPlugin = ClassName("cocoapods.Flutter", "FlutterPluginProtocol")
147-
private val FlutterStandardMethodCodec = ClassName("cocoapods.Flutter", "FlutterStandardMethodCodec")
141+
private val FlutterResult = ClassName("flutter", "FlutterResult")
142+
private val FlutterMethodCall = ClassName("flutter", "FlutterMethodCall")
143+
private val FlutterMethodChannel = ClassName("flutter", "FlutterMethodChannel")
144+
private val FlutterEventChannel = ClassName("flutter", "FlutterEventChannel")
145+
private val FlutterPluginRegistrar = ClassName("flutter", "FlutterPluginRegistrarProtocol")
146+
private val FlutterPlugin = ClassName("flutter", "FlutterPluginProtocol")
147+
private val FlutterStandardMethodCodec = ClassName("flutter", "FlutterStandardMethodCodec")
148148
private val toEventStreamHandler = MemberName(flutterKmpPackageName, "toEventStreamHandler")

flutter-kmp/build.gradle.kts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
2+
13
plugins {
24
kotlin("multiplatform")
35
kotlin("plugin.serialization")
4-
kotlin("native.cocoapods")
56
id("com.android.library")
67
}
78

@@ -30,21 +31,18 @@ kotlin {
3031
publishLibraryVariants("release")
3132
}
3233

33-
cocoapods {
34-
// this cocoapods dependency is needed for the iOS EventStreamHandler utils
35-
pod("Flutter")
36-
37-
// we do not need to create a podspec file because the KMP projects that use this library
38-
// must produce a podspec that contains the Flutter cocoapod dependency
39-
noPodspec()
40-
41-
ios.deploymentTarget = "11.0"
34+
fun KotlinNativeTarget.configureFlutterInterop() {
35+
val main by compilations.getting {
36+
val flutter by cinterops.creating {
37+
includeDirs("src/nativeInterop/cinterop/")
38+
packageName("flutter")
39+
}
40+
}
4241
}
4342

44-
45-
iosX64()
46-
iosArm64()
47-
iosSimulatorArm64()
43+
iosX64 { configureFlutterInterop() }
44+
iosArm64 { configureFlutterInterop() }
45+
iosSimulatorArm64 { configureFlutterInterop() }
4846
wasmJs { nodejs() }
4947

5048
applyDefaultHierarchyTemplate()

flutter-kmp/src/iosMain/kotlin/EventStreamHandlerUtils.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package de.voize.flutterkmp
22

3-
import cocoapods.Flutter.FlutterError
4-
import cocoapods.Flutter.FlutterEventSink
53
import kotlinx.coroutines.flow.Flow
6-
import cocoapods.Flutter.FlutterStreamHandlerProtocol
74
import kotlinx.coroutines.CoroutineScope
85
import kotlinx.coroutines.Dispatchers
96
import kotlinx.coroutines.Job
@@ -12,7 +9,9 @@ import kotlinx.coroutines.withContext
129
import kotlinx.serialization.SerializationStrategy
1310
import kotlinx.serialization.serializer
1411
import platform.darwin.NSObject
15-
12+
import flutter.FlutterStreamHandlerProtocol
13+
import flutter.FlutterEventSink
14+
import flutter.FlutterError
1615

1716
inline fun <reified T> Flow<T>.toEventStreamHandler(): NSObject =
1817
toEventStreamHandler(serializer<T>())
@@ -45,4 +44,4 @@ fun <T> Flow<T>.toEventStreamHandler(serializer: SerializationStrategy<T>): NSOb
4544
return null
4645
}
4746
}
48-
}
47+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
2+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h
3+
4+
#import <Foundation/Foundation.h>
5+
6+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h#L49C1-L49C44
7+
@protocol FlutterBinaryMessenger <NSObject>
8+
@end
9+
10+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterChannels.h#L194
11+
typedef void (^FlutterResult)(id _Nullable result);
12+
13+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L391
14+
@protocol FlutterMethodCodec
15+
+ (instancetype)sharedInstance;
16+
@end
17+
18+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L18C1-L18C30
19+
@protocol FlutterMessageCodec
20+
+ (instancetype)sharedInstance;
21+
@end
22+
23+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterChannels.h#L220
24+
@interface FlutterMethodChannel : NSObject
25+
- (instancetype)initWithName:(NSString*)name
26+
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
27+
codec:(NSObject<FlutterMessageCodec>*)codec;
28+
@end
29+
30+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterChannels.h#L350
31+
typedef void (^FlutterEventSink)(id _Nullable event);
32+
33+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L246C1-L246C35
34+
@interface FlutterError : NSObject
35+
+ (instancetype)errorWithCode:(NSString*)code
36+
message:(NSString* _Nullable)message
37+
details:(id _Nullable)details;
38+
@end
39+
40+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterChannels.h#L356
41+
@protocol FlutterStreamHandler <NSObject>
42+
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
43+
eventSink:(FlutterEventSink)events;
44+
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments;
45+
@end
46+
47+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterChannels.h#L400C1-L400C42
48+
@interface FlutterEventChannel : NSObject
49+
- (instancetype)initWithName:(NSString*)name
50+
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
51+
codec:(NSObject<FlutterMethodCodec>*)codec;
52+
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>* _Nullable)handler;
53+
@end
54+
55+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h#L189
56+
@protocol FlutterPlugin <NSObject>
57+
@end
58+
59+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h#L283
60+
@protocol FlutterPluginRegistrar <NSObject>
61+
- (NSObject<FlutterBinaryMessenger>*)messenger;
62+
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
63+
channel:(FlutterMethodChannel*)channel;
64+
@end
65+
66+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L220
67+
@interface FlutterMethodCall : NSObject
68+
@property(readonly, nonatomic) NSString* method;
69+
@property(readonly, nonatomic, nullable) id arguments;
70+
@end
71+
72+
// https://github.com/flutter/engine/blob/3.22.2/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h#L469
73+
@interface FlutterStandardMethodCodec : NSObject <FlutterMethodCodec>
74+
@end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
language = Objective-C
2+
headers = Flutter/FlutterPlugin.h
3+
headerFilter = Flutter/*

0 commit comments

Comments
 (0)