Skip to content

Commit

Permalink
feat: Send Locale with Events
Browse files Browse the repository at this point in the history
Send the current locale with events. The SDK puts the locale into the device
context of the event.

Fixes GH-1438
  • Loading branch information
philipphofmann committed Dec 10, 2021
1 parent 15265e7 commit 1144518
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 12 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- feat: Send Locale with Events (#1539)

## 7.6.1

- fix: iOS13-Swift build (#1522)
Expand Down
35 changes: 33 additions & 2 deletions Sources/Sentry/SentryCrashIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
static dispatch_once_t installationToken = 0;
static SentryCrashInstallationReporter *installation = nil;

static NSString *const DEVICE_KEY = @"device";
static NSString *const LOCALE_KEY = @"locale";

@interface
SentryCrashIntegration ()

Expand Down Expand Up @@ -159,6 +162,8 @@ - (void)uninstall
installationToken = 0;
}
[self.crashAdapter deactivateAsyncHooks];

[NSNotificationCenter.defaultCenter removeObserver:self];
}

- (void)configureScope
Expand Down Expand Up @@ -229,10 +234,13 @@ - (void)configureScope
[deviceData setValue:systemInfo[@"bootTime"] forKey:@"boot_time"];
[deviceData setValue:systemInfo[@"timezone"] forKey:@"timezone"];

[outerScope setContextValue:deviceData forKey:@"device"];
NSString *locale =
[[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleIdentifier];
[deviceData setValue:locale forKey:LOCALE_KEY];

// APP
[outerScope setContextValue:deviceData forKey:DEVICE_KEY];

// APP
NSMutableDictionary *appData = [NSMutableDictionary new];
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];

Expand Down Expand Up @@ -264,6 +272,29 @@ - (void)configureScope
[outerScope addObserver:self.scopeObserver];
}];
}

[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(currentLocaleDidChange)
name:NSCurrentLocaleDidChangeNotification
object:nil];
}

- (void)currentLocaleDidChange
{
[SentrySDK.currentHub configureScope:^(SentryScope *_Nonnull scope) {
NSMutableDictionary<NSString *, id> *device;
if (scope.contextDictionary != nil && scope.contextDictionary[DEVICE_KEY] != nil) {
device = [[NSMutableDictionary alloc]
initWithDictionary:scope.contextDictionary[DEVICE_KEY]];
} else {
device = [NSMutableDictionary new];
}

NSString *locale = [[NSLocale autoupdatingCurrentLocale] objectForKey:NSLocaleIdentifier];
device[LOCALE_KEY] = locale;

[scope setContextValue:device forKey:DEVICE_KEY];
}];
}

@end
3 changes: 3 additions & 0 deletions Sources/Sentry/include/SentryScope+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ SentryScope (Private)

@property (atomic, strong) SentryUser *_Nullable userObject;

@property (atomic, strong)
NSMutableDictionary<NSString *, NSDictionary<NSString *, id> *> *contextDictionary;

