1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Bonjour/MYBonjourBrowser.m Wed Apr 22 16:45:39 2009 -0700
1.3 @@ -0,0 +1,239 @@
1.4 +//
1.5 +// MYBonjourBrowser.m
1.6 +// MYNetwork
1.7 +//
1.8 +// Created by Jens Alfke on 1/22/08.
1.9 +// Copyright 2008 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "MYBonjourBrowser.h"
1.13 +#import "MYBonjourService.h"
1.14 +#import "Test.h"
1.15 +#import "Logging.h"
1.16 +
1.17 +
1.18 +@interface MYBonjourBrowser ()
1.19 +@property BOOL browsing;
1.20 +@property (retain) NSError* error;
1.21 +@end
1.22 +
1.23 +
1.24 +@implementation MYBonjourBrowser
1.25 +
1.26 +
1.27 +- (id) initWithServiceType: (NSString*)serviceType
1.28 +{
1.29 + Assert(serviceType);
1.30 + self = [super init];
1.31 + if (self != nil) {
1.32 + _serviceType = [serviceType copy];
1.33 + _browser = [[NSNetServiceBrowser alloc] init];
1.34 + _browser.delegate = self;
1.35 + _services = [[NSMutableSet alloc] init];
1.36 + _addServices = [[NSMutableSet alloc] init];
1.37 + _rmvServices = [[NSMutableSet alloc] init];
1.38 + _serviceClass = [MYBonjourService class];
1.39 + }
1.40 + return self;
1.41 +}
1.42 +
1.43 +
1.44 +- (void) dealloc
1.45 +{
1.46 + LogTo(Bonjour,@"DEALLOC BonjourBrowser");
1.47 + [_browser stop];
1.48 + _browser.delegate = nil;
1.49 + [_browser release];
1.50 + [_serviceType release];
1.51 + [_error release];
1.52 + [_services release];
1.53 + [_addServices release];
1.54 + [_rmvServices release];
1.55 + [super dealloc];
1.56 +}
1.57 +
1.58 +
1.59 +@synthesize browsing=_browsing, error=_error, services=_services, serviceClass=_serviceClass;
1.60 +
1.61 +
1.62 +- (void) start
1.63 +{
1.64 + [_browser searchForServicesOfType: _serviceType inDomain: @"local."];
1.65 +}
1.66 +
1.67 +- (void) stop
1.68 +{
1.69 + [_browser stop];
1.70 +}
1.71 +
1.72 +
1.73 +- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)netServiceBrowser
1.74 +{
1.75 + LogTo(Bonjour,@"%@ started browsing",self);
1.76 + self.browsing = YES;
1.77 +}
1.78 +
1.79 +- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)netServiceBrowser
1.80 +{
1.81 + LogTo(Bonjour,@"%@ stopped browsing",self);
1.82 + self.browsing = NO;
1.83 +}
1.84 +
1.85 +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser
1.86 + didNotSearch:(NSDictionary *)errorDict
1.87 +{
1.88 + NSString *domain = [errorDict objectForKey: NSNetServicesErrorDomain];
1.89 + int err = [[errorDict objectForKey: NSNetServicesErrorCode] intValue];
1.90 + self.error = [NSError errorWithDomain: domain code: err userInfo: nil];
1.91 + LogTo(Bonjour,@"%@ got error: ",self,self.error);
1.92 + self.browsing = NO;
1.93 +}
1.94 +
1.95 +
1.96 +- (void) _updateServiceList
1.97 +{
1.98 + if( _rmvServices.count ) {
1.99 + [self willChangeValueForKey: @"services"
1.100 + withSetMutation: NSKeyValueMinusSetMutation
1.101 + usingObjects: _rmvServices];
1.102 + [_services minusSet: _rmvServices];
1.103 + [self didChangeValueForKey: @"services"
1.104 + withSetMutation: NSKeyValueMinusSetMutation
1.105 + usingObjects: _rmvServices];
1.106 + [_rmvServices makeObjectsPerformSelector: @selector(removed)];
1.107 + [_rmvServices removeAllObjects];
1.108 + }
1.109 + if( _addServices.count ) {
1.110 + [_addServices makeObjectsPerformSelector: @selector(added)];
1.111 + [self willChangeValueForKey: @"services"
1.112 + withSetMutation: NSKeyValueUnionSetMutation
1.113 + usingObjects: _addServices];
1.114 + [_services unionSet: _addServices];
1.115 + [self didChangeValueForKey: @"services"
1.116 + withSetMutation: NSKeyValueUnionSetMutation
1.117 + usingObjects: _addServices];
1.118 + [_addServices removeAllObjects];
1.119 + }
1.120 +}
1.121 +
1.122 +
1.123 +- (void) _handleService: (NSNetService*)netService
1.124 + addTo: (NSMutableSet*)addTo
1.125 + removeFrom: (NSMutableSet*)removeFrom
1.126 + moreComing: (BOOL)moreComing
1.127 +{
1.128 + // Wrap the NSNetService in a BonjourService, using an existing instance if possible:
1.129 + MYBonjourService *service = [[_serviceClass alloc] initWithNetService: netService];
1.130 + MYBonjourService *existingService = [_services member: service];
1.131 + if( existingService ) {
1.132 + [service release];
1.133 + service = [existingService retain];
1.134 + }
1.135 +
1.136 + if( [removeFrom containsObject: service] )
1.137 + [removeFrom removeObject: service];
1.138 + else
1.139 + [addTo addObject: service];
1.140 + [service release];
1.141 + if( ! moreComing )
1.142 + [self _updateServiceList];
1.143 +}
1.144 +
1.145 +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser
1.146 + didFindService:(NSNetService *)netService
1.147 + moreComing:(BOOL)moreComing
1.148 +{
1.149 + //LogTo(Bonjour,@"Add service %@",netService);
1.150 + [self _handleService: netService addTo: _addServices removeFrom: _rmvServices moreComing: moreComing];
1.151 +}
1.152 +
1.153 +- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser
1.154 + didRemoveService:(NSNetService *)netService
1.155 + moreComing:(BOOL)moreComing
1.156 +{
1.157 + //LogTo(Bonjour,@"Remove service %@",netService);
1.158 + [self _handleService: netService addTo: _rmvServices removeFrom: _addServices moreComing: moreComing];
1.159 +}
1.160 +
1.161 +
1.162 +@end
1.163 +
1.164 +
1.165 +
1.166 +#pragma mark -
1.167 +#pragma mark TESTING:
1.168 +
1.169 +@interface BonjourTester : NSObject
1.170 +{
1.171 + MYBonjourBrowser *_browser;
1.172 +}
1.173 +@end
1.174 +
1.175 +@implementation BonjourTester
1.176 +
1.177 +- (id) init
1.178 +{
1.179 + self = [super init];
1.180 + if (self != nil) {
1.181 + _browser = [[MYBonjourBrowser alloc] initWithServiceType: @"_http._tcp"];
1.182 + [_browser addObserver: self forKeyPath: @"services" options: NSKeyValueObservingOptionNew context: NULL];
1.183 + [_browser addObserver: self forKeyPath: @"browsing" options: NSKeyValueObservingOptionNew context: NULL];
1.184 + [_browser start];
1.185 + }
1.186 + return self;
1.187 +}
1.188 +
1.189 +- (void) dealloc
1.190 +{
1.191 + [_browser stop];
1.192 + [_browser removeObserver: self forKeyPath: @"services"];
1.193 + [_browser removeObserver: self forKeyPath: @"browsing"];
1.194 + [_browser release];
1.195 + [super dealloc];
1.196 +}
1.197 +
1.198 +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
1.199 +{
1.200 + LogTo(Bonjour,@"Observed change in %@: %@",keyPath,change);
1.201 + if( $equal(keyPath,@"services") ) {
1.202 + if( [[change objectForKey: NSKeyValueChangeKindKey] intValue]==NSKeyValueChangeInsertion ) {
1.203 + NSSet *newServices = [change objectForKey: NSKeyValueChangeNewKey];
1.204 + for( MYBonjourService *service in newServices ) {
1.205 + LogTo(Bonjour,@" --> %@ : TXT=%@", service,service.txtRecord);
1.206 + }
1.207 + }
1.208 + }
1.209 +}
1.210 +
1.211 +@end
1.212 +
1.213 +TestCase(Bonjour) {
1.214 + [NSRunLoop currentRunLoop]; // create runloop
1.215 + BonjourTester *tester = [[BonjourTester alloc] init];
1.216 + [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 15]];
1.217 + [tester release];
1.218 +}
1.219 +
1.220 +
1.221 +
1.222 +/*
1.223 + Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
1.224 +
1.225 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.226 + provided that the following conditions are met:
1.227 +
1.228 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.229 + and the following disclaimer.
1.230 + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
1.231 + and the following disclaimer in the documentation and/or other materials provided with the
1.232 + distribution.
1.233 +
1.234 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.235 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.236 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.237 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.238 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.239 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.240 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.241 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.242 + */