Last active
August 29, 2015 14:04
-
-
Save dimazen/71baff77f4fd7840b905 to your computer and use it in GitHub Desktop.
TLKUserStore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import Foundation; | |
#import "TLKUserStore.h" | |
@class RACSignal; | |
@interface TLKUserStore (ReactiveCocoa) | |
- (RACSignal *)authorizedUserSignalInContext:(NSManagedObjectContext *)context; | |
- (RACSignal *)authorizedUserSignal; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "TLKUserStore+ReactiveCocoa.h" | |
@implementation TLKUserStore (ReactiveCocoa) | |
- (RACSignal *)authorizedUserSignalInContext:(NSManagedObjectContext *)context { | |
NSParameterAssert(context); | |
__weak typeof(self) weakSelf = self; | |
return [[[RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) { | |
void (^sendUser)(NSManagedObject *) = ^(NSManagedObject *user) { | |
__strong typeof(self) self = weakSelf; | |
if (!self) { | |
return [subscriber sendCompleted]; | |
} | |
if (user.managedObjectContext == context) { | |
[subscriber sendNext:self.authorizedUser]; | |
} else { | |
[context performBlock:^{ | |
[subscriber sendNext:user ? [self authorizedUserInContext:context] : nil]; | |
}]; | |
} | |
}; | |
sendUser(weakSelf.authorizedUser); | |
NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | |
NSString *name = TLKUserStoreAuthorizedUserChangedNotification; | |
RACDisposable *disposable = [[center rac_addObserverForName:name object:weakSelf] subscribeNext:^(NSNotification *notification) { | |
NSManagedObject *user = notification.userInfo[TLKAuthorizedUserKey]; | |
sendUser(user); | |
}]; | |
return [RACDisposable disposableWithBlock:^{ | |
[disposable dispose]; | |
}]; | |
}] takeUntil:[self rac_willDeallocSignal]] replayLast]; | |
} | |
- (RACSignal *)authorizedUserSignal { | |
return [self authorizedUserSignalInContext:self.defaultContext]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
extern NSString *TLKUserStoreAuthorizedUserChangedNotification; | |
extern NSString *TLKAuthorizedUserKey; | |
@interface TLKUserStore : NSObject | |
+ (instancetype)defaultStore; | |
+ (void)setDefaultStore:(TLKUserStore *)defaultStore; | |
- (instancetype)initWithDefaultContext:(NSManagedObjectContext *)context; | |
+ (id)new __attribute__((unavailable("use +[TLKUserStore initWithDefaultContext:] instead"))); | |
- (id)init __attribute__((unavailable("use +[TLKUserStore initWithDefaultContext:] instead"))); | |
@property (nonatomic, strong, readonly) NSManagedObjectContext *defaultContext; | |
@property (nonatomic, strong) id authorizedUser; | |
- (id)authorizedUserInContext:(NSManagedObjectContext *)context; | |
@end | |
@interface TLKUserStore (Migration) | |
+ (BOOL)migrateAuthorizedUserTo:(NSManagedObject *)object; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "TLKUserStore.h" | |
#import <libkern/OSAtomic.h> | |
NSString *const TLKUserStoreIdentifierKey = @"com.yalantis.userstore.URI"; | |
NSString *TLKUserStoreAuthorizedUserChangedNotification = @"com.yalantis.userstore.didChanged"; | |
NSString *TLKAuthorizedUserKey = @"user"; | |
@implementation TLKUserStore { | |
NSManagedObjectID *_objectIDCache; | |
NSLock *_lock; | |
} | |
#pragma mark - Init | |
- (instancetype)initWithDefaultContext:(NSManagedObjectContext *)context { | |
NSParameterAssert(context != nil); | |
self = [super init]; | |
if (self) { | |
_defaultContext = context; | |
_lock = [NSLock new]; | |
} | |
return self; | |
} | |
#pragma mark - Default Store | |
static volatile OSSpinLock TLKUserStoreDefaultStoreLock = OS_SPINLOCK_INIT; | |
static TLKUserStore *_defaultStore = nil; | |
+ (void)setDefaultStore:(TLKUserStore *)defaultStore { | |
OSSpinLockLock(&TLKUserStoreDefaultStoreLock); | |
_defaultStore = defaultStore; | |
OSSpinLockUnlock(&TLKUserStoreDefaultStoreLock); | |
} | |
+ (instancetype)defaultStore { | |
TLKUserStore *defaultStore = nil; | |
OSSpinLockLock(&TLKUserStoreDefaultStoreLock); | |
defaultStore = _defaultStore; | |
OSSpinLockUnlock(&TLKUserStoreDefaultStoreLock); | |
return defaultStore; | |
} | |
#pragma mark - Notification | |
- (void)postAuthorizedUserChangedNotificationWithUser:(NSManagedObject *)user { | |
void (^postNotification)() = ^{ | |
NSDictionary *userInfo = user ? @{TLKAuthorizedUserKey : user} : nil; | |
[[NSNotificationCenter defaultCenter] postNotificationName:TLKUserStoreAuthorizedUserChangedNotification | |
object:self | |
userInfo:userInfo]; | |
}; | |
if (user.managedObjectContext) { | |
[user.managedObjectContext performBlock:postNotification]; | |
} else { | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
postNotification(); | |
}); | |
} | |
} | |
#pragma mark - Cached User | |
- (id)cachedAuthorizedUserInContext:(NSManagedObjectContext *)context { | |
if (!_objectIDCache) return nil; | |
__block id output = nil; | |
[context performBlockAndWait:^{ | |
__autoreleasing NSError *error = nil; | |
output = [context existingObjectWithID:_objectIDCache error:&error]; | |
#ifdef DEBUG | |
if (error) { | |
NSLog(@"%@: cache error:%@", self, error); | |
} | |
#endif | |
}]; | |
return output; | |
} | |
- (void)cacheAuthorizedUser:(NSManagedObject *)user { | |
_objectIDCache = [user.objectID isTemporaryID] ? nil : user.objectID; | |
} | |
#pragma mark - Application User | |
- (void)setAuthorizedUser:(id)authorizedUser { | |
[_lock lock]; | |
NSManagedObjectID *objectID = [authorizedUser objectID]; | |
NSURL *URI = nil; | |
if (authorizedUser) { | |
NSAssert([objectID isTemporaryID] == NO, @"User.objectID can't be termporary. Have you forgot to save?"); | |
URI = [objectID URIRepresentation]; | |
} | |
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; | |
[defaults setURL:URI forKey:TLKUserStoreIdentifierKey]; | |
[self cacheAuthorizedUser:authorizedUser]; | |
[_lock unlock]; | |
[self postAuthorizedUserChangedNotificationWithUser:authorizedUser]; | |
} | |
- (id)authorizedUserInContext:(NSManagedObjectContext *)context { | |
NSParameterAssert(context != nil); | |
[_lock lock]; | |
__block id user = [self cachedAuthorizedUserInContext:context]; | |
if (user) { | |
[_lock unlock]; | |
return user; | |
} | |
NSURL *URI = [[NSUserDefaults standardUserDefaults] URLForKey:TLKUserStoreIdentifierKey]; | |
if (URI) { | |
[context performBlockAndWait:^{ | |
NSManagedObjectID *objectID = [context.persistentStoreCoordinator managedObjectIDForURIRepresentation:URI]; | |
if (objectID) { | |
user = [context existingObjectWithID:objectID error:NULL]; | |
} | |
[self cacheAuthorizedUser:user]; | |
}]; | |
} | |
[_lock unlock]; | |
return user; | |
} | |
- (id)authorizedUser { | |
return [self authorizedUserInContext:self.defaultContext]; | |
} | |
@end | |
@implementation TLKUserStore (Migration) | |
+ (BOOL)migrateAuthorizedUserTo:(NSManagedObject *)object { | |
NSManagedObjectID *objectID = [object objectID]; | |
if (objectID != nil && ![objectID isTemporaryID]) { | |
NSURL *URI = [objectID URIRepresentation]; | |
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; | |
[defaults setURL:URI forKey:TLKUserStoreIdentifierKey]; | |
return YES; | |
} | |
return NO; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment