ComplexBrowser/FileSystemNode.m
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
An abstract wrapper node around the file system. |
*/ |
#import "FileSystemNode.h" |
@interface FileSystemNode () |
@property (strong) NSURL *URL; |
@property (assign) BOOL childrenDirty; |
@property (strong) NSArray *internalChildren; |
@end |
#pragma mark - |
@implementation FileSystemNode |
@dynamic displayName, children, isDirectory, icon, labelColor, documentKind, size, formattedFileSize, creationDate, modificationDate, lastOpened; |
- (instancetype)init { |
NSAssert(NO, @"Invalid use of init; use initWithURL to create FileSystemNode"); |
return [self init]; |
} |
- (instancetype)initWithURL:(NSURL *)url { |
self = [super init]; |
if (self != nil) { |
_URL = url; |
} |
return self; |
} |
- (NSString *)description { |
return [NSString stringWithFormat:@"%@ - %@", super.description, self.URL]; |
} |
- (NSString *)displayName { |
NSString *displayName = @""; |
NSError *error = nil; |
BOOL success = [self.URL getResourceValue:&displayName forKey:NSURLLocalizedNameKey error:&error]; |
// if we got a no value for the localized name, we will try the non-localized name |
if (success && displayName.length > 0) { |
[self.URL getResourceValue:&displayName forKey:NSURLNameKey error:&error]; |
} |
else { |
// can't find resource value for the display name, use the localizedDescription as last resort |
return error.localizedDescription; |
} |
return displayName; |
} |
- (NSImage *)icon { |
return [[NSWorkspace sharedWorkspace] iconForFile:(self.URL).path]; |
} |
- (NSString *)documentKind { |
NSString *kindStr; |
[self.URL getResourceValue:&kindStr forKey:NSURLLocalizedTypeDescriptionKey error:nil]; |
return kindStr; |
} |
- (NSDate *)creationDate { |
NSDate *dateValue; |
[self.URL getResourceValue:&dateValue forKey:NSURLCreationDateKey error:nil]; |
return dateValue; |
} |
- (NSDate *)modificationDate { |
NSDate *dateValue; |
[self.URL getResourceValue:&dateValue forKey:NSURLContentModificationDateKey error:nil]; |
return dateValue; |
} |
- (NSDate *)lastOpened { |
MDItemRef itemRef = MDItemCreateWithURL(nil, (CFURLRef)self.URL); |
NSDate *lastOpenedDate = CFBridgingRelease(MDItemCopyAttribute(itemRef, kMDItemLastUsedDate)); |
CFRelease(itemRef); |
return lastOpenedDate; |
} |
- (NSUInteger)size { |
NSNumber *sizeValue; |
[self.URL getResourceValue:&sizeValue forKey:NSURLFileSizeKey error:nil]; |
return [sizeValue unsignedIntegerValue]; |
} |
- (NSString *)formattedFileSize { |
NSString *sizeStr; |
sizeStr = [NSByteCountFormatter stringFromByteCount:[self size] countStyle:NSByteCountFormatterCountStyleFile]; |
return sizeStr; |
} |
- (BOOL)isDirectory { |
id value = nil; |
[self.URL getResourceValue:&value forKey:NSURLIsDirectoryKey error:nil]; |
return [value boolValue]; |
} |
- (BOOL)isPackage { |
id value = nil; |
[self.URL getResourceValue:&value forKey:NSURLIsPackageKey error:nil]; |
return [value boolValue]; |
} |
- (NSColor *)labelColor { |
id value = nil; |
[self.URL getResourceValue:&value forKey:NSURLLabelColorKey error:nil]; |
return value; |
} |
// We are equal if we represent the same URL. This allows children to reuse the same instances. |
// |
- (BOOL)isEqual:(id)object { |
if ([object isKindOfClass:[FileSystemNode class]]) { |
FileSystemNode *other = (FileSystemNode *)object; |
return [other.URL isEqual:self.URL]; |
} else { |
return NO; |
} |
} |
- (NSUInteger)hash { |
return self.URL.hash; |
} |
- (NSArray *)children { |
if (self.internalChildren == nil || self.childrenDirty) { |
// This logic keeps the same pointers around, if possible. |
NSMutableArray *newChildren = [NSMutableArray array]; |
CFURLEnumeratorRef enumerator = CFURLEnumeratorCreateForDirectoryURL(NULL, (CFURLRef)self.URL, kCFURLEnumeratorSkipInvisibles, (CFArrayRef)[NSArray array]); |
CFURLRef childURL = nil; |
CFURLEnumeratorResult enumeratorResult; |
do { |
enumeratorResult = CFURLEnumeratorGetNextURL(enumerator, &childURL, NULL); |
if (enumeratorResult == kCFURLEnumeratorSuccess) { |
FileSystemNode *node = [[FileSystemNode alloc] initWithURL:(__bridge NSURL *)childURL]; |
if (self.internalChildren != nil) { |
NSInteger oldIndex = [self.internalChildren indexOfObject:(__bridge NSURL *)childURL]; |
if (oldIndex != NSNotFound) { |
// Use the same pointer value, if possible |
node = (self.internalChildren)[oldIndex]; |
} |
} |
[newChildren addObject:node]; |
} else if (enumeratorResult == kCFURLEnumeratorError) { |
// A possible enhancement would be to present error-based items to the user. |
} |
} while (enumeratorResult != kCFURLEnumeratorEnd); |
CFRelease(enumerator); |
_childrenDirty = NO; |
// Now sort them |
_internalChildren = [newChildren sortedArrayUsingComparator:^(id obj1, id obj2) { |
NSString *objName = [obj1 displayName]; |
NSString *obj2Name = [obj2 displayName]; |
NSComparisonResult result = [objName compare:obj2Name options:NSNumericSearch | NSCaseInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch range:NSMakeRange(0, objName.length) locale:[NSLocale currentLocale]]; |
return result; |
}]; |
} |
return self.internalChildren; |
} |
- (void)invalidateChildren { |
_childrenDirty = YES; |
for (FileSystemNode *child in self.internalChildren) { |
[child invalidateChildren]; |
} |
} |
@end |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-04-07