Last active
August 29, 2015 14:01
-
-
Save indragiek/610b1f4b466ecba8d5db to your computer and use it in GitHub Desktop.
ReactiveCocoa-based wrapper around NSNetServiceBrowser
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
// | |
// LTClientBrowser.h | |
// LayerTreeServer | |
// | |
// Created by Indragie Karunaratne on 2014-05-21. | |
// Copyright (c) 2014 Indragie Karunaratne. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import <LayerTreeKit/LayerTreeKit.h> | |
@interface LTClientBrowser : NSObject | |
/** | |
* Searches for LayerTree clients. | |
* | |
* Trying to search while an existing search is still executing will result in a signal | |
* that errors immediately. | |
* | |
* @return A signal that sends an array of `NSNetService`s found once the search completes. | |
*/ | |
- (RACSignal *)searchForClients; | |
@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
// | |
// LTClientBrowser.m | |
// LayerTreeServer | |
// | |
// Created by Indragie Karunaratne on 2014-05-21. | |
// Copyright (c) 2014 Indragie Karunaratne. All rights reserved. | |
// | |
#import "LTClientBrowser.h" | |
#import <LayerTreeKit/LayerTreeKit.h> | |
typedef NS_ENUM(NSInteger, LTClientBrowserState) { | |
LTClientBrowserStateStopped = 0, | |
LTClientBrowserStateSearching, | |
}; | |
typedef NS_ENUM(NSInteger, LTCollectionMutationType) { | |
LTCollectionMutationTypeNone = 0, | |
LTCollectionMutationTypeInsertion, | |
LTCollectionMutationTypeRemoval | |
}; | |
@interface LTCollectionMutation : NSObject | |
@property (nonatomic, assign, readonly) LTCollectionMutationType type; | |
@property (nonatomic, strong) id object; | |
- (instancetype)initWithType:(LTCollectionMutationType)type object:(id)object; | |
@end | |
static NSString * const LTClientBrowserErrorDomain = @"LTClientBrowserErrorDomain"; | |
static NSError * LTClientBrowserAlreadySearchingError() | |
{ | |
NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"A search is already in progress. Please wait until the existing search has completed before attempting to start another one."}; | |
return [NSError errorWithDomain:LTClientBrowserErrorDomain code:1 userInfo:userInfo]; | |
} | |
@interface LTClientBrowser () <NSNetServiceBrowserDelegate> | |
@property (nonatomic, strong, readonly) NSNetServiceBrowser *serviceBrowser; | |
@property (nonatomic, assign) LTClientBrowserState state; | |
@end | |
@implementation LTClientBrowser | |
- (id)init | |
{ | |
if ((self = [super init])) { | |
_serviceBrowser = [[NSNetServiceBrowser alloc] init]; | |
_serviceBrowser.delegate = self; | |
[_serviceBrowser scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes]; | |
} | |
return self; | |
} | |
- (void)dealloc | |
{ | |
[_serviceBrowser removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes]; | |
} | |
#pragma mark - Searching | |
- (RACSignal *)mutationSignalForSelector:(SEL)sel type:(LTCollectionMutationType)type | |
{ | |
return [[self | |
rac_signalForSelector:sel fromProtocol:@protocol(NSNetServiceDelegate)] | |
reduceEach:^(NSNetServiceBrowser *browser, NSNetService *service, NSNumber *moreComing) { | |
LTCollectionMutation *mutation = [[LTCollectionMutation alloc] initWithType:type object:service]; | |
return RACTuplePack(mutation, moreComing); | |
}]; | |
} | |
- (RACSignal *)searchForClients | |
{ | |
if (self.state == LTClientBrowserStateSearching) { | |
return [RACSignal error:LTClientBrowserAlreadySearchingError()]; | |
} | |
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { | |
self.state = LTClientBrowserStateSearching; | |
RACSignal *insertion = [self mutationSignalForSelector:@selector(netServiceBrowser:didFindService:moreComing:) type:LTCollectionMutationTypeInsertion]; | |
RACSignal *removal = [self mutationSignalForSelector:@selector(netServiceBrowser:didRemoveService:moreComing:) type:LTCollectionMutationTypeRemoval]; | |
NSMutableArray *services = [[NSMutableArray alloc] init]; | |
RACDisposable *results = [[[[[RACSignal | |
merge:@[insertion, removal]] | |
reduceEach:^(LTCollectionMutation *mutation, NSNumber *moreComing) { | |
if (mutation.type == LTCollectionMutationTypeInsertion) { | |
[services addObject:mutation.object]; | |
} else if (mutation.type == LTCollectionMutationTypeRemoval) { | |
[services removeObject:mutation.object]; | |
} | |
return moreComing; | |
}] | |
takeUntilBlock:^BOOL(NSNumber *moreComing) { | |
return [moreComing isEqualToNumber:@NO]; | |
}] | |
then:^RACSignal *{ | |
return [RACSignal return:services]; | |
}] | |
subscribe:subscriber]; | |
RACDisposable *failure = [[[[[self | |
rac_signalForSelector:@selector(netServiceBrowser:didNotSearch:) fromProtocol:@protocol(NSNetServiceBrowserDelegate)] | |
take:1] | |
reduceEach:^(NSNetServiceBrowser *browser, NSDictionary *errorDict) { | |
return [RACEvent eventWithError:LTErrorFromBonjourErrorDictionary(errorDict)]; | |
}] | |
dematerialize] | |
subscribe:subscriber]; | |
[self.serviceBrowser searchForServicesOfType:LTServiceType inDomain:LTServiceDomain]; | |
return [RACDisposable disposableWithBlock:^{ | |
[results dispose]; | |
[failure dispose]; | |
[self.serviceBrowser stop]; | |
self.state = LTClientBrowserStateStopped; | |
}]; | |
}] setNameWithFormat:@"<%@:%p> -searchForClients", self.class, self]; | |
} | |
#pragma mark - NSNetServiceBrowserDelegate | |
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didFindService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing {} | |
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didRemoveService:(NSNetService *)netService moreComing:(BOOL)moreServicesComing {} | |
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didNotSearch:(NSDictionary *)errorInfo {} | |
@end | |
@implementation LTCollectionMutation | |
- (instancetype)initWithType:(LTCollectionMutationType)type object:(id)object | |
{ | |
if ((self = [super init])) { | |
_type = type; | |
_object = object; | |
} | |
return self; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment