Skip to content

Commit 8dcc1d0

Browse files
committed
Refactor setTag and removeTag to FFI/JNI
1 parent 6fcf3ed commit 8dcc1d0

File tree

9 files changed

+102
-78
lines changed

9 files changed

+102
-78
lines changed

packages/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ class SentryFlutterPlugin :
6363
"closeNativeSdk" -> closeNativeSdk(result)
6464
"setExtra" -> setExtra(call.argument("key"), call.argument("value"), result)
6565
"removeExtra" -> removeExtra(call.argument("key"), result)
66-
"setTag" -> setTag(call.argument("key"), call.argument("value"), result)
67-
"removeTag" -> removeTag(call.argument("key"), result)
6866
"setReplayConfig" -> setReplayConfig(call, result)
6967
"captureReplay" -> captureReplay(result)
7068
else -> result.notImplemented()
@@ -166,33 +164,6 @@ class SentryFlutterPlugin :
166164
result.success("")
167165
}
168166

169-
private fun setTag(
170-
key: String?,
171-
value: String?,
172-
result: Result,
173-
) {
174-
if (key == null || value == null) {
175-
result.success("")
176-
return
177-
}
178-
Sentry.setTag(key, value)
179-
180-
result.success("")
181-
}
182-
183-
private fun removeTag(
184-
key: String?,
185-
result: Result,
186-
) {
187-
if (key == null) {
188-
result.success("")
189-
return
190-
}
191-
Sentry.removeTag(key)
192-
193-
result.success("")
194-
}
195-
196167
private fun closeNativeSdk(result: Result) {
197168
ScopesAdapter.getInstance().close()
198169

packages/flutter/example/integration_test/integration_test.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,36 @@ void main() {
793793
expect(removedValues['key5'], isNull, reason: 'key5 should be removed');
794794
});
795795

796+
testWidgets('setTag and removeTag sync to native', (tester) async {
797+
await restoreFlutterOnErrorAfter(() async {
798+
await setupSentryAndApp(tester);
799+
});
800+
801+
await Sentry.configureScope((scope) async {
802+
scope.setTag('key1', 'randomValue');
803+
scope.setTag('key2', '12');
804+
});
805+
806+
var contexts = await SentryFlutter.native?.loadContexts();
807+
final tags = contexts!['tags'];
808+
expect(tags, isNotNull, reason: 'Tags are null');
809+
810+
expect(tags['key1'], 'randomValue', reason: 'key1 mismatch');
811+
expect(tags['key2'], '12', reason: 'key2 mismatch');
812+
813+
await Sentry.configureScope((scope) async {
814+
scope.removeTag('key1');
815+
scope.removeTag('key2');
816+
});
817+
818+
contexts = await SentryFlutter.native?.loadContexts();
819+
if (Platform.isIOS) {
820+
expect(contexts!['tags'], isNull, reason: 'Tags are not null');
821+
} else if (Platform.isAndroid) {
822+
expect(contexts!['tags'], isEmpty, reason: 'Tags are not empty');
823+
}
824+
});
825+
796826
group('e2e', () {
797827
var output = find.byKey(const Key('output'));
798828
late Fixture fixture;

packages/flutter/ffi-cocoa.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ objc-interfaces:
4040
- 'clearBreadcrumbs'
4141
- 'setContextValue:forKey:'
4242
- 'removeContextForKey:'
43+
- 'setTagValue:forKey:'
44+
- 'removeTagForKey:'
4345
preamble: |
4446
// ignore_for_file: type=lint, unused_element
4547

packages/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
8686
let key = arguments?["key"] as? String
8787
removeExtra(key: key, result: result)
8888

89-
case "setTag":
90-
let arguments = call.arguments as? [String: Any?]
91-
let key = arguments?["key"] as? String
92-
let value = arguments?["value"] as? String
93-
setTag(key: key, value: value, result: result)
94-
95-
case "removeTag":
96-
let arguments = call.arguments as? [String: Any?]
97-
let key = arguments?["key"] as? String
98-
removeTag(key: key, result: result)
99-
10089
#if !os(tvOS) && !os(watchOS)
10190
case "discardProfiler":
10291
discardProfiler(call, result)
@@ -278,30 +267,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
278267
}
279268
}
280269

281-
private func setTag(key: String?, value: String?, result: @escaping FlutterResult) {
282-
guard let key = key, let value = value else {
283-
result("")
284-
return
285-
}
286-
SentrySDK.configureScope { scope in
287-
scope.setTag(value: value, key: key)
288-
289-
result("")
290-
}
291-
}
292-
293-
private func removeTag(key: String?, result: @escaping FlutterResult) {
294-
guard let key = key else {
295-
result("")
296-
return
297-
}
298-
SentrySDK.configureScope { scope in
299-
scope.removeTag(key: key)
300-
301-
result("")
302-
}
303-
}
304-
305270
private func collectProfile(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
306271
guard let arguments = call.arguments as? [String: Any],
307272
let traceId = arguments["traceId"] as? String else {

packages/flutter/lib/src/native/cocoa/binding.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,8 @@ interface class SentrySerializable extends objc.ObjCProtocolBase
11481148
: this._(other, retain: retain, release: release);
11491149
}
11501150

1151+
late final _sel_setTagValue_forKey_ = objc.registerName("setTagValue:forKey:");
1152+
late final _sel_removeTagForKey_ = objc.registerName("removeTagForKey:");
11511153
late final _sel_clearBreadcrumbs = objc.registerName("clearBreadcrumbs");
11521154
final _objc_msgSend_1pl9qdv = objc.msgSendPointer
11531155
.cast<
@@ -1183,6 +1185,19 @@ class SentryScope extends objc.NSObject implements SentrySerializable {
11831185
obj.ref.pointer, _sel_isKindOfClass_, _class_SentryScope);
11841186
}
11851187

