Skip to content

fix(session-tracker): add session start for SDK start after didBecomeActive #5121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 36 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d645fc9
fix(session-tracker): add session start for SDK start after didBecome…
philprime Apr 22, 2025
a79c8df
update changelog
philprime Apr 22, 2025
b858da6
revert unexpected changes
philprime Apr 22, 2025
4092925
remove unrelated changes
philprime Apr 22, 2025
2883753
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime Apr 23, 2025
d257543
added fix for didbecomeactive later
philprime Apr 23, 2025
c5ab7bd
update changelog
philprime Apr 23, 2025
f451c2c
fix compilation for macOS
philprime Apr 24, 2025
387fb61
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 12, 2025
8f05be7
apply feedback from review
philprime May 12, 2025
414ddc5
add shared application protocol
philprime May 13, 2025
8c786d4
update changelog
philprime May 13, 2025
03a160f
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 13, 2025
d6b40c7
update changelog
philprime May 13, 2025
a213431
add defines
philprime May 13, 2025
95ecbae
wip
philprime May 13, 2025
a459aed
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 15, 2025
58942e8
refactor tests to set correct application state
philprime May 15, 2025
2d7d2e5
fixes
philprime May 15, 2025
7d5c29f
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 19, 2025
4b71e34
Format code
getsentry-bot May 19, 2025
071d54c
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 19, 2025
90feb60
revert changes
philprime May 19, 2025
687b3a2
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 19, 2025
e0e4d2e
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 19, 2025
04990ef
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 21, 2025
49ca30a
revert merge errors
philprime May 22, 2025
f01dc17
Update CHANGELOG and add test for session tracker auto-start function…
philprime May 22, 2025
5697a38
WIP
philprime May 22, 2025
fb8fe97
WIP
philprime May 22, 2025
a4c08ec
use notification center in session tracker tests
philprime May 23, 2025
133654a
Merge remote-tracking branch 'origin/main' into philprime/issue-5069
philprime May 30, 2025
3b68d89
fix testing of session tracker tests using autoreleasing
philprime May 30, 2025
a17ba96
WIP
philprime May 30, 2025
f5b7bd6
refactor: Improve array element access and clean up session tracker t…
philprime May 30, 2025
04962a4
fix changelog
philprime May 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

## Fixes

