1) Subclass SentryFileManager to modify its paths
The issue here appears to be SentryFileManager
's hard coded path properties in init(error:)
:
NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
self.sentryPath = [cachePath stringByAppendingPathComponent:@"io.sentry"];
...
self.breadcrumbsPath = [self.sentryPath stringByAppendingPathComponent:@"breadcrumbs"];
...
self.eventsPath = [self.sentryPath stringByAppendingPathComponent:@"events"];
Unfortunately, they are all private, so subclassing SentryFileManager
still won't give us access to them. Although I wouldn't recommended it, we ended up setting them using key/value coding to circumvent the compiler's access control.
Remember to create the directories if they don't exist.
There is nothing exiting going on in this subclass. It simply overwrites the protected properties with unique, encapsulated URLs for the provided domain name.
class DomainFileManager: SentryFileManager {
private enum Component {
static let sentry = "io.sentry"
static let breadcrumbs = "breadcrumbs"
static let events = "events"
}
private enum Key {
static let sentryPath = "sentryPath"
static let breadcrumbsPath = "breadcrumbsPath"
static let eventsPath = "eventsPath"
}
private static var cacheURL: URL {
return FileManager.default.urls(
for: .cachesDirectory,
in: .userDomainMask
)[0]
}
private let domainURL: URL
private var sentryURL: URL {
return domainURL.appendingPathComponent(Component.sentry)
}
private var breadcrumbsURL: URL {
return sentryURL.appendingPathComponent(Component.breadcrumbs)
}
private var eventsURL: URL {
return sentryURL.appendingPathComponent(Component.events)
}
init(domain: String, error: ()) throws {
domainURL = DomainFileManager.cacheURL.appendingPathComponent(domain)
try super.init(error: error)
setValue(sentryURL.path, forKey: Key.sentryPath)
setValue(breadcrumbsURL.path, forKey: Key.breadcrumbsPath)
setValue(eventsURL.path, forKey: Key.eventsPath)
for url in [sentryURL, breadcrumbsURL, eventsURL] {
if !FileManager.default.fileExists(atPath: url.path) {
try DomainFileManager.createDirectory(atPath: url.path)
}
}
}
}
As you can see, subclassing isn't strictly necessary, but other objects really shouldn't start messing with the SentryFileManager
s private properties. Neither should subclasses, but they "really shouldn't" a bit less.
2) Update the clients breadcrumb store
Then we define a new BreadcrumbStore
for the external device's Sentry client using our subclass.
let fileManager = try DomainFileManager(domain: "domain", error: ())
client.breadcrumbs = BreadcrumbStore(fileManager: fileManager)
So, thats probably all, isn't it? Turns out it isn't.
3) Update the clients file manager
It turns out that the client itself contains a private file manager that causes trouble. Suddenly the breadcrumbs are stored at out encapsulated paths, but events are stored using the default SentryFileManager
.
Once again we have to dive into the grit and circumvent the access control using key/value coding.
let fileManager = try DomainFileManager(domain: "domain", error: ())
client.breadcrumbs = BreadcrumbStore(fileManager: fileManager)
client.setValue(fileManager, forKey: "fileManager")
And thats all folks!