Skip to content

Commit 36c8299

Browse files
committed
Adds global Hooks and implements error handling hook.
1 parent 7f2bdaa commit 36c8299

File tree

7 files changed

+117
-17
lines changed

7 files changed

+117
-17
lines changed

Rx.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,9 @@
656656
C822BAD01DB424EC00F98810 /* Reactive+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C822BACD1DB424EC00F98810 /* Reactive+Tests.swift */; };
657657
C82A336B1E2C3343003A6044 /* PerformanceTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E8BA701E2C18AE00A4AC2C /* PerformanceTools.swift */; };
658658
C82A336D1E2C3344003A6044 /* PerformanceTools.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E8BA701E2C18AE00A4AC2C /* PerformanceTools.swift */; };
659+
C82FF0EF1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82FF0EE1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift */; };
660+
C82FF0F01F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82FF0EE1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift */; };
661+
C82FF0F11F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C82FF0EE1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift */; };
659662
C8323A8E1E33FD5200CC0C7F /* Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8323A8D1E33FD5200CC0C7F /* Resources.swift */; };
660663
C8323A8F1E33FD5200CC0C7F /* Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8323A8D1E33FD5200CC0C7F /* Resources.swift */; };
661664
C8323A901E33FD5200CC0C7F /* Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8323A8D1E33FD5200CC0C7F /* Resources.swift */; };
@@ -1938,6 +1941,7 @@
19381941
C820AA111EB5145200D431BC /* Observable+DelayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+DelayTests.swift"; sourceTree = "<group>"; };
19391942
C822BAC51DB4048F00F98810 /* Event+Test.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Event+Test.swift"; sourceTree = "<group>"; };
19401943
C822BACD1DB424EC00F98810 /* Reactive+Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+Tests.swift"; sourceTree = "<group>"; };
1944+
C82FF0EE1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObservableType+SubscriptionTests.swift"; sourceTree = "<group>"; };
19411945
C8323A8D1E33FD5200CC0C7F /* Resources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Resources.swift; sourceTree = "<group>"; };
19421946
C834F6C11DB394E100C29244 /* Observable+BlockingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+BlockingTest.swift"; sourceTree = "<group>"; };
19431947
C834F6C51DB3950600C29244 /* NSControl+RxTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSControl+RxTests.swift"; sourceTree = "<group>"; };
@@ -2830,6 +2834,7 @@
28302834
C801DE391F6EAD48008DB060 /* MaybeTest.swift */,
28312835
C801DE3D1F6EAD57008DB060 /* CompletableTest.swift */,
28322836
C801DE491F6EBB84008DB060 /* Observable+PrimitiveSequenceTest.swift */,
2837+
C82FF0EE1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift */,
28332838
);
28342839
path = RxSwiftTests;
28352840
sourceTree = "<group>";
@@ -4377,6 +4382,7 @@
43774382
C8A9B6F41DAD752200C9B027 /* Observable+BindTests.swift in Sources */,
43784383
271A97441CFC9F7B00D64125 /* UIViewController+RxTests.swift in Sources */,
43794384
C83509631C38706E0027C24C /* SubjectConcurrencyTest.swift in Sources */,
4385+
C82FF0EF1F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift in Sources */,
43804386
C820A9721EB4F84000D431BC /* Observable+OptionalTests.swift in Sources */,
43814387
84E4D3961C9B011000ADFDC9 /* UISearchController+RxTests.swift in Sources */,
43824388
C8C4F15F1DE9CC5B00003FA7 /* UISwitch+RxTests.swift in Sources */,
@@ -4490,6 +4496,7 @@
44904496
C8C4F17A1DE9DF0200003FA7 /* UIBarButtonItem+RxTests.swift in Sources */,
44914497
C8350A151C38756A0027C24C /* ObserverTests.swift in Sources */,
44924498
1AF67DA71CED430100C310FA /* ReplaySubjectTest.swift in Sources */,
4499+
C82FF0F01F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift in Sources */,
44934500
C820A9531EB4ECC000D431BC /* Observable+ToArrayTests.swift in Sources */,
44944501
C81B6AAB1DB2C15C0047CF86 /* Platform.Darwin.swift in Sources */,
44954502
C801DE3B1F6EAD48008DB060 /* MaybeTest.swift in Sources */,
@@ -4686,6 +4693,7 @@
46864693
C8350A001C38755E0027C24C /* Observable+Tests.swift in Sources */,
46874694
C83509E91C3875580027C24C /* TestConnectableObservable.swift in Sources */,
46884695
C820A97C1EB4FA0800D431BC /* Observable+RangeTests.swift in Sources */,
4696+
C82FF0F11F93DD2E00BDB34D /* ObservableType+SubscriptionTests.swift in Sources */,
46894697
C83509DF1C38754F0027C24C /* TestVirtualScheduler.swift in Sources */,
46904698
C820A9DC1EB50CAA00D431BC /* Observable+DoOnTests.swift in Sources */,
46914699
C83509CD1C3875230027C24C /* NotificationCenterTests.swift in Sources */,

