Skip to content

Commit

Permalink
fix: Crash for Custom ViewController init on iOS 15 (#1361)
Browse files Browse the repository at this point in the history
Swizzle all custom ViewControllers. First, fetch all subclasses of ViewController and then swizzle them. As there is no straightforward way to get all sub-classes in Objective-C, the code first retrieves all classes in the runtime, iterates over all classes, and checks for every class if UIViewController is a parent. Cause loading all classes can take a few milliseconds, do this on a background thread, which should be fine because the SDK swizzles the root view controller and its children.
Previously, the code intercepted the ViewController initializers with swizzling to swizzle the lifecycle methods. This approach led to ViewControllers crashing when using a convenience initializer, see GH-1355. The error occurred because our swizzling logic adds the method to swizzle if the class doesn't implement it. It seems like adding an extra initializer causes problems with the rules for initialization in Swift, see https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID216.

Fixes GH-1355
  • Loading branch information
philipphofmann committed Oct 7, 2021
1 parent 74d66ff commit 75e7281
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 108 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

- fix: Crash for Custom ViewController init on iOS 15 (#1361)

## 7.4.2

- fix: Crash When Observing Span Finished (#1360)
Expand Down
24 changes: 18 additions & 6 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,6 @@
7BC8523724588115005A70F0 /* SentryRateLimitCategory.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BC8523624588115005A70F0 /* SentryRateLimitCategory.h */; };
7BC852392458830A005A70F0 /* SentryEnvelopeItemType.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BC852382458830A005A70F0 /* SentryEnvelopeItemType.h */; settings = {ATTRIBUTES = (Public, ); }; };
7BC8523B2458849E005A70F0 /* SentryRateLimitCategoryMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC8523A2458849E005A70F0 /* SentryRateLimitCategoryMapperTests.swift */; };
7BC9CD4226A99E670047518E /* SentryUIViewControllerSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BC9CD4126A99E670047518E /* SentryUIViewControllerSwizzlingTests.m */; };
7BCFBD672681C95000BC27D8 /* SentryScopeObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BCFBD662681C95000BC27D8 /* SentryScopeObserver.h */; };
7BCFBD6D2681D0A900BC27D8 /* SentryCrashScopeObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BCFBD6C2681D0A900BC27D8 /* SentryCrashScopeObserver.h */; };
7BCFBD6F2681D0EE00BC27D8 /* SentryCrashScopeObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BCFBD6E2681D0EE00BC27D8 /* SentryCrashScopeObserver.m */; };
Expand Down Expand Up @@ -440,6 +439,10 @@
7BECF42826145CD900D9826E /* SentryMechanismMeta.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BECF42726145CD900D9826E /* SentryMechanismMeta.m */; };
7BECF432261463E600D9826E /* SentryMechanismMetaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BECF431261463E600D9826E /* SentryMechanismMetaTests.swift */; };
7BED3576266F7BFF00EAA70D /* TestSentryCrashAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BED3575266F7BFF00EAA70D /* TestSentryCrashAdapter.m */; };
7BEF4951270C3F5700F8F30E /* SentrySubClassFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEF4950270C3F5700F8F30E /* SentrySubClassFinder.h */; };
7BEF4953270C3F7F00F8F30E /* SentrySubClassFinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BEF4952270C3F7F00F8F30E /* SentrySubClassFinder.m */; };
7BEF4955270C3FEC00F8F30E /* SentrySubClassFinderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEF4954270C3FEC00F8F30E /* SentrySubClassFinderTests.swift */; };
7BEF4957270C4B9D00F8F30E /* SentryUIViewControllerSwizzlingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEF4956270C4B9D00F8F30E /* SentryUIViewControllerSwizzlingTests.swift */; };
7BEFB044270B0F630025F808 /* SentryTracerObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BEFB043270B0F630025F808 /* SentryTracerObjCTests.m */; };
7BF536D124BDF3E7004FA6A2 /* SentryEnvelopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF536D024BDF3E7004FA6A2 /* SentryEnvelopeTests.swift */; };
7BF536D424BEF255004FA6A2 /* SentryAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF536D324BEF255004FA6A2 /* SentryAssertions.swift */; };
Expand Down Expand Up @@ -960,8 +963,7 @@
7BC8523624588115005A70F0 /* SentryRateLimitCategory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryRateLimitCategory.h; path = include/SentryRateLimitCategory.h; sourceTree = "<group>"; };
7BC852382458830A005A70F0 /* SentryEnvelopeItemType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryEnvelopeItemType.h; path = Public/SentryEnvelopeItemType.h; sourceTree = "<group>"; };
7BC8523A2458849E005A70F0 /* SentryRateLimitCategoryMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryRateLimitCategoryMapperTests.swift; sourceTree = "<group>"; };
7BC9CD4126A99E670047518E /* SentryUIViewControllerSwizzlingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryUIViewControllerSwizzlingTests.m; sourceTree = "<group>"; };
7BC9CD4326A99F660047518E /* SentryUIViewControllerSwizziling+TestInit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryUIViewControllerSwizziling+TestInit.h"; sourceTree = "<group>"; };
7BC9CD4326A99F660047518E /* SentryUIViewControllerSwizziling+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryUIViewControllerSwizziling+Test.h"; sourceTree = "<group>"; };
7BCFBD662681C95000BC27D8 /* SentryScopeObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryScopeObserver.h; path = include/SentryScopeObserver.h; sourceTree = "<group>"; };
7BCFBD6C2681D0A900BC27D8 /* SentryCrashScopeObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryCrashScopeObserver.h; path = include/SentryCrashScopeObserver.h; sourceTree = "<group>"; };
7BCFBD6E2681D0EE00BC27D8 /* SentryCrashScopeObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCrashScopeObserver.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1004,6 +1006,10 @@
7BECF431261463E600D9826E /* SentryMechanismMetaTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryMechanismMetaTests.swift; sourceTree = "<group>"; };
7BED3574266F7BC600EAA70D /* TestSentryCrashAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestSentryCrashAdapter.h; sourceTree = "<group>"; };
7BED3575266F7BFF00EAA70D /* TestSentryCrashAdapter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestSentryCrashAdapter.m; sourceTree = "<group>"; };
7BEF4950270C3F5700F8F30E /* SentrySubClassFinder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentrySubClassFinder.h; sourceTree = "<group>"; };
7BEF4952270C3F7F00F8F30E /* SentrySubClassFinder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySubClassFinder.m; sourceTree = "<group>"; };
7BEF4954270C3FEC00F8F30E /* SentrySubClassFinderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySubClassFinderTests.swift; sourceTree = "<group>"; };
7BEF4956270C4B9D00F8F30E /* SentryUIViewControllerSwizzlingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIViewControllerSwizzlingTests.swift; sourceTree = "<group>"; };
7BEFB043270B0F630025F808 /* SentryTracerObjCTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTracerObjCTests.m; sourceTree = "<group>"; };
7BF536D024BDF3E7004FA6A2 /* SentryEnvelopeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelopeTests.swift; sourceTree = "<group>"; };
7BF536D324BEF255004FA6A2 /* SentryAssertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAssertions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1421,6 +1427,8 @@
7B98D7E725FB7BCD00C5A389 /* SentryAppState.m */,
7BD86EC4264A63F6005439DB /* SentrySysctl.h */,
7BD86EC6264A641D005439DB /* SentrySysctl.m */,
7BEF4950270C3F5700F8F30E /* SentrySubClassFinder.h */,
7BEF4952270C3F7F00F8F30E /* SentrySubClassFinder.m */,
);
name = Helper;
sourceTree = "<group>";
Expand Down Expand Up @@ -1874,8 +1882,8 @@
8EAE8E5D2681768000D6958B /* URLSessionTaskMock.m */,
7B965727268321CD00C66E25 /* SentryCrashScopeObserverTests.swift */,
7B6ADFCE26A02CAE0076C206 /* SentryCrashReportTests.swift */,
7BC9CD4126A99E670047518E /* SentryUIViewControllerSwizzlingTests.m */,
7BC9CD4326A99F660047518E /* SentryUIViewControllerSwizziling+TestInit.h */,
7BEF4956270C4B9D00F8F30E /* SentryUIViewControllerSwizzlingTests.swift */,
7BC9CD4326A99F660047518E /* SentryUIViewControllerSwizziling+Test.h */,
D8FFE50B2703DAAE00607131 /* SwizzlingCallTests.swift */,
);
path = Integrations;
Expand Down Expand Up @@ -1967,6 +1975,7 @@
69BEE6F62620729E006DF9DF /* UrlSessionDelegateSpy.swift */,
7BD86ECA264A6DB5005439DB /* TestSysctl.swift */,
7B16FD012654F86B008177D3 /* SentrySysctlTests.swift */,
7BEF4954270C3FEC00F8F30E /* SentrySubClassFinderTests.swift */,
);
path = Helper;
sourceTree = "<group>";
Expand Down Expand Up @@ -2245,6 +2254,7 @@
7B04A9AF24EAC02C00E710B1 /* SentryRetryAfterHeaderParser.h in Headers */,
7DC83100239826280043DD9A /* SentryIntegrationProtocol.h in Headers */,
7B98D7BC25FB607300C5A389 /* SentryOutOfMemoryTracker.h in Headers */,
7BEF4951270C3F5700F8F30E /* SentrySubClassFinder.h in Headers */,
7BA61CB9247BC57B00C130A8 /* SentryCrashDefaultBinaryImageProvider.h in Headers */,
8E4E7C7D25DAB287006AB9E2 /* SentryTracer.h in Headers */,
7BC8523724588115005A70F0 /* SentryRateLimitCategory.h in Headers */,
Expand Down Expand Up @@ -2407,6 +2417,7 @@
15360CD62432832400112302 /* SentryAutoSessionTrackingIntegration.m in Sources */,
7BE3C7692445C1A800A38442 /* SentryCurrentDate.m in Sources */,
63EED6C02237923600E02400 /* SentryOptions.m in Sources */,
7BEF4953270C3F7F00F8F30E /* SentrySubClassFinder.m in Sources */,
63AA769E1EB9C57A00D153DE /* SentryError.m in Sources */,
7B8713B026415B22006D6004 /* SentryAppStartTrackingIntegration.m in Sources */,
8E133FA225E72DEF00ABD0BF /* SentrySamplingContext.m in Sources */,
Expand Down Expand Up @@ -2548,6 +2559,7 @@
7BA61CC6247CFC5F00C130A8 /* SentryCrashDefaultBinaryImageProviderTests.swift in Sources */,
7BBC827925DFD7D7005F1ED8 /* SentryInAppLogicTests.swift in Sources */,
7BD7299A2463EA4A00EA3610 /* SentryDateUtilTests.swift in Sources */,
7BEF4957270C4B9D00F8F30E /* SentryUIViewControllerSwizzlingTests.swift in Sources */,
630436161EC0AD3100C4D3FA /* SentryNSDataCompressionTests.m in Sources */,
63FE722420DA66EC00CDBAE8 /* SentryCrashMonitor_NSException_Tests.m in Sources */,
63B819141EC352A7002FDF4C /* SentryInterfacesTests.m in Sources */,
Expand All @@ -2570,6 +2582,7 @@
63FE720D20DA66EC00CDBAE8 /* NSError+SimpleConstructor_Tests.m in Sources */,
69BEE6F72620729E006DF9DF /* UrlSessionDelegateSpy.swift in Sources */,
7B98D7EC25FB7C4900C5A389 /* SentryAppStateTests.swift in Sources */,
7BEF4955270C3FEC00F8F30E /* SentrySubClassFinderTests.swift in Sources */,
8EE017A126704CD500470616 /* SentryUIPerformanceTrackerTests.swift in Sources */,
7B5B94352657AD21002E474B /* SentryFramesTrackingIntegrationTests.swift in Sources */,
7BAF3DC8243DB90E008A5414 /* TestTransport.swift in Sources */,
Expand Down Expand Up @@ -2631,7 +2644,6 @@
A811D867248E2770008A41EA /* SentrySystemEventsBreadcrumbsTest.swift in Sources */,
7B82D54924E2A2D400EE670F /* SentryIdTests.swift in Sources */,
7BD47B4E268F0B470076A663 /* ClearTestState.swift in Sources */,
7BC9CD4226A99E670047518E /* SentryUIViewControllerSwizzlingTests.m in Sources */,
7B6D98ED24C703F8005502FA /* Async.swift in Sources */,
63FE722520DA66EC00CDBAE8 /* SentryCrashFileUtils_Tests.m in Sources */,
7BFC16BA2524D4AF00FF6266 /* SentryMessage+Equality.m in Sources */,
Expand Down
8 changes: 7 additions & 1 deletion Sources/Sentry/SentryPerformanceTrackingIntegration.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "SentryPerformanceTrackingIntegration.h"
#import "SentryDispatchQueueWrapper.h"
#import "SentryLog.h"
#import "SentryUIViewControllerSwizziling.h"