1188+
/// Set a global tag. Tags are searchable key/value string pairs attached to
1189+
/// every event.
1190+
void setTagValue(objc.NSString value, {required objc.NSString forKey}) {
1191+
_objc_msgSend_pfv6jd(this.ref.pointer, _sel_setTagValue_forKey_,
1192+
value.ref.pointer, forKey.ref.pointer);
1193+
}
1194+
1195+
/// Remove the tag for the specified key.
1196+
void removeTagForKey(objc.NSString key) {
1197+
_objc_msgSend_xtuoz7(
1198+
this.ref.pointer, _sel_removeTagForKey_, key.ref.pointer);
1199+
}
1200+
11861201
/// Clears all breadcrumbs in the scope
11871202
void clearBreadcrumbs() {
11881203
_objc_msgSend_1pl9qdv(this.ref.pointer, _sel_clearBreadcrumbs);

packages/flutter/lib/src/native/cocoa/sentry_native_cocoa.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,24 @@ class SentryNativeCocoa extends SentryNativeChannel {
266266
scope.removeContextForKey(key.toNSString());
267267
}));
268268
});
269+
270+
@override
271+
void setTag(String key, String value) => tryCatchSync('setTag', () {
272+
cocoa.SentrySDK.configureScope(
273+
cocoa.ObjCBlock_ffiVoid_SentryScope.fromFunction(
274+
(cocoa.SentryScope scope) {
275+
scope.setTagValue(value.toNSString(), forKey: key.toNSString());
276+
}));
277+
});
278+
279+
@override
280+
void removeTag(String key) => tryCatchSync('removeTag', () {
281+
cocoa.SentrySDK.configureScope(
282+
cocoa.ObjCBlock_ffiVoid_SentryScope.fromFunction(
283+
(cocoa.SentryScope scope) {
284+
scope.removeTagForKey(key.toNSString());
285+
}));
286+
});
269287
}
270288

271289
// The default conversion does not handle bool so we will add it ourselves

packages/flutter/lib/src/native/java/sentry_native_java.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,23 @@ class SentryNativeJava extends SentryNativeChannel {
319319
});
320320
})));
321321
});
322+
323+
@override
324+
void setTag(String key, String value) => tryCatchSync('setTag', () {
325+
using((arena) {
326+
final jKey = key.toJString()..releasedBy(arena);
327+
final jVal = value.toJString()..releasedBy(arena);
328+
native.Sentry.setTag(jKey, jVal);
329+
});
330+
});
331+
332+
@override
333+
void removeTag(String key) => tryCatchSync('removeTag', () {
334+
using((arena) {
335+
final jKey = key.toJString()..releasedBy(arena);
336+
native.Sentry.removeTag(jKey);
337+
});
338+
});
322339
}
323340

324341
JObject? _dartToJObject(Object? value, Arena arena) => switch (value) {

packages/flutter/lib/src/native/sentry_native_channel.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,14 @@ class SentryNativeChannel
158158
channel.invokeMethod('removeExtra', {'key': key});
159159

160160
@override
161-
Future<void> setTag(String key, String value) =>
162-
channel.invokeMethod('setTag', {'key': key, 'value': value});
161+
FutureOr<void> setTag(String key, String value) {
162+
assert(false, 'setTag should not be used through method channels.');
163+
}
163164

164165
@override
165-
Future<void> removeTag(String key) =>
166-
channel.invokeMethod('removeTag', {'key': key});
166+
FutureOr<void> removeTag(String key) {
167+
assert(false, 'removeTag should not be used through method channels.');
168+
}
167169

168170
@override
169171
int? startProfiler(SentryId traceId) =>

packages/flutter/test/sentry_native_channel_test.dart

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,27 @@ void main() {
142142
});
143143

144144
test('setTag', () async {
145-
when(channel.invokeMethod(
146-
'setTag', {'key': 'fixture-key', 'value': 'fixture-value'}))
147-
.thenAnswer((_) => Future.value());
145+
final matcher = _nativeUnavailableMatcher(
146+
mockPlatform,
147+
includeLookupSymbol: true,
148+
includeFailedToLoadClassException: true,
149+
);
148150

149-
await sut.setTag('fixture-key', 'fixture-value');
151+
expect(() => sut.setTag('fixture-key', 'fixture-value'), matcher);
150152

151-
verify(channel.invokeMethod(
152-
'setTag', {'key': 'fixture-key', 'value': 'fixture-value'}));
153+
verifyZeroInteractions(channel);
153154
});
154155

155156
test('removeTag', () async {
156-
when(channel.invokeMethod('removeTag', {'key': 'fixture-key'}))
157-
.thenAnswer((_) => Future.value());
157+
final matcher = _nativeUnavailableMatcher(
158+
mockPlatform,
159+
includeLookupSymbol: true,
160+
includeFailedToLoadClassException: true,
161+
);
158162

159-
await sut.removeTag('fixture-key');
163+
expect(() => sut.removeTag('fixture-key'), matcher);
160164

161-
verify(channel.invokeMethod('removeTag', {'key': 'fixture-key'}));
165+
verifyZeroInteractions(channel);
162166
});
163167

164168
test('startProfiler', () {

0 commit comments

Comments
 (0)