Skip to content

Commit 5f54fc5

Browse files
committed
Add sentry private SPM
1 parent 32b4f2b commit 5f54fc5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+257
-229
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,11 @@ jobs:
309309

310310
# If the SentryAsyncSafeLog doesn't contain the SENTRY_ASYNC_SAFE_LOG_LEVEL_ERROR this fails.
311311
- name: Async Safe Log Level is Error
312-
run: grep -c "SENTRY_ASYNC_SAFE_LOG_LEVEL SENTRY_ASYNC_SAFE_LOG_LEVEL_ERROR" Sources/Sentry/SentryAsyncSafeLog.h
312+
run: grep -c "SENTRY_ASYNC_SAFE_LOG_LEVEL SENTRY_ASYNC_SAFE_LOG_LEVEL_ERROR" Sources/Sentry/_SentryPrivate/include/SentryAsyncSafeLog.h
313313

314314
- name: Set Async Safe Log Level to Debug
315315
run: |
316-
sed -i '' 's/#define SENTRY_ASYNC_SAFE_LOG_LEVEL SENTRY_ASYNC_SAFE_LOG_LEVEL_ERROR/#define SENTRY_ASYNC_SAFE_LOG_LEVEL SENTRY_ASYNC_SAFE_LOG_LEVEL_TRACE/' Sources/Sentry/SentryAsyncSafeLog.h
316+
sed -i '' 's/#define SENTRY_ASYNC_SAFE_LOG_LEVEL SENTRY_ASYNC_SAFE_LOG_LEVEL_ERROR/#define SENTRY_ASYNC_SAFE_LOG_LEVEL SENTRY_ASYNC_SAFE_LOG_LEVEL_TRACE/' Sources/Sentry/_SentryPrivate/include/SentryAsyncSafeLog.h
317317
shell: bash
318318

319319
- run: ./scripts/ci-select-xcode.sh 16.3

Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ let package = Package(
3535
],
3636
publicHeadersPath: "SentryInternal/"
3737
),
38-
.target(name: "SentryCoreSwift", path: "Sources/Swift/Core")
38+
.target(name: "SentryCoreSwift", path: "Sources/Swift/Core"),
39+
// The name of this package is _SentryPrivate so that it matches the imports already used in Swift code to access ObjC.
40+
.target(name: "_SentryPrivate", dependencies: ["SentryCoreSwift"], path: "Sources/Sentry/_SentryPrivate", cSettings: [.headerSearchPath("Public")])
3941
],
4042
cxxLanguageStandard: .cxx14
4143
)

Samples/iOS-SwiftUI/iOS-SwiftUI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ targets:
2828
- ../Shared/SampleAssets.xcassets
2929
- ../../Sources/Sentry/include/SentryTracer.h
3030
- ../../Sources/Sentry/include/SentryPerformanceTracker.h
31-
- ../../Sources/Sentry/Public/SentryProfilingConditionals.h
31+
- ../../Sources/Sentry/_SentryPrivate/Public/SentryProfilingConditionals.h
3232
dependencies:
3333
- target: Sentry/Sentry
3434
- target: Sentry/SentrySwiftUI

Sentry.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Pod::Spec.new do |s|
3838
"Sources/SentryCrash/**/*.{h,hpp,m,mm,c,cpp}", "Sources/Swift/**/*.{swift,h,hpp,m,mm,c,cpp}"
3939
sp.preserve_path = "Sources/Sentry/include/module.modulemap"
4040
sp.public_header_files =
41-
"Sources/Sentry/Public/*.h"
41+
"Sources/Sentry/Public/*.h", "Sources/Sentry/_SentryPrivate/Public/*.h"
4242

4343
sp.preserve_path = "Sources/Sentry/include/module.modulemap"
4444
sp.resource_bundles = { "Sentry" => "Sources/Resources/PrivacyInfo.xcprivacy" }
@@ -50,7 +50,7 @@ Pod::Spec.new do |s|
5050

5151
sp.preserve_path = "Sources/Sentry/include/module.modulemap"
5252
sp.public_header_files =
53-
"Sources/Sentry/Public/*.h", "Sources/Sentry/include/HybridPublic/*.h"
53+
"Sources/Sentry/Public/*.h", "Sources/Sentry/_SentryPrivate/Public/*.h", "Sources/Sentry/include/HybridPublic/*.h"
5454

