Skip to content

Commit 97797b4

Browse files
authored
feat: track network connectivity changes with breadcrumbs (#3232)
1 parent 51307b7 commit 97797b4

17 files changed

+262
-133
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Features
6+
7+
- Record changes to network connectivity in breadcrumbs (#3232)
8+
39
## 8.12.0
410

511
### Fixes

Sentry.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,7 @@
15951595
84AF45A529A7FFA500FBB177 /* SentryProfiledTracerConcurrency.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfiledTracerConcurrency.mm; sourceTree = "<group>"; };
15961596
84B7FA3B29B2866200AD93B1 /* SentryTestUtils-ObjC-BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryTestUtils-ObjC-BridgingHeader.h"; sourceTree = "<group>"; };
15971597
84B7FA4729B2995A00AD93B1 /* DeploymentTargets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DeploymentTargets.xcconfig; sourceTree = "<group>"; };
1598+
84C404B02ABA9F9C007F69C5 /* SentryReachability+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryReachability+Private.h"; sourceTree = "<group>"; };
15981599
84C47B2B2A09239100DAEB8A /* .codecov.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .codecov.yml; sourceTree = "<group>"; };
15991600
84E4F5692914F020004C7358 /* Brewfile */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = text; path = Brewfile; sourceTree = "<group>"; tabWidth = 2; };
16001601
84F993C32A62A74000EC0190 /* SentryCurrentDateProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCurrentDateProvider.m; sourceTree = "<group>"; };
@@ -1888,6 +1889,7 @@
18881889
84AC61D129F7541E009EEF61 /* SentryDispatchSourceWrapper.m */,
18891890
0AAE202028ED9BCC00D0CD80 /* SentryReachability.h */,
18901891
0AAE201D28ED9B9400D0CD80 /* SentryReachability.m */,
1892+
84C404B02ABA9F9C007F69C5 /* SentryReachability+Private.h */,
18911893
);
18921894
name = Networking;
18931895
sourceTree = "<group>";

Sources/Sentry/SentryBreadcrumbTracker.m

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#import "SentryDependencyContainer.h"
77
#import "SentryHub.h"
88
#import "SentryLog.h"
9+
#import "SentryReachability.h"
910
#import "SentryScope.h"
1011
#import "SentrySwift.h"
1112
#import "SentrySwizzle.h"
@@ -15,7 +16,7 @@
1516
# import <UIKit/UIKit.h>
1617
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
1718
# import <Cocoa/Cocoa.h>
18-
#endif
19+
#endif // !TARGET_OS_WATCH
1920

2021
NS_ASSUME_NONNULL_BEGIN
2122

@@ -24,23 +25,31 @@
2425

2526
@interface
2627
SentryBreadcrumbTracker ()
28+
#if !TARGET_OS_WATCH
29+
<SentryReachabilityObserver>
30+
#endif // !TARGET_OS_WATCH
2731

2832
@property (nonatomic, weak) id<SentryBreadcrumbDelegate> delegate;
2933

3034
@end
3135

3236
@implementation SentryBreadcrumbTracker
3337

34-
- (instancetype)init
38+
#if !TARGET_OS_WATCH
39+
- (void)dealloc
3540
{
36-
return [super init];
41+
[SentryDependencyContainer.sharedInstance.reachability removeObserver:self];
3742
}
43+
#endif // !TARGET_OS_WATCH
3844

3945
- (void)startWithDelegate:(id<SentryBreadcrumbDelegate>)delegate
4046
{
4147
_delegate = delegate;
4248
[self addEnabledCrumb];
4349
[self trackApplicationUIKitNotifications];
50+
#if !TARGET_OS_WATCH
51+
[self trackNetworkConnectivityChanges];
52+
#endif // !TARGET_OS_WATCH
4453
}
4554

4655
- (void)startSwizzle
@@ -56,7 +65,7 @@ - (void)stop
5665
#if SENTRY_HAS_UIKIT
5766
[SentryDependencyContainer.sharedInstance.swizzleWrapper
5867
removeSwizzleSendActionForKey:SentryBreadcrumbTrackerSwizzleSendAction];
59-
#endif
68+
#endif // SENTRY_HAS_UIKIT
6069
_delegate = nil;
6170
}
6271

