Skip to content

Instantly share code, notes, and snippets.

@dimazen
Last active August 29, 2015 14:04
Show Gist options
  • Save dimazen/71baff77f4fd7840b905 to your computer and use it in GitHub Desktop.
Save dimazen/71baff77f4fd7840b905 to your computer and use it in GitHub Desktop.
TLKUserStore
@import Foundation;
#import "TLKUserStore.h"
@class RACSignal;
@interface TLKUserStore (ReactiveCocoa)
- (RACSignal *)authorizedUserSignalInContext:(NSManagedObjectContext *)context;
- (RACSignal *)authorizedUserSignal;
@end
#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
#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
#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