Skip to content

Instantly share code, notes, and snippets.

@ifournight
Last active December 17, 2015 00:48
Show Gist options
  • Save ifournight/5523255 to your computer and use it in GitHub Desktop.
Save ifournight/5523255 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
// Relative path for library's info.plist.
UIKIT_EXTERN NSString *kLibraryInfoPlistPath;
// Relative path for library's NSManagedObjectStore.
UIKIT_EXTERN NSString *kLibraryStorePath;
// Relative path where all library's documents locate.
UIKIT_EXTERN NSString *kLibraryDocumentPath;
// SLibrary represents one Apple documentation library.
// Each Apple documentation library file locate in App's Document directory.
// And it's SLibraryManager's responsibility to locate them and add their's Core Data information.
@interface SLibrary : NSObject
// Absolute path in App's Document directory.
@property (nonatomic, copy) NSString *path;
// Library's name.
@property (nonatomic, copy) NSString *name;
// Copyright.
@property (nonatomic, copy) NSString *copyright;
// ID.
@property (nonatomic, copy) NSString *ID;
// Fallback.
@property (nonatomic, copy) NSString *fallback;
// Library's NSManagedObjectStore's URL that located inside library.
@property (nonatomic, strong) NSURL *storeURL;
// Root SNodes in this library. Will fetched lazily.
@property (nonatomic, strong) NSArray *rootNodes;
// Desiganted Initializer.
- (id)initWithPath:(NSString *)path;
@end
#import "SLibrary.h"
const NSString *kLibraryInfoPlistPath = @"Contents/Info.plist";
const NSString *kLibraryStorePath = @"Contents/Resources/docSet.dsidx";
const NSString *kLibraryDocumentPath @"Contents/Resources/Documents";
@implementation SLibrary
- (id)initWithPath:(NSString *)path
{
self = [super init];
if (self) {
_path = path;
NSString *infoPlistPath = [path stringByAppendingPathComponent:kLibraryInfoPlistPath];
NSDictionary *infoPlist = [NSDictionary dictionaryWithContentsOfFile:infoPlistPath];
if (infoPlist) {
_name = infoPlist[@"CFBundleName"];
_copyright = infoPlist[@"NSHumanReadableCopyright"];
_ID = infoPlist[@"CFBundleIdentifier"];
_fallback = infoPlist[@"DocSetFallbackURL"];
NSString *storePath = [_path stringByAppendingPathComponent:kLibraryStorePath];
_storeURL = [NSURL fileURLWithPath:storePath];
} else {
NSLog(@"Library: fail to init because of nil info.plist");
}
}
return self;
}
- (NSArray *)rootNodes
{
if (_rootNodes == nil) {
// Create NSManagedObjectContext.
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
context.persistentStoreCoordinator = [[SLibraryManager share] persistentStoreCoordinator];
// ObjectIDDescription
NSExpressionDescription *objectIDDescription = [[NSExpressionDescription alloc] init];
objectIDDescription.name = @"Object ID";
objectIDDescription.expression = [NSExpression expressionForEvaluatedObject];
objectIDDescription.expressionResultType = NSObjectIDAttributeType;
// Fetch request and request properties.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequstWithEntityName:@"Node"];
// Predicate
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"kIsSearchable == NO AND primaryParent.kName == [c] %@", @"Developer Library"];
// SortDescriptors
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithName:@"kName" ascending:YES selector:@selector(caseInsensitiveCompare:)]];
// Affected Stores
fetchRequest.affectedStores = @[[[[SLibraryManager share] coordinator] persistentStoreForURL:self.storeURL]];
// Result type.
fetchRequest.resultType = NSDictionaryResultType.
// Properties to fetch.
NSEntityDescription *nodeEntity = [NSEntityDescription entityForName:@"Node" inManagedObjectContext:context];
NSAttributeDescription *nodeNameAttribute = [nodeEntity attributesByName][@"kName"];
fetchRequest.propertiesToFetch = @[objectIDDescription, nodeNameAttribute];
NSArray *fetchResults = [context executeFetchRequest:fetchRequest error:nil];
// Turn fetch result into SNodes.
NSMutableArray *rootNodes = [[NSMutableArray alloc] init];
for (NSDictionary *fetchResults in fetchResults] {
SNodes *node = [[SNode alloc] initWithName:fetchResult[@"kName"] ID:fetchResult[@"objectID"]];
[rootNodes addObject:node];
}
_rootNodes = rootNodes;
}
return _rootNodes;
}
@end
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "SLibrary.h"
UIKIT_EXTERN NSString *SLibraryManagerWillReloadLibrariesNotification;
UIKIT_EXTERN NSString *SLibraryManagerDidReloadLibrariesNottification;
// SLibraryManager is a singleton object, use share to get it.
// It is responsible for Manage all the libraries in App's Document directory, reload when necessary.
// And each reload libraries, library manager will load all libraries' Core Data stuff appropriately,
// for other class that that need to fetch something from Core Data, like SSearchLibrary's search job; Library's root nodes; SNode, SToken fetch information properties.
@interface SLibraryManager : NSObject
// Managed libraries.
@property (nonatomic, strong) NSArray *libraries;
// An dictionary which each library object's key is library's corresponding managed object store's ID.
// This property's purpose:
// Because NSManagedObject like token and node, don't have a property or method that can fetch its corresponding NSManagedObjectStore, and Project needs to get object's store to compose object's URL.
// But NSManagedObject can fetch its storeID, so this property can help SToken and SNode to get its mother library.
@property (nonatomic, strong) NSDictionary *librariesByStoreID;
// NSPersistentStoreCoordinator that holds all libraries' store within itself.
// The one and only coordinator throughout the application.
@property (nonatomic, strong) NSPersistentStoreCoordinator *coordinator;
// Use this method to get the singleton manager.
+ (id)share;
// Reload libraries.
- (void)reloadLibraries;
@end
#import "SLibraryManager.h"
const NSString *SLibraryManagerWillReloadLibrariesNotification = @"Library Manager Will Reload Libraries";
const NSString *SLibraryManagerDidReloadLibrariesNotification = @Library Manager Did Reload Libraries";
@interface SLibraryManager()
// Use SLibrary's rootNodes property to prefetch all libraries' root nodes.
- (void)prefetchLibrariesRootNodes;
@end
@implementation SLibraryManager
- (id)init
{
self = [super init];
if (self) {
// NSManagedObjectModel
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"docSet" withExtension:@"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
// NSPersistentStoreCoordinator.
_coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
}
return self;
}
+ (id)share
{
static SLibraryManager *share = nil;
dispatch_once_t = onceToken;
dispatch_once(&onceToken, ^{
share = [[SLibraryManager alloc] init];
});
return share;
}
- (void)reloadLibraries
{
// Remove old store in coordinator.
if (self.coordinator.persistentStores) {
for (NSPersistentStore *store in self.coordinator.persistentStores) {
[self.coordinator removePersistentStore:store error:nil];
}
}
// Scan and locate files with docset extension in App's Document directory.
NSMutableArray *libraries = [NSMutableArray alloc] init];
NSMutableDictionary *librariesByStoreID = [NSMutableDictionary alloc] init];
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSFileManager *fileManager = [NSFileManager defaultManager];
for (NSString *path in [fileManager contentsOfDirectoryAtPath:documentPath error:nil]) {
if ([path.pathExtension isEqualToString:@"docset"]) {
NSString *libraryPath = [documentPath stringByAppendingPathComponent:path];
Library *library = [[Library alloc] initWithPath:libraryPath];
// Add persistent store into coordinator.
[self.coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:library.storeURL options:@{NSReadOnlyPersistentStoreOption : @YES} error:nil];
NSPersistentStore *store = [self.coordinator persistentStoreForURL:library.storeURL];
[libraries addObject:library];
[librariesByStoreID setObject:library forKey:store.identifier];
}
}
self.libraries = libraries;
self.librariesByStoreID = librariesByStoreID;
// Every time reload libraries, prefetch libraries' root nodes.
[self prefetchLibrariesRootNodes];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment