profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/mikeabdullah/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Mike Abdullah mikeabdullah Mike Abdullah Development Ltd. UK http://mikeabdullah.net

karelia/ConnectionKit 500

FTP/SFTP/WebDAV etc. for Cocoa. Join the ConnectionKit mailing list for information and suggestions.

karelia/KSFileUtilities 195

NSURL & path additions

karelia/iMedia 129

The Karelia iMedia Browser, framework and application for browsing media on Mac OS X

karelia/KSHTMLWriter 77

Small set of classes to simplify XML/HTML generation in Cocoa apps.

karelia/BSManagedDocument 54

Brings UIManagedDocument's design to OS X

karelia/CurlHandle 51

Cocoa Class wrapping libcurl

karelia/KSExtensibleManagedObject 26

KSExtensibleManagedObject

karelia/DAVKit 25

A Cocoa framework for talking to WebDAV servers

karelia/KSError 12

Additions to NSError for easier creation of errors, and querying existing ones.

PullRequestReviewEvent

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration {     if ((self = [super init])) {         // Take a shallow copy of the configuration         _configuration = [configuration copy];+        BSGFileLocations *fileLocations = [BSGFileLocations currentWithSubdirectory:[_configuration.exclusiveSubdirectory copy]];+        if (fileLocations.usesExclusiveSubdirectory) {

I like it, nice 👍

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(v     [self.uploadQueue addOperation:operation]; } ++ (BOOL)synchronouslyUploadExclusiveReportsWithConfiguration:(BugsnagConfiguration *)configuration {++    BugsnagNotifier *notifier = [BugsnagNotifier new];+    NSFileManager *fm = NSFileManager.defaultManager;+    NSError *error = nil;++    NSString *container = [BSGFileLocations exclusiveDirectoryContainer];++    NSArray<NSString *> * contents = [fm contentsOfDirectoryAtPath:container error:&error];+    if (!contents) {+        // Log the error, except if it indicates that the directory is simply missing, since this can legitimately happen.+        if ([error.domain isEqual:NSCocoaErrorDomain] && error.code == NSFileReadNoSuchFileError) {+            return YES;+        }

❤️

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(v     [self.uploadQueue addOperation:operation]; } ++ (void)synchronouslyUploadAtomicReportsWithConfiguration:(BugsnagConfiguration *)configuration {++    BugsnagNotifier *notifier = [BugsnagNotifier new];+    NSFileManager *fm = NSFileManager.defaultManager;+    NSError *error = nil;++    NSString *container = [BSGFileLocations atomicDirectoryContainer];++    // If the parent directory does not exist, there is nothing to do.+    if (![fm fileExistsAtPath:container]) {+        return;+    }++    NSArray<NSString *> * contents = [fm contentsOfDirectoryAtPath:container error:&error];+    if (!contents) {+        bsg_log_err(@"failed to get contents of atomic container: %@", error);+        return;+    }++    // Enumerate subdirectories.+    for (NSString *item in contents) {+        NSString *fullItemPath = [container stringByAppendingPathComponent:item];+        BOOL isDirectory;+        if (![fm fileExistsAtPath:fullItemPath isDirectory:&isDirectory] || !isDirectory) {+            continue;+        }++        // Instantiate locations for this subdirectory.+        BSGFileLocations *locations = [[BSGFileLocations alloc] initWithSubdirectory:item];

Okidoke. One other thing you could consider is the URL-based API for getting the contents of a directory. As part of that, tell it to preload whether the URLs are themselves directories. Query that resource property of each URL. Should be neater still, and means fewer trips to disk in theory.

mkeiser

comment created time in 2 months

PullRequestReviewEvent
PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(v     [self.uploadQueue addOperation:operation]; } ++ (void)synchronouslyUploadAtomicReportsWithConfiguration:(BugsnagConfiguration *)configuration {++    BugsnagNotifier *notifier = [BugsnagNotifier new];+    NSFileManager *fm = NSFileManager.defaultManager;+    NSError *error = nil;++    NSString *container = [BSGFileLocations atomicDirectoryContainer];++    // If the parent directory does not exist, there is nothing to do.+    if (![fm fileExistsAtPath:container]) {+        return;+    }++    NSArray<NSString *> * contents = [fm contentsOfDirectoryAtPath:container error:&error];+    if (!contents) {+        bsg_log_err(@"failed to get contents of atomic container: %@", error);+        return;+    }++    // Enumerate subdirectories.+    for (NSString *item in contents) {+        NSString *fullItemPath = [container stringByAppendingPathComponent:item];+        BOOL isDirectory;+        if (![fm fileExistsAtPath:fullItemPath isDirectory:&isDirectory] || !isDirectory) {+            continue;+        }++        // Instantiate locations for this subdirectory.+        BSGFileLocations *locations = [[BSGFileLocations alloc] initWithSubdirectory:item];++        // Lock the directory. If locking fails, it might indicate that the writer is still writing to it.+        // In that case, we simply ignore it.
        // In that case, we simply ignore it. A future upload should deal with the directory once the lock is released.

Think that helps?

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(v     [self.uploadQueue addOperation:operation]; } ++ (void)synchronouslyUploadAtomicReportsWithConfiguration:(BugsnagConfiguration *)configuration {++    BugsnagNotifier *notifier = [BugsnagNotifier new];+    NSFileManager *fm = NSFileManager.defaultManager;+    NSError *error = nil;++    NSString *container = [BSGFileLocations atomicDirectoryContainer];++    // If the parent directory does not exist, there is nothing to do.+    if (![fm fileExistsAtPath:container]) {+        return;+    }++    NSArray<NSString *> * contents = [fm contentsOfDirectoryAtPath:container error:&error];+    if (!contents) {+        bsg_log_err(@"failed to get contents of atomic container: %@", error);+        return;+    }++    // Enumerate subdirectories.+    for (NSString *item in contents) {+        NSString *fullItemPath = [container stringByAppendingPathComponent:item];+        BOOL isDirectory;+        if (![fm fileExistsAtPath:fullItemPath isDirectory:&isDirectory] || !isDirectory) {+            continue;+        }++        // Instantiate locations for this subdirectory.+        BSGFileLocations *locations = [[BSGFileLocations alloc] initWithSubdirectory:item];

Seems a better approach would be to give BSGFileLocations behaviour whereby it fails to init if item doesn't exist, or isn't a directory, rather than trying to do a pre-emptive check of file exists here.

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 static BOOL ensureDirExists(NSString *path) {     return YES; } -static NSString *rootDirectory(NSString *fsVersion) {+static NSString *rootDirectory(NSString *fsVersion, NSString *subdirectory) {     // Default to an unusable location that will always fail.     static NSString* defaultRootPath = @"/";     static NSString* rootPath = @"/";--    static dispatch_once_t onceToken;-    dispatch_once(&onceToken, ^{+     #if TARGET_OS_TV-        // On tvOS, locations outside the caches directory are not writable, so fall back to using that.-        // https://developer.apple.com/library/archive/documentation/General/Conceptual/AppleTV_PG/index.html#//apple_ref/doc/uid/TP40015241-        NSSearchPathDirectory directory = NSCachesDirectory;+    // On tvOS, locations outside the caches directory are not writable, so fall back to using that.+    // https://developer.apple.com/library/archive/documentation/General/Conceptual/AppleTV_PG/index.html#//apple_ref/doc/uid/TP40015241+    NSSearchPathDirectory directory = NSCachesDirectory; #else-        NSSearchPathDirectory directory = NSApplicationSupportDirectory;+    NSSearchPathDirectory directory = NSApplicationSupportDirectory; #endif-        NSError *error = nil;-        NSURL *url = [NSFileManager.defaultManager URLForDirectory:directory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];-        if (!url) {-            bsg_log_err(@"Could not locate directory for storage: %@", error);-            return;-        }-+    NSError *error = nil;+    NSURL *url = [NSFileManager.defaultManager URLForDirectory:directory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];+    if (!url) {+        bsg_log_err(@"Could not locate directory for storage: %@", error);+        return rootPath;+    }+    +    if (subdirectory != nil) {+        rootPath = [NSString stringWithFormat:@"%@/com.bugsnag.Bugsnag/%@/%@/%@/%@",

You could keep the original code. If subdirectory != nil, append the subdirectory specific things?

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (instancetype)initWithVersion1 {         _metadata = [root stringByAppendingPathComponent:@"metadata.json"];         _state = [root stringByAppendingPathComponent:@"state.json"];         _systemState = [root stringByAppendingPathComponent:@"system_state.json"];+        _lockFile = [root stringByAppendingPathComponent:BSGLockFileName];+        _atomicSubdirectory = [subdirectory copy];     }     return self; } ++ (NSString *)atomicDirectoryContainer {+    return [self v1AtomicDirectoryContainer];+}+++ (NSString *)v1AtomicDirectoryContainer {+    NSString *root = rootDirectory(@"v1", nil);+    return [root stringByAppendingPathComponent:BSGAtomicDirectoryContainerName];+}++- (BOOL)lockForWritingBlocking {+    // Open and lock the lock file, creating it if it doesn't exist. Do block.+    int fd = open(self.lockFile.UTF8String, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, S_IRUSR | S_IWUSR);++    if (fd < 0 ) {+        bsg_log_info(@"failed to lock for writing res: %i error: %s", fd, strerror(errno));+        return NO;+    }+    // NOTE: We currently "leak" the file descriptor here, since currently we don't ever want to unlock+    // until we quit.+    return YES;

Great comment 👍

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 NS_ASSUME_NONNULL_BEGIN  */ @property (readonly, nonatomic) NSString *systemState; +/**+ * Returns `YES` if the receiver uses an atomic subdirectory, `NO` if it uses the shared default directory.+ */+@property (readonly, nonatomic) BOOL usesAtomicSubdirectory;

I worry a bit about the use of "atomic" in these APIs. It feels like the implementation (use of a lock to coordinate multiple processes) is what's being named after, rather than the purpose. Feels like usesSubdirectory would do the job just as well? Maybe terming things "sub-process" instead of "atomic" would work better?

mkeiser

comment created time in 2 months

Pull request review commentsketch-hq/bugsnag-cocoa

PR: on feature/37975-submit-sketchtool-crash-reports

 - (void)uploadEvent:(BugsnagEvent *)event completionHandler:(nullable void (^)(v     [self.uploadQueue addOperation:operation]; } ++ (void)synchronouslyUploadAtomicReportsWithConfiguration:(BugsnagConfiguration *)configuration {++    BugsnagNotifier *notifier = [BugsnagNotifier new];+    NSFileManager *fm = NSFileManager.defaultManager;+    NSError *error = nil;++    NSString *container = [BSGFileLocations atomicDirectoryContainer];++    // If the parent directory does not exist, there is nothing to do.+    if (![fm fileExistsAtPath:container]) {+        return;+    }++    NSArray<NSString *> * contents = [fm contentsOfDirectoryAtPath:container error:&error];+    if (!contents) {+        bsg_log_err(@"failed to get contents of atomic container: %@", error);+        return;+    }

I would say the correct way to handle this is to ask for the contents of the directory. If it fails, consult the error to see if it failed because the directory doesn't exist. Bail early. Otherwise log the error.

mkeiser

comment created time in 2 months

PullRequestReviewEvent