@@ -70,10 +79,10 @@ - (void)trackApplicationUIKitNotifications
7079
// Will resign Active notification is the nearest one to
7180
// UIApplicationDidEnterBackgroundNotification
7281
NSNotificationName backgroundNotificationName = NSApplicationWillResignActiveNotification;
73-
#else
82+
#else // TARGET_OS_WATCH
7483
SENTRY_LOG_DEBUG(@"NO UIKit, OSX and Catalyst -> [SentryBreadcrumbTracker "
7584
@"trackApplicationUIKitNotifications] does nothing.");
76-
#endif
85+
#endif // !TARGET_OS_WATCH
7786

7887
// not available for macOS
7988
#if SENTRY_HAS_UIKIT
@@ -90,9 +99,9 @@ - (void)trackApplicationUIKitNotifications
9099
crumb.message = @"Low memory";
91100
[self.delegate addBreadcrumb:crumb];
92101
}];
93-
#endif
102+
#endif // SENTRY_HAS_UIKIT
94103

95-
#if SENTRY_HAS_UIKIT || TARGET_OS_OSX || TARGET_OS_MACCATALYST
104+
#if !TARGET_OS_WATCH
96105
[NSNotificationCenter.defaultCenter addObserverForName:backgroundNotificationName
97106
object:nil
98107
queue:nil
@@ -114,9 +123,25 @@ - (void)trackApplicationUIKitNotifications
114123
withDataKey:@"state"
115124
withDataValue:@"foreground"];
116125
}];
117-
#endif
126+
#endif // !TARGET_OS_WATCH
118127
}
119128

129+
#if !TARGET_OS_WATCH
130+
- (void)trackNetworkConnectivityChanges
131+
{
132+
[SentryDependencyContainer.sharedInstance.reachability
133+
addObserver:self
134+
withCallback:^(BOOL connected, NSString *_Nonnull typeDescription) {
135+
SentryBreadcrumb *crumb =
136+
[[SentryBreadcrumb alloc] initWithLevel:kSentryLevelInfo
137+
category:@"device.connectivity"];
138+
crumb.type = @"connectivity";
139+
crumb.data = [NSDictionary dictionaryWithObject:typeDescription forKey:@"connectivity"];
140+
[self.delegate addBreadcrumb:crumb];
141+
}];
142+
}
143+
#endif // !TARGET_OS_WATCH
144+
120145
- (void)addBreadcrumbWithType:(NSString *)type
121146
withCategory:(NSString *)category
122147
withLevel:(SentryLevel)level
@@ -155,7 +180,7 @@ + (BOOL)avoidSender:(id)sender forTarget:(id)target action:(NSString *)action
155180
}
156181
return NO;
157182
}
158-
#endif
183+
#endif // SENTRY_HAS_UIKIT
159184

160185
- (void)swizzleSendAction
161186
{
@@ -183,9 +208,9 @@ - (void)swizzleSendAction
183208
}
184209
forKey:SentryBreadcrumbTrackerSwizzleSendAction];
185210

186-
#else
211+
#else // !SENTRY_HAS_UIKIT
187212
SENTRY_LOG_DEBUG(@"NO UIKit -> [SentryBreadcrumbTracker swizzleSendAction] does nothing.");
188-
#endif
213+
#endif // SENTRY_HAS_UIKIT
189214
}
190215

191216
- (void)swizzleViewDidAppear
@@ -223,9 +248,9 @@ - (void)swizzleViewDidAppear
223248
}),
224249
mode, swizzleViewDidAppearKey);
225250
# pragma clang diagnostic pop
226-
#else
251+
#else // !SENTRY_HAS_UIKIT
227252
SENTRY_LOG_DEBUG(@"NO UIKit -> [SentryBreadcrumbTracker swizzleViewDidAppear] does nothing.");
228-
#endif
253+
#endif // SENTRY_HAS_UIKIT
229254
}
230255

231256
#if SENTRY_HAS_UIKIT
@@ -287,7 +312,7 @@ + (NSDictionary *)fetchInfoAboutViewController:(UIViewController *)controller
287312

288313
return info;
289314
}
290-
#endif
315+
#endif // SENTRY_HAS_UIKIT
291316

292317
@end
293318

Sources/Sentry/SentryDependencyContainer.m

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
# import "SentryUIDeviceWrapper.h"
3636
#endif // TARGET_OS_IOS
3737

38+
#if !TARGET_OS_WATCH
39+
# import "SentryReachability.h"
40+
#endif // !TARGET_OS_WATCH
41+
3842
@implementation SentryDependencyContainer
3943

4044
static SentryDependencyContainer *instance;
@@ -368,4 +372,18 @@ - (SentryMXManager *)metricKitManager
368372