- (void)addObserver:(id<SentryScopeObserver>)observer;

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,8 @@ class SentryCrashIntegrationTests: XCTestCase {
#endif

func testEndSessionAsCrashed_NoClientSet() {
let hub = SentryHub(client: nil, andScope: nil)
SentrySDK.setCurrentHub(hub)
let (sut, _) = givenSutWithGlobalHub()

let sut = fixture.getSut()
sut.install(with: Options())

let fileManager = fixture.fileManager
Expand All @@ -168,9 +166,8 @@ class SentryCrashIntegrationTests: XCTestCase {
}

func testEndSessionAsCrashed_NoCurrentSession() {
SentrySDK.setCurrentHub(fixture.hub)
let (sut, _) = givenSutWithGlobalHub()

let sut = fixture.getSut()
sut.install(with: Options())

let fileManager = fixture.fileManager
Expand Down Expand Up @@ -204,16 +201,53 @@ class SentryCrashIntegrationTests: XCTestCase {
XCTAssertTrue(fixture.sentryCrash.deactivateAsyncHooksCalled)
}

func testUninstall_DoesNotUpdateLocale_OnLocaleDidChangeNotification() {
let (sut, hub) = givenSutWithGlobalHub()

sut.install(with: Options())

let locale = "garbage"
setLocaleToGlobalScope(locale: locale)

sut.uninstall()

TestNotificationCenter.localeDidChange()

assertLocaleOnHub(locale: locale, hub: hub)
}

func testOSCorrectlySetToScopeContext() {
let hub = fixture.hub
SentrySDK.setCurrentHub(hub)
let (sut, hub) = givenSutWithGlobalHub()

let sut = fixture.getSut()
sut.install(with: Options())

let context = hub.scope.serialize()["context"]as? [String: Any] ?? ["": ""]
assertContext(context: hub.scope.contextDictionary as? [String: Any] ?? ["": ""])
}

func testLocaleChanged_NoDeviceContext_SetsCurrentLocale() {
let (sut, hub) = givenSutWithGlobalHub()

assertContext(context: context)
sut.install(with: Options())

SentrySDK.configureScope { scope in
scope.removeContext(key: "device")
}

TestNotificationCenter.localeDidChange()

assertLocaleOnHub(locale: Locale.autoupdatingCurrent.identifier, hub: hub)
}

func testLocaleChanged_DifferentLocale_SetsCurrentLocale() {
let (sut, hub) = givenSutWithGlobalHub()

sut.install(with: Options())

setLocaleToGlobalScope(locale: "garbage")

TestNotificationCenter.localeDidChange()

assertLocaleOnHub(locale: Locale.autoupdatingCurrent.identifier, hub: hub)
}

private func givenCurrentSession() -> SentrySession {
Expand All @@ -238,6 +272,26 @@ class SentryCrashIntegrationTests: XCTestCase {
}
#endif

private func givenSutWithGlobalHub() -> (SentryCrashIntegration, SentryHub) {
let sut = fixture.getSut()
let hub = fixture.hub
SentrySDK.setCurrentHub(hub)

return (sut, hub)
}

private func setLocaleToGlobalScope(locale: String) {
SentrySDK.configureScope { scope in
guard var device = scope.contextDictionary["device"] as? [String: Any] else {
XCTFail("No device found on context.")
return
}

device["locale"] = locale
scope.setContext(value: device, key: "device")
}
}

private func assertUserInfoField(userInfo: [AnyHashable: Any], key: String, expected: String) {
if let actual = userInfo[key] as? String {
XCTAssertEqual(expected, actual)
Expand Down Expand Up @@ -279,6 +333,19 @@ class SentryCrashIntegrationTests: XCTestCase {
XCTAssertEqual("tvOS", os["name"] as? String)
XCTAssertEqual(UIDevice.current.systemVersion, os["version"] as? String)
#endif

XCTAssertEqual(Locale.autoupdatingCurrent.identifier, device["locale"] as? String)
}

private func assertLocaleOnHub(locale: String, hub: SentryHub) {
let context = hub.scope.contextDictionary as? [String: Any] ?? ["": ""]

guard let device = context["device"] as? [String: Any] else {
XCTFail("No device found on context.")
return
}

XCTAssertEqual(locale, device["locale"] as? String)
}

private func advanceTime(bySeconds: TimeInterval) {
Expand Down
4 changes: 4 additions & 0 deletions Tests/SentryTests/Integrations/TestNotificationCenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,8 @@ class TestNotificationCenter {
NotificationCenter.default.post(Notification(name: UIWindow.didBecomeVisibleNotification))
#endif
}

static func localeDidChange() {
NotificationCenter.default.post(Notification(name: NSLocale.currentLocaleDidChangeNotification))
}
}

0 comments on commit 1144518

Please sign in to comment.