Expand All @@ -15,7 +16,12 @@ - (void)installWithOptions:(SentryOptions *)options
self.options = options;
if (options.enableAutoPerformanceTracking) {
#if SENTRY_HAS_UIKIT
[SentryUIViewControllerSwizziling startWithOptions:options];
dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_PRIORITY_HIGH, 0);
SentryDispatchQueueWrapper *dispatchQueue =
[[SentryDispatchQueueWrapper alloc] initWithName:"sentry-ui-view-controller-swizzling"
attributes:attributes];
[SentryUIViewControllerSwizziling startWithOptions:options dispatchQueue:dispatchQueue];
#else
[SentryLog logWithMessage:@"NO UIKit -> [SentryPerformanceTrackingIntegration "
@"start] does nothing."
Expand Down
15 changes: 15 additions & 0 deletions Sources/Sentry/SentrySubClassFinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SentrySubClassFinder : NSObject

/**
* Returns an array of subclasses of the parentClass. You shouldn't call this on the main thread as
* this uses objc_getClassList internally, which can take up to 60 ms to complete.
*/
+ (NSArray<Class> *)getSubclassesOf:(Class)parentClass;

@end

NS_ASSUME_NONNULL_END
38 changes: 38 additions & 0 deletions Sources/Sentry/SentrySubClassFinder.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#import "SentrySubClassFinder.h"
#import "SentryLog.h"
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@implementation SentrySubClassFinder

+ (NSArray<Class> *)getSubclassesOf:(Class)parentClass
{
int numClasses = objc_getClassList(NULL, 0);
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);

NSMutableArray<Class> *result = [NSMutableArray new];

if (numClasses <= 0) {
[SentryLog logWithMessage:@"No classes found when retrieving class list."
andLevel:kSentryLevelError];
return result;
}

for (NSInteger i = 0; i < numClasses; i++) {
Class superClass = classes[i];
do {
superClass = class_getSuperclass(superClass);
} while (superClass && superClass != parentClass);

if (superClass != nil) {
[result addObject:classes[i]];
}
}

free(classes);

return result;
}

@end
Loading

0 comments on commit 75e7281

Please sign in to comment.