- Fix auto-start for session tracker when SDK is started after app did become active (#5121)

## 8.52.0-beta

### Features
Expand Down
16 changes: 16 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,9 @@
A8F17B2E2901765900990B25 /* SentryRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = A8F17B2D2901765900990B25 /* SentryRequest.m */; };
A8F17B342902870300990B25 /* SentryHttpStatusCodeRange.m in Sources */ = {isa = PBXBuildFile; fileRef = A8F17B332902870300990B25 /* SentryHttpStatusCodeRange.m */; };
D4009EB22D771BC20007AF30 /* SentryFileIOTrackerSwiftHelpersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4009EB12D771BB90007AF30 /* SentryFileIOTrackerSwiftHelpersTests.swift */; };
D40604472DD2471600C40DC0 /* SentryNSApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D40604462DD2471600C40DC0 /* SentryNSApplication.m */; };
D40604492DD2472600C40DC0 /* SentryNSApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = D40604482DD2472600C40DC0 /* SentryNSApplication.h */; };
D406044B2DD2483D00C40DC0 /* SentryApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = D406044A2DD2483D00C40DC0 /* SentryApplication.h */; };
D4291A692DD61A3F00772088 /* SentryDispatchQueueProviderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D4291A672DD61A3F00772088 /* SentryDispatchQueueProviderProtocol.h */; };
D4291A6D2DD62ACE00772088 /* SentryDispatchFactoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D4291A6C2DD62AC800772088 /* SentryDispatchFactoryTests.m */; };
D42E48572D48DF1600D251BC /* SentryBuildAppStartSpansTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42E48562D48DF1600D251BC /* SentryBuildAppStartSpansTests.swift */; };
Expand Down Expand Up @@ -847,6 +850,7 @@
D4E3F35D2D4A864600F79E2B /* SentryNSDictionarySanitizeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D42E48582D48FC8F00D251BC /* SentryNSDictionarySanitizeTests.swift */; };
D4E3F35E2D4A877300F79E2B /* SentryNSDictionarySanitize+Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = D41909942D490006002B83D0 /* SentryNSDictionarySanitize+Tests.m */; };
D4EDF9842D0B2A210071E7B3 /* Data+SentryTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EDF9832D0B2A1D0071E7B3 /* Data+SentryTracing.swift */; };
D4EE12D22DE9AC3800385BAF /* TestNSNotificationCenterWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4EE12D12DE9AC3300385BAF /* TestNSNotificationCenterWrapperTests.swift */; };
D4F2B5352D0C69D500649E42 /* SentryCrashCTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F2B5342D0C69D100649E42 /* SentryCrashCTests.swift */; };
D4FC68172DD632E7001B74FF /* SentryDispatchSourceProviderProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D4FC68162DD632E7001B74FF /* SentryDispatchSourceProviderProtocol.h */; };
D4FC681A2DD63465001B74FF /* SentryDispatchQueueWrapperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D4FC68192DD63465001B74FF /* SentryDispatchQueueWrapperTests.m */; };
Expand Down Expand Up @@ -1996,6 +2000,9 @@
A8F17B2D2901765900990B25 /* SentryRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryRequest.m; sourceTree = "<group>"; };
A8F17B332902870300990B25 /* SentryHttpStatusCodeRange.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryHttpStatusCodeRange.m; sourceTree = "<group>"; };
D4009EB12D771BB90007AF30 /* SentryFileIOTrackerSwiftHelpersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFileIOTrackerSwiftHelpersTests.swift; sourceTree = "<group>"; };
D40604462DD2471600C40DC0 /* SentryNSApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryNSApplication.m; sourceTree = "<group>"; };
D40604482DD2472600C40DC0 /* SentryNSApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryNSApplication.h; path = include/SentryNSApplication.h; sourceTree = "<group>"; };
D406044A2DD2483D00C40DC0 /* SentryApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryApplication.h; path = include/SentryApplication.h; sourceTree = "<group>"; };
D41909922D48FFF6002B83D0 /* SentryNSDictionarySanitize+Tests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryNSDictionarySanitize+Tests.h"; sourceTree = "<group>"; };
D41909942D490006002B83D0 /* SentryNSDictionarySanitize+Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SentryNSDictionarySanitize+Tests.m"; sourceTree = "<group>"; };
D4291A672DD61A3F00772088 /* SentryDispatchQueueProviderProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDispatchQueueProviderProtocol.h; path = include/SentryDispatchQueueProviderProtocol.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2042,6 +2049,7 @@
D4CBA2432DE06D0200581618 /* SentryTestUtilsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SentryTestUtilsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D4CBA2512DE06D1600581618 /* TestConstantTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConstantTests.swift; sourceTree = "<group>"; };
D4EDF9832D0B2A1D0071E7B3 /* Data+SentryTracing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+SentryTracing.swift"; sourceTree = "<group>"; };
D4EE12D12DE9AC3300385BAF /* TestNSNotificationCenterWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSNotificationCenterWrapperTests.swift; sourceTree = "<group>"; };
D4F2B5342D0C69D100649E42 /* SentryCrashCTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryCrashCTests.swift; sourceTree = "<group>"; };
D4FC68162DD632E7001B74FF /* SentryDispatchSourceProviderProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDispatchSourceProviderProtocol.h; path = include/SentryDispatchSourceProviderProtocol.h; sourceTree = "<group>"; };
D4FC68192DD63465001B74FF /* SentryDispatchQueueWrapperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryDispatchQueueWrapperTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3822,8 +3830,11 @@
8E25C95125F836D000DC215B /* SentryRandom.m */,
D8C67E9A28000E23007E326E /* SentryScreenshot.h */,
D85852B427ECEEDA00C6D8AE /* SentryScreenshot.m */,
D406044A2DD2483D00C40DC0 /* SentryApplication.h */,
D8C67E9928000E23007E326E /* SentryUIApplication.h */,
D85852B827EDDC5900C6D8AE /* SentryUIApplication.m */,
D40604482DD2472600C40DC0 /* SentryNSApplication.h */,
D40604462DD2471600C40DC0 /* SentryNSApplication.m */,
D8F6A24A2885515B00320515 /* SentryPredicateDescriptor.h */,
D8F6A2452885512100320515 /* SentryPredicateDescriptor.m */,
0A2D8DA6289BC905008720F6 /* SentryViewHierarchy.h */,
Expand Down Expand Up @@ -3933,6 +3944,7 @@
D4CBA2522DE06D1600581618 /* SentryTestUtilsTests */ = {
isa = PBXGroup;
children = (
D4EE12D12DE9AC3300385BAF /* TestNSNotificationCenterWrapperTests.swift */,
D43B0E5D2DE7198000EE3759 /* Info.plist */,
D44B16712DE464A9006DBDB3 /* TestDispatchFactoryTests.swift */,
D4CBA2512DE06D1600581618 /* TestConstantTests.swift */,
Expand Down Expand Up @@ -4504,6 +4516,7 @@
33EB2A912C3412E4004FED3D /* SentryWithoutUIKit.h in Headers */,
639FCFAC1EBC811400778193 /* SentryUser.h in Headers */,
D8CB74192947285A00A5F964 /* SentryEnvelopeItemHeader.h in Headers */,
D40604492DD2472600C40DC0 /* SentryNSApplication.h in Headers */,
7D7F0A5F23DF3D2C00A4629C /* SentryGlobalEventProcessor.h in Headers */,
D867063D27C3BC2400048851 /* SentryCoreDataTrackingIntegration.h in Headers */,
0A2D8D5D289815EB008720F6 /* SentryBaseIntegration.h in Headers */,
Expand Down Expand Up @@ -4605,6 +4618,7 @@
7BB654FB253DC14A00887E87 /* SentryUserFeedback.h in Headers */,
7B8ECBFA26498907005FE2EF /* SentryAppStateManager.h in Headers */,
639FCFA01EBC804600778193 /* SentryException.h in Headers */,
D406044B2DD2483D00C40DC0 /* SentryApplication.h in Headers */,
7BA235632600B61200E12865 /* SentryInternalNotificationNames.h in Headers */,
7BAF3DB9243C9777008A5414 /* SentryTransport.h in Headers */,
845C16D52A622A5B00EC9519 /* SentryTracer+Private.h in Headers */,
Expand Down Expand Up @@ -5375,6 +5389,7 @@
D8A3649C2C91AA3300AC569B /* SentryReplayApi.m in Sources */,
7B42C48227E08F4B009B58C2 /* SentryDependencyContainer.m in Sources */,
639FCFAD1EBC811400778193 /* SentryUser.m in Sources */,
D40604472DD2471600C40DC0 /* SentryNSApplication.m in Sources */,
7DAC589123D8B2E0001CF26B /* SentryGlobalEventProcessor.m in Sources */,
7BBD189E244EC8D200427C76 /* SentryRetryAfterHeaderParser.m in Sources */,
63FE711920DA4C1000CDBAE8 /* SentryCrashMachineContext.c in Sources */,
Expand Down Expand Up @@ -5789,6 +5804,7 @@
files = (
D43B0E602DE7416900EE3759 /* TestFileManagerTests.swift in Sources */,
D44B16722DE464AD006DBDB3 /* TestDispatchFactoryTests.swift in Sources */,
D4EE12D22DE9AC3800385BAF /* TestNSNotificationCenterWrapperTests.swift in Sources */,
D4CBA2532DE06D1600581618 /* TestConstantTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
2 changes: 1 addition & 1 deletion SentryTestUtils/ArrayAccesses.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation

public extension Array {
func element(at index: Int) -> Self.Element? {
guard count >= index else {
guard count > index else {
return nil
}
return self[index]
Expand Down
68 changes: 53 additions & 15 deletions SentryTestUtils/TestNSNotificationCenterWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,55 @@ import Sentry
public var ignoreRemoveObserver = false
public var ignoreAddObserver = false

public var addObserverInvocations = Invocations<(observer: WeakReference<NSObject>, selector: Selector, name: NSNotification.Name)>()
public var addObserverInvocationsCount: Int {
return addObserverInvocations.count
public var addObserverInvocations = Invocations<(
observer: WeakReference<NSObject>,
selector: Selector,
name: NSNotification.Name
)>()
public override func addObserver(
_ observer: NSObject,
selector aSelector: Selector,
name aName: NSNotification.Name
) {
if ignoreAddObserver == false {
addObserverInvocations.record((WeakReference(value: observer), aSelector, aName))
}
}

public override func addObserver(_ observer: NSObject, selector aSelector: Selector, name aName: NSNotification.Name) {
public var addObserverWithObjectInvocations = Invocations<(
observer: WeakReference<NSObject>,
selector: Selector,
name: NSNotification.Name,
object: Any?
)>()
public override func addObserver(
_ observer: NSObject,
selector aSelector: Selector,
name aName: NSNotification.Name,
object anObject: Any?
) {
if ignoreAddObserver == false {
addObserverInvocations.record((WeakReference(value: observer), aSelector, aName))
addObserverWithObjectInvocations.record((WeakReference(value: observer), aSelector, aName, anObject))
}
}


public var addObserverForKeyPathWithContextInvocations = Invocations<(
observer: WeakReference<NSObject>,
keyPath: String,
options: NSKeyValueObservingOptions,
context: UnsafeMutableRawPointer?
)>()
public override func addObserver(
_ observer: NSObject,
forKeyPath keyPath: String,
options: NSKeyValueObservingOptions = [],
context: UnsafeMutableRawPointer?
) {
if ignoreAddObserver == false {
addObserverForKeyPathWithContextInvocations.record((WeakReference(value: observer), keyPath, options, context))
}
}

public var addObserverWithBlockInvocations = Invocations<(observer: WeakReference<NSObject>, name: NSNotification.Name?, block: (Notification) -> Void)>()
public override func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> any NSObjectProtocol {
let observer = NSObject()
Expand All @@ -25,20 +63,14 @@ import Sentry
}

public var removeObserverWithNameInvocations = Invocations< NSNotification.Name>()
public var removeObserverWithNameInvocationsCount: Int {
return removeObserverWithNameInvocations.count
}
public override func removeObserver(_ observer: any NSObjectProtocol, name aName: NSNotification.Name) {
removeObserverWithNameInvocations.record(aName)
}

/// We don't keep track of the actual objects, because removeObserver
/// gets often called in dealloc, and we don't want to store an object about to be deallocated
/// in an array.
var removeObserverInvocations = Invocations<Void>()
public var removeObserverInvocationsCount: Int {
return removeObserverInvocations.count
}
public var removeObserverInvocations = Invocations<Void>()
public override func removeObserver(_ observer: any NSObjectProtocol) {
if ignoreRemoveObserver == false {
removeObserverInvocations.record(Void())
Expand All @@ -47,9 +79,15 @@ import Sentry

public override func post(_ notification: Notification) {
addObserverInvocations.invocations
.filter { $0.2 == notification.name }
.filter { $0.name == notification.name }
.forEach { observer, selector, _ in
_ = observer.value?.perform(selector, with: nil)
_ = observer.value?.perform(selector, with: notification.object)
}
addObserverWithObjectInvocations.invocations
.filter { $0.name == notification.name }
.filter { ($0.object == nil && notification.object == nil) || $0.object as AnyObject === notification.object as AnyObject }
.forEach { observer, selector, _, _ in
_ = observer.value?.perform(selector, with: notification)
}
addObserverWithBlockInvocations.invocations.forEach { _, name, block in
if let name = name {
Expand Down
152 changes: 152 additions & 0 deletions SentryTestUtilsTests/TestNSNotificationCenterWrapperTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
@testable import SentryTestUtils
import XCTest

class TestNSNotificationCenterWrapperTests: XCTestCase {

private let notificationName = Notification.Name("TestNotification")
private let dummySelector = #selector(dummyMethod)

private var sut: TestNSNotificationCenterWrapper!

override func setUp() {
super.setUp()
sut = TestNSNotificationCenterWrapper()
}

func testAddObserver_whenIgnoreAddObserverIsFalse_shouldRecordObserver() throws {
// -- Arrange --
sut.ignoreAddObserver = false

// -- Act --
sut.addObserver(self, selector: dummySelector, name: notificationName)

// -- Assert --
let invocations = sut.addObserverInvocations
XCTAssertEqual(invocations.count, 1)
let invocation = try XCTUnwrap(invocations.invocations.first)
XCTAssertIdentical(invocation.observer.value, self)
XCTAssertEqual(invocation.selector, dummySelector)
XCTAssertEqual(invocation.name, notificationName)
}

func testAddObserver_whenIgnoreAddObserverIsTrue_shouldNotRecordObserver() {
// -- Arrange --
sut.ignoreAddObserver = true

// -- Act --
sut.addObserver(self, selector: dummySelector, name: notificationName)

// -- Assert --
XCTAssertEqual(sut.addObserverInvocations.count, 0)
}

func testAddObserverWithObject_whenIgnoreAddObserverIsFalse_shouldRecordObserver() throws {
// -- Arrange --
let object = "<some object>"
sut.ignoreAddObserver = false

// -- Act --
sut.addObserver(self, selector: dummySelector, name: notificationName, object: object)

// -- Assert --
let invocations = sut.addObserverWithObjectInvocations
XCTAssertEqual(invocations.count, 1)
let invocation = try XCTUnwrap(invocations.first)
XCTAssertIdentical(invocation.observer.value, self)
XCTAssertEqual(invocation.selector, dummySelector)
XCTAssertEqual(invocation.name, notificationName)
XCTAssertEqual(invocation.object as? String, object)
}

func testAddObserverWithObject_whenIgnoreAddObserverIsTrue_shouldNotRecordObserver() {
// -- Arrange --
sut.ignoreAddObserver = true

// -- Act --
sut.addObserver(self, selector: dummySelector, name: notificationName)

// -- Assert --
XCTAssertEqual(sut.addObserverWithObjectInvocations.count, 0)
}

func testAddObserverWithBlock_shouldRecordBlockObserver() throws {
// -- Arrange --
sut.ignoreAddObserver = false
let block: (Notification) -> Void = { _ in }

// -- Act --
let observer = sut.addObserver(forName: notificationName, object: nil, queue: nil, using: block)

// -- Assert --
XCTAssertNotNil(observer)

XCTAssertEqual(sut.addObserverWithBlockInvocations.count, 1)
let invocation = try XCTUnwrap(sut.addObserverWithBlockInvocations.invocations.first)
XCTAssertEqual(invocation.name, notificationName)
XCTAssertNotNil(invocation.block)
}

func testRemoveObserverWithName_shouldRecordRemoval() throws {
// -- Act --
sut.removeObserver(self, name: notificationName)

// -- Assert --
XCTAssertEqual(sut.removeObserverWithNameInvocations.count, 1)
let invocation = try XCTUnwrap(sut.removeObserverWithNameInvocations.invocations.first)
XCTAssertEqual(invocation, notificationName)
}

func testRemoveObserver_whenIgnoreRemoveObserverIsFalse_shouldRecordRemoval() throws {
// -- Arrange --
sut.ignoreRemoveObserver = false

// -- Act --
sut.removeObserver(self)

// -- Assert --
XCTAssertEqual(sut.removeObserverInvocations.count, 1)
}

func testRemoveObserver_whenIgnoreRemoveObserverIsTrue_shouldNotRecordRemoval() {
// -- Arrange --
sut.ignoreRemoveObserver = true

// -- Act --
sut.removeObserver(self)

// -- Assert --
XCTAssertEqual(sut.removeObserverInvocations.count, 0)
}

func testPost_whenObserverAdded_shouldPerformSelectorOnObserverObject() {
// -- Arrange --
class Observer: NSObject {
let invocations = Invocations<Notification?>()

@objc func method(_ notification: Notification?) {
invocations.record(notification)
}
}

sut.ignoreAddObserver = false

let observer = Observer()
let object = NSObject()
sut.addObserver(observer, selector: #selector(observer.method(_:)), name: notificationName, object: object)

// -- Act --
sut.post(Notification(name: notificationName, object: object))

// -- Assert --
XCTAssertEqual(observer.invocations.count, 1)
let invocation = try? XCTUnwrap(observer.invocations.invocations.first)
XCTAssertEqual(invocation?.name, notificationName)
XCTAssertIdentical(invocation?.object as? NSObject, object)
}

// MARK: - Helpers

@objc private func dummyMethod() {
// Dummy selector for testing purposes
}
}
Loading
Loading