5555
sp.preserve_path = "Sources/Sentry/include/module.modulemap"
5656
sp.resource_bundles = { "Sentry" => "Sources/Resources/PrivacyInfo.xcprivacy" }

Sentry.xcodeproj/project.pbxproj

Lines changed: 23 additions & 15 deletions
Large diffs are not rendered by default.

SentryTestUtils/SentryLogExtensions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
2-
@testable import Sentry
2+
@testable @_spi(Private) import Sentry
33

4-
extension SentryLog {
4+
public enum SentryLog {
55
public static func setTestDefaultLogLevel() {
66
SentryLogSwiftSupport.configure(true, diagnosticLevel: .debug)
77
}

Sources/Sentry/SentryFileManager.m

Lines changed: 1 addition & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "SentryFileManager.h"
2+
#import "FileUtils.h"
23
#import "SentryAppState.h"
34
#import "SentryDataCategoryMapper.h"
45
#import "SentryDateUtils.h"
@@ -22,51 +23,6 @@
2223

2324
#pragma mark - Helper Methods
2425

25-
BOOL
26-
isErrorPathTooLong(NSError *error)
27-
{
28-
NSError *underlyingError = NULL;
29-
if (@available(macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5, *)) {
30-
underlyingError = error.underlyingErrors.firstObject;
31-
}
32-
if (underlyingError == NULL) {
33-
id errorInUserInfo = [error.userInfo valueForKey:NSUnderlyingErrorKey];
34-
if (errorInUserInfo && [errorInUserInfo isKindOfClass:[NSError class]]) {
35-
underlyingError = errorInUserInfo;
36-
}
37-
}
38-
if (underlyingError == NULL) {
39-
underlyingError = error;
40-
}
41-
BOOL isEnameTooLong
42-
= underlyingError.domain == NSPOSIXErrorDomain && underlyingError.code == ENAMETOOLONG;
43-
// On older OS versions the error code is NSFileWriteUnknown
44-
// Reference: https://developer.apple.com/forums/thread/128927?answerId=631839022#631839022
45-
BOOL isUnknownError = underlyingError.domain == NSCocoaErrorDomain
46-
&& underlyingError.code == NSFileWriteUnknownError;
47-
48-
return isEnameTooLong || isUnknownError;
49-
}
50-
51-
BOOL
52-
createDirectoryIfNotExists(NSString *path, NSError **error)
53-
{
54-
BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:path
55-
withIntermediateDirectories:YES
56-
attributes:nil
57-
error:error];
58-
if (success) {
59-
return YES;
60-
}
61-
62-
if (isErrorPathTooLong(*error)) {
63-
SENTRY_LOG_FATAL(@"Failed to create directory, path is too long: %@", path);
64-
}
65-
*error = NSErrorFromSentryErrorWithUnderlyingError(kSentryErrorFileIO,
66-
[NSString stringWithFormat:@"Failed to create the directory at path %@.", path], *error);
67-
return NO;
68-
}
69-
7026
/**
7127
* @warning This is called from a `@synchronized` context in instance methods, but doesn't require
7228
* that when calling from other static functions. Make sure you pay attention to where this is used
@@ -634,116 +590,6 @@ - (BOOL)isDirectory:(NSString *)path
634590
return [NSFileManager.defaultManager fileExistsAtPath:path isDirectory:&isDir] && isDir;
635591
}
636592

637-
/**
638-
* @note This method must be statically accessible because it will be called during app launch,
639-
* before any instance of ``SentryFileManager`` exists, and so wouldn't be able to access this path
640-
* from an objc property on it like the other paths.
641-
*/
642-
NSString *_Nullable sentryStaticCachesPath(void)
643-
{
644-
static NSString *_Nullable sentryStaticCachesPath = nil;
645-
static dispatch_once_t onceToken;
646-
dispatch_once(&onceToken, ^{
647-
// We request the users cache directory from Foundation.
648-
// For iOS apps and macOS apps with sandboxing, this path will be scoped for the current
649-
// app. For macOS apps without sandboxing, this path is not scoped and will be shared
650-
// between all apps.
651-
NSString *_Nullable cachesDirectory
652-
= NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)
653-
.firstObject;
654-
if (cachesDirectory == nil) {
655-
SENTRY_LOG_WARN(@"No caches directory location reported.");
656-
return;
657-
}
658-
659-
// We need to ensure our own scoped directory so that this path is not shared between other
660-
// apps on the same system.
661-
NSString *_Nullable scopedCachesDirectory = sentryGetScopedCachesDirectory(cachesDirectory);
662-
if (!scopedCachesDirectory) {
663-
SENTRY_LOG_WARN(@"Failed to get scoped static caches directory.");
664-
return;
665-
}
666-
sentryStaticCachesPath = scopedCachesDirectory;
667-
SENTRY_LOG_DEBUG(@"Using static cache directory: %@", sentryStaticCachesPath);
668-
});
669-
return sentryStaticCachesPath;
670-
}
671-
672-
NSString *_Nullable sentryGetScopedCachesDirectory(NSString *cachesDirectory)
673-
{
674-
#if !TARGET_OS_OSX
675-
// iOS apps are always sandboxed, therefore we can just early-return with the provided caches
676-
// directory.
677-
return cachesDirectory;
678-
#else
679-
680-
// For macOS apps, we need to ensure our own sandbox so that this path is not shared between
681-
// all apps that ship the SDK.
682-
683-
// We can not use the SentryNSProcessInfoWrapper here because this method is called before
684-
// the SentryDependencyContainer is initialized.
685-
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
686-
687-
// Only apps running in a sandboxed environment have the `APP_SANDBOX_CONTAINER_ID` set as a
688-
// process environment variable. Reference implementation:
689-
// https://github.com/realm/realm-js/blob/a03127726939f08f608edbdb2341605938f25708/packages/realm/binding/apple/platform.mm#L58-L74
690-
BOOL isSandboxed = processInfo.environment[@"APP_SANDBOX_CONTAINER_ID"] != nil;
691-
692-
// The bundle identifier is used to create a unique cache directory for the app.
693-
// If the bundle identifier is not available, we use the name of the executable.
694-
// Note: `SentryCrash.getBundleName` is using `CFBundleName` to create a scoped directory.
695-
// That value can be absent, therefore we use a more stable approach here.
696-
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
697-
NSString *lastPathComponent = [[[NSBundle mainBundle] executablePath] lastPathComponent];
698-
699-
// Due to `NSProcessInfo` and `NSBundle` not being mockable in unit tests, we extract only the
700-
// logic to a separate function.
701-
return sentryBuildScopedCachesDirectoryPath(
702-
cachesDirectory, isSandboxed, bundleIdentifier, lastPathComponent);
703-
#endif
704-
}
705-
706-
NSString *_Nullable sentryBuildScopedCachesDirectoryPath(NSString *cachesDirectory,
707-
BOOL isSandboxed, NSString *_Nullable bundleIdentifier, NSString *_Nullable lastPathComponent)
708-
{
709-
// If the app is sandboxed, we can just use the provided caches directory.
710-
if (isSandboxed) {
711-
return cachesDirectory;
712-
}
713-
714-
// If the macOS app is not sandboxed, we need to manually create a scoped cache
715-
// directory. The cache path must be unique an stable over app launches, therefore we
716-
// can not use any changing identifier.
717-
SENTRY_LOG_DEBUG(
718-
@"App is not sandboxed, extending default cache directory with bundle identifier.");
719-
NSString *_Nullable identifier = bundleIdentifier;
720-
if (identifier == nil) {
721-
SENTRY_LOG_WARN(@"No bundle identifier found, using main bundle executable name.");
722-
identifier = lastPathComponent;
723-
} else if (identifier.length == 0) {
724-
SENTRY_LOG_WARN(@"Bundle identifier exists but is zero length, using main bundle "
725-
@"executable name.");
726-
identifier = lastPathComponent;
727-
}
728-
729-
// If neither the bundle identifier nor the executable name are available, we can't
730-
// create a unique and stable cache directory.
731-
// We do not fall back to any default path, because it could be shared with other apps
732-
// and cause leaks impacting other apps.
733-
if (identifier == nil) {
734-
SENTRY_LOG_ERROR(@"No bundle identifier found, cannot create cache directory.");
735-
return nil;
736-
}
737-
738-
// It's unlikely that the executable name will be zero length, but we'll cover this case anyways
739-
if (identifier.length == 0) {
740-
SENTRY_LOG_ERROR(@"Executable name was zero length.");
741-
return nil;
742-
}
743-
744-
return [cachesDirectory stringByAppendingPathComponent:identifier];
745-
}
746-
747593
NSString *_Nullable sentryStaticBasePath(void)
748594
{
749595
static NSString *_Nullable sentryStaticBasePath = nil;
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#import "FileUtils.h"
2+
#import "SentryError.h"
3+
#import "SentryLog.h"
4+
5+
BOOL
6+
isErrorPathTooLong(NSError *error)
7+
{
8+
NSError *underlyingError = NULL;
9+
if (@available(macOS 11.3, iOS 14.5, watchOS 7.4, tvOS 14.5, *)) {
10+
underlyingError = error.underlyingErrors.firstObject;
11+
}
12+
if (underlyingError == NULL) {
13+
id errorInUserInfo = [error.userInfo valueForKey:NSUnderlyingErrorKey];
14+
if (errorInUserInfo && [errorInUserInfo isKindOfClass:[NSError class]]) {
15+
underlyingError = errorInUserInfo;
16+
}
17+
}
18+
if (underlyingError == NULL) {
19+
underlyingError = error;
20+
}
21+
BOOL isEnameTooLong
22+
= underlyingError.domain == NSPOSIXErrorDomain && underlyingError.code == ENAMETOOLONG;
23+
// On older OS versions the error code is NSFileWriteUnknown
24+
// Reference: https://developer.apple.com/forums/thread/128927?answerId=631839022#631839022
25+
BOOL isUnknownError = underlyingError.domain == NSCocoaErrorDomain
26+
&& underlyingError.code == NSFileWriteUnknownError;
27+
28+
return isEnameTooLong || isUnknownError;
29+
}
30+
31+
BOOL
32+
createDirectoryIfNotExists(NSString *path, NSError **error)
33+
{
34+
BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:path
35+
withIntermediateDirectories:YES
36+
attributes:nil
37+
error:error];
38+
if (success) {
39+
return YES;
40+
}
41+
42+
if (isErrorPathTooLong(*error)) {
43+
SENTRY_LOG_FATAL(@"Failed to create directory, path is too long: %@", path);
44+
}
45+
*error = NSErrorFromSentryErrorWithUnderlyingError(kSentryErrorFileIO,
46+
[NSString stringWithFormat:@"Failed to create the directory at path %@.", path], *error);
47+
return NO;
48+
}
49+
50+
NSString *_Nullable sentryBuildScopedCachesDirectoryPath(NSString *cachesDirectory,
51+
BOOL isSandboxed, NSString *_Nullable bundleIdentifier, NSString *_Nullable lastPathComponent)
52+
{
53+
// If the app is sandboxed, we can just use the provided caches directory.
54+
if (isSandboxed) {
55+
return cachesDirectory;
56+
}
57+
58+
// If the macOS app is not sandboxed, we need to manually create a scoped cache
59+
// directory. The cache path must be unique an stable over app launches, therefore we
60+
// can not use any changing identifier.
61+
SENTRY_LOG_DEBUG(
62+
@"App is not sandboxed, extending default cache directory with bundle identifier.");
63+
NSString *_Nullable identifier = bundleIdentifier;
64+
if (identifier == nil) {
65+
SENTRY_LOG_WARN(@"No bundle identifier found, using main bundle executable name.");
66+
identifier = lastPathComponent;
67+
} else if (identifier.length == 0) {
68+
SENTRY_LOG_WARN(@"Bundle identifier exists but is zero length, using main bundle "
69+
@"executable name.");
70+
identifier = lastPathComponent;
71+
}
72+
73+
// If neither the bundle identifier nor the executable name are available, we can't
74+
// create a unique and stable cache directory.
75+
// We do not fall back to any default path, because it could be shared with other apps
76+
// and cause leaks impacting other apps.
77+
if (identifier == nil) {
78+
SENTRY_LOG_ERROR(@"No bundle identifier found, cannot create cache directory.");
79+
return nil;
80+
}
81+
82+
// It's unlikely that the executable name will be zero length, but we'll cover this case anyways
83+
if (identifier.length == 0) {
84+
SENTRY_LOG_ERROR(@"Executable name was zero length.");
85+
return nil;
86+
}
87+
88+
return [cachesDirectory stringByAppendingPathComponent:identifier];
89+
}
90+
91+
NSString *_Nullable sentryGetScopedCachesDirectory(NSString *cachesDirectory)
92+
{
93+
#if !TARGET_OS_OSX
94+
// iOS apps are always sandboxed, therefore we can just early-return with the provided caches
95+
// directory.
96+
return cachesDirectory;
97+
#else
98+
99+
// For macOS apps, we need to ensure our own sandbox so that this path is not shared between
100+
// all apps that ship the SDK.
101+
102+
// We can not use the SentryNSProcessInfoWrapper here because this method is called before
103+
// the SentryDependencyContainer is initialized.
104+
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
105+
106+
// Only apps running in a sandboxed environment have the `APP_SANDBOX_CONTAINER_ID` set as a
107+
// process environment variable. Reference implementation:
108+
// https://github.com/realm/realm-js/blob/a03127726939f08f608edbdb2341605938f25708/packages/realm/binding/apple/platform.mm#L58-L74
109+
BOOL isSandboxed = processInfo.environment[@"APP_SANDBOX_CONTAINER_ID"] != nil;
110+
111+
// The bundle identifier is used to create a unique cache directory for the app.
112+
// If the bundle identifier is not available, we use the name of the executable.
113+
// Note: `SentryCrash.getBundleName` is using `CFBundleName` to create a scoped directory.
114+
// That value can be absent, therefore we use a more stable approach here.
115+
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
116+
NSString *lastPathComponent = [[[NSBundle mainBundle] executablePath] lastPathComponent];
117+
118+
// Due to `NSProcessInfo` and `NSBundle` not being mockable in unit tests, we extract only the
119+
// logic to a separate function.
120+
return sentryBuildScopedCachesDirectoryPath(
121+
cachesDirectory, isSandboxed, bundleIdentifier, lastPathComponent);
122+
#endif
123+
}
124+
125+
/**
126+
* @note This method must be statically accessible because it will be called during app launch,
127+
* before any instance of ``SentryFileManager`` exists, and so wouldn't be able to access this path
128+
* from an objc property on it like the other paths.
129+
*/
130+
NSString *_Nullable sentryStaticCachesPath(void)
131+
{
132+
static NSString *_Nullable sentryStaticCachesPath = nil;
133+
static dispatch_once_t onceToken;
134+
dispatch_once(&onceToken, ^{
135+
// We request the users cache directory from Foundation.
136+
// For iOS apps and macOS apps with sandboxing, this path will be scoped for the current
137+
// app. For macOS apps without sandboxing, this path is not scoped and will be shared
138+
// between all apps.
139+
NSString *_Nullable cachesDirectory
140+
= NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)
141+
.firstObject;
142+
if (cachesDirectory == nil) {
143+
SENTRY_LOG_WARN(@"No caches directory location reported.");
144+
return;
145+
}
146+
147+
// We need to ensure our own scoped directory so that this path is not shared between other
148+
// apps on the same system.
149+
NSString *_Nullable scopedCachesDirectory = sentryGetScopedCachesDirectory(cachesDirectory);
150+
if (!scopedCachesDirectory) {
151+
SENTRY_LOG_WARN(@"Failed to get scoped static caches directory.");
152+
return;
153+
}
154+
sentryStaticCachesPath = scopedCachesDirectory;
155+
SENTRY_LOG_DEBUG(@"Using static cache directory: %@", sentryStaticCachesPath);
156+
});
157+
return sentryStaticCachesPath;
158+
}

0 commit comments

Comments
 (0)