369373
#endif // SENTRY_HAS_METRIC_KIT
370374

375+
#if !TARGET_OS_WATCH
376+
- (SentryReachability *)reachability
377+
{
378+
if (_reachability == nil) {
379+
@synchronized(sentryDependencyContainerLock) {
380+
if (_reachability == nil) {
381+
_reachability = [[SentryReachability alloc] init];
382+
}
383+
}
384+
}
385+
return _reachability;
386+
}
387+
#endif // !TARGET_OS_WATCH
388+
371389
@end

Sources/Sentry/SentryHttpTransport.m

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
@interface
3030
SentryHttpTransport ()
31+
#if !TARGET_OS_WATCH
32+
<SentryReachabilityObserver>
33+
#endif // !TARGET_OS_WATCH
3134

3235
@property (nonatomic, strong) SentryFileManager *fileManager;
3336
@property (nonatomic, strong) id<SentryRequestManager> requestManager;
@@ -37,9 +40,6 @@
3740
@property (nonatomic, strong) SentryEnvelopeRateLimit *envelopeRateLimit;
3841
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueue;
3942
@property (nonatomic, strong) dispatch_group_t dispatchGroup;
40-
#if !TARGET_OS_WATCH
41-
@property (nonatomic, strong) SentryReachability *reachability;
42-
#endif // !TARGET_OS_WATCH
4343

4444
#if TEST || TESTCI
4545
@property (nullable, nonatomic, strong) void (^startFlushCallback)(void);
@@ -73,9 +73,6 @@ - (id)initWithOptions:(SentryOptions *)options
7373
rateLimits:(id<SentryRateLimits>)rateLimits
7474
envelopeRateLimit:(SentryEnvelopeRateLimit *)envelopeRateLimit
7575
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
76-
#if !TARGET_OS_WATCH
77-
reachability:(SentryReachability *)reachability
78-
#endif // !TARGET_OS_WATCH
7976
{
8077
if (self = [super init]) {
8178
self.options = options;
@@ -95,33 +92,33 @@ - (id)initWithOptions:(SentryOptions *)options
9592
[self sendAllCachedEnvelopes];
9693

9794
#if !TARGET_OS_WATCH
98-
self.reachability = reachability;
9995
__weak SentryHttpTransport *weakSelf = self;
100-
[self.reachability monitorURL:[NSURL URLWithString:@"https://sentry.io"]
101-
usingCallback:^(BOOL connected, NSString *_Nonnull typeDescription) {
102-
if (weakSelf == nil) {
103-
SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything.");
104-
return;
105-
}
106-
107-
if (connected) {
108-
SENTRY_LOG_DEBUG(@"Internet connection is back.");
109-
[weakSelf sendAllCachedEnvelopes];
110-
} else {
111-
SENTRY_LOG_DEBUG(@"Lost internet connection.");
112-
}
113-
}];
114-
#endif
96+
[SentryDependencyContainer.sharedInstance.reachability
97+
addObserver:self
98+
withCallback:^(BOOL connected, NSString *_Nonnull typeDescription) {
99+
if (weakSelf == nil) {
100+
SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything.");
101+
return;
102+
}
103+
104+
if (connected) {
105+
SENTRY_LOG_DEBUG(@"Internet connection is back.");
106+
[weakSelf sendAllCachedEnvelopes];
107+
} else {
108+
SENTRY_LOG_DEBUG(@"Lost internet connection.");
109+
}
110+
}];
111+
#endif // !TARGET_OS_WATCH
115112
}
116113
return self;
117114
}
118115

116+
#if !TARGET_OS_WATCH
119117
- (void)dealloc
120118
{
121-
#if !TARGET_OS_WATCH
122-
[self.reachability stopMonitoring];
123-
#endif
119+
[SentryDependencyContainer.sharedInstance.reachability removeObserver:self];
124120
}
121+
#endif // !TARGET_OS_WATCH
125122

126123
- (void)sendEnvelope:(SentryEnvelope *)envelope
127124
{
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#import "SentryReachability.h"
2+
3+
#if !TARGET_OS_WATCH
4+
5+
void SentryConnectivityCallback(__unused SCNetworkReachabilityRef target,
6+
SCNetworkReachabilityFlags flags, __unused void *info);
7+
8+
@interface
9+
SentryReachability ()
10+
11+
@property SCNetworkReachabilityRef sentry_reachability_ref;
12+
13+
@end
14+
15+
#endif // !TARGET_OS_WATCH

0 commit comments

Comments
 (0)