RxSwift/ObservableType+Extensions.swift

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
77
//
88

9+
#if DEBUG
10+
import Foundation
11+
#endif
12+
913
extension ObservableType {
1014
/**
1115
Subscribes an event handler to an observable sequence.
@@ -34,9 +38,7 @@ extension ObservableType {
3438
*/
3539
public func subscribe(onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
3640
-> Disposable {
37-
3841
#if DEBUG
39-
4042
let disposable: Disposable
4143

4244
if let disposed = onDisposed {
@@ -46,23 +48,24 @@ extension ObservableType {
4648
disposable = Disposables.create()
4749
}
4850

49-
let _synchronizationTracker = SynchronizationTracker()
50-
51-
let observer = AnonymousObserver<E> { e in
52-
53-
_synchronizationTracker.register(synchronizationErrorMessage: .default)
54-
defer { _synchronizationTracker.unregister() }
51+
let synchronizationTracker = SynchronizationTracker()
52+
53+
let callStack = Thread.callStackSymbols
54+
55+
let observer = AnonymousObserver<E> { event in
5556

57+
synchronizationTracker.register(synchronizationErrorMessage: .default)
58+
defer { synchronizationTracker.unregister() }
5659

57-
switch e {
60+
switch event {
5861
case .next(let value):
5962
onNext?(value)
60-
case .error(let e):
63+
case .error(let error):
6164
if let onError = onError {
62-
onError(e)
65+
onError(error)
6366
}
6467
else {
65-
print("Received unhandled error: \(e)")
68+
Hooks.defaultErrorHandler(callStack, error)
6669
}
6770
disposable.dispose()
6871
case .completed:
@@ -75,7 +78,6 @@ extension ObservableType {
7578
disposable
7679
)
7780
#else
78-
7981
let disposable: Disposable
8082

8183
if let disposed = onDisposed {
@@ -85,12 +87,17 @@ extension ObservableType {
8587
disposable = Disposables.create()
8688
}
8789

88-
let observer = AnonymousObserver<E> { e in
89-
switch e {
90+
let observer = AnonymousObserver<E> { event in
91+
switch event {
9092
case .next(let value):
9193
onNext?(value)
92-
case .error(let e):
93-
onError?(e)
94+
case .error(let error):
95+
if let onError = onError {
96+
onError(error)
97+
}
98+
else {
99+
Hooks.defaultErrorHandler([], error)
100+
}
94101
disposable.dispose()
95102
case .completed:
96103
onCompleted?()
@@ -106,3 +113,29 @@ extension ObservableType {
106113
}
107114
}
108115

116+
import class Foundation.NSRecursiveLock
117+
118+
extension Hooks {
119+
public typealias DefaultErrorHandler = (_ subscriptionCallStack: [String], _ error: Error) -> ()
120+
121+
fileprivate static let _lock = RecursiveLock()
122+
fileprivate static var _defaultErrorHandler: DefaultErrorHandler = { subscriptionCallStack, error in
123+
#if DEBUG
124+
let serializedCallStack = subscriptionCallStack.joined(separator: "\n")
125+
print("Unhandled error happened: \(error)\n subscription called from:\n\(serializedCallStack)")
126+
#endif
127+
}
128+
129+
/// Error handler called in case onError handler wasn't provided.
130+
public static var defaultErrorHandler: DefaultErrorHandler {
131+
get {
132+
_lock.lock(); defer { _lock.unlock() }
133+
return _defaultErrorHandler
134+
}
135+
set {
136+
_lock.lock(); defer { _lock.unlock() }
137+
_defaultErrorHandler = newValue
138+
}
139+
}
140+
}
141+

RxSwift/Rx.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,8 @@ func decrementChecked(_ i: inout Int) throws -> Int {
132132
}
133133

134134
#endif
135+
136+
/// RxSwift global hooks
137+
public enum Hooks {
138+
139+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../Tests/RxSwiftTests/ObservableType+SubscriptionTests.swift

Sources/AllTestz/main.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,18 @@ final class ObservableZipTest_ : ObservableZipTest, RxTestCase {
779779
] }
780780
}
781781

782+
final class ObservableSubscriptionTest_ : ObservableSubscriptionTest, RxTestCase {
783+
#if os(macOS)
784+
required override init() {
785+
super.init()
786+
}
787+
#endif
788+
789+
static var allTests: [(String, (ObservableSubscriptionTest_) -> () -> ())] { return [
790+
("testDefaultErrorHandler", ObservableSubscriptionTest.testDefaultErrorHandler),
791+
] }
792+
}
793+
782794
final class ObservableSkipUntilTest_ : ObservableSkipUntilTest, RxTestCase {
783795
#if os(macOS)
784796
required override init() {
@@ -1957,6 +1969,7 @@ func XCTMain(_ tests: [() -> ()]) {
19571969
testCase(SharedSequenceOperatorTests_.allTests),
19581970
testCase(SingleTest_.allTests),
19591971
testCase(ObservableZipTest_.allTests),
1972+
testCase(ObservableSubscriptionTest_.allTests),
19601973
testCase(ObservableSkipUntilTest_.allTests),
19611974
testCase(ObservableDefaultIfEmptyTest_.allTests),
19621975
testCase(ObservableFilterTest_.allTests),
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// ObservableType+SubscriptionTests.swift
3+
// Tests
4+
//
5+
// Created by Krunoslav Zaher on 10/15/17.
6+
// Copyright © 2017 Krunoslav Zaher. All rights reserved.
7+
//
8+
9+
import XCTest
10+
import RxSwift
11+
import RxTest
12+
13+
class ObservableSubscriptionTest : RxTest {
14+
15+
}
16+
17+
extension ObservableSubscriptionTest {
18+
func testDefaultErrorHandler() {
19+
var loggedErrors = [TestError]()
20+
21+
_ = Observable<Int>.error(testError).subscribe()
22+
XCTAssertEqual(loggedErrors, [])
23+
24+
let originalErrorHandler = Hooks.defaultErrorHandler
25+
26+
Hooks.defaultErrorHandler = { _, error in
27+
loggedErrors.append(error as! TestError)
28+
}
29+
30+
_ = Observable<Int>.error(testError).subscribe()
31+
XCTAssertEqual(loggedErrors, [testError])
32+
33+
Hooks.defaultErrorHandler = originalErrorHandler
34+
35+
_ = Observable<Int>.error(testError).subscribe()
36+
XCTAssertEqual(loggedErrors, [testError])
37+
}
38+
}
39+

Tests/RxTest.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ extension RxTest {
8787
}
8888

8989
func setUpActions(){
90+
_ = Hooks.defaultErrorHandler // lazy load resource so resource count matches
9091
#if TRACE_RESOURCES
9192
self.startResourceCount = Resources.total
9293
//registerMallocHooks()

0 commit comments

Comments
 (0)