Tweaked release to be immediate instead of on autorelease pool.
2 // MYBonjourRegistration.m
5 // Created by Jens Alfke on 4/27/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYBonjourRegistration.h"
10 #import "MYBonjourService.h"
11 #import "ExceptionUtils.h"
17 #define kTXTTTL 60 // TTL in seconds for TXT records I register
20 @interface MYBonjourRegistration ()
21 @property BOOL registered;
25 @implementation MYBonjourRegistration
28 static NSMutableDictionary *sAllRegistrations;
31 + (void) priv_addRegistration: (MYBonjourRegistration*)reg {
32 if (!sAllRegistrations)
33 sAllRegistrations = [[NSMutableDictionary alloc] init];
34 [sAllRegistrations setObject: reg forKey: reg.fullName];
37 + (void) priv_removeRegistration: (MYBonjourRegistration*)reg {
38 [sAllRegistrations removeObjectForKey: reg.fullName];
41 + (MYBonjourRegistration*) registrationWithFullName: (NSString*)fullName {
42 return [sAllRegistrations objectForKey: fullName];
46 - (id) initWithServiceType: (NSString*)serviceType port: (UInt16)port
50 self.continuous = YES;
51 self.usePrivateConnection = YES; // DNSServiceUpdateRecord doesn't work with shared conn :(
52 _type = [serviceType copy];
67 @synthesize name=_name, type=_type, domain=_domain, port=_port, autoRename=_autoRename;
68 @synthesize registered=_registered;
71 - (NSString*) fullName {
72 return [[self class] fullNameOfService: _name ofType: _type inDomain: _domain];
76 - (BOOL) isSameAsService: (MYBonjourService*)service {
77 return _name && _domain && [self.fullName isEqualToString: service.fullName];
81 - (NSString*) description
83 return $sprintf(@"%@['%@'.%@%@]", self.class,_name,_type,_domain);
87 - (void) priv_registeredAsName: (NSString*)name
88 type: (NSString*)regtype
89 domain: (NSString*)domain
91 if (!$equal(name,_name))
93 if (!$equal(domain,_domain))
95 LogTo(Bonjour,@"Registered %@", self);
96 self.registered = YES;
97 [[self class] priv_addRegistration: self];
101 static void regCallback(DNSServiceRef sdRef,
102 DNSServiceFlags flags,
103 DNSServiceErrorType errorCode,
109 MYBonjourRegistration *reg = context;
112 [reg priv_registeredAsName: [NSString stringWithUTF8String: name]
113 type: [NSString stringWithUTF8String: regtype]
114 domain: [NSString stringWithUTF8String: domain]];
115 }catchAndReport(@"MYBonjourRegistration callback");
116 [reg gotResponse: errorCode];
120 - (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
121 DNSServiceFlags flags = 0;
123 flags |= kDNSServiceFlagsNoAutoRename;
124 NSData *txtData = nil;
126 txtData = [NSNetService dataFromTXTRecordDictionary: _txtRecord];
127 return DNSServiceRegister(sdRefPtr,
130 _name.UTF8String, // _name is likely to be nil
132 _domain.UTF8String, // _domain is most likely nil
145 [[self class] priv_removeRegistration: self];
146 self.registered = NO;
151 - (void) updateTxtRecord {
152 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
153 if (self.serviceRef) {
154 NSData *data = [NSNetService dataFromTXTRecordDictionary: _txtRecord];
155 Assert(data!=nil, @"Can't convert dictionary to TXT record");
156 DNSServiceErrorType err = DNSServiceUpdateRecord(self.serviceRef,
163 Warn(@"%@ failed to update TXT (err=%i)", self,err);
165 LogTo(Bonjour,@"%@ updated TXT to %@", self,data);
170 - (NSDictionary*) txtRecord {
174 - (void) setTxtRecord: (NSDictionary*)txtDict {
175 if (!$equal(_txtRecord,txtDict)) {
177 [_txtRecord setDictionary: txtDict];
179 setObj(&_txtRecord,nil);
180 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
181 [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
185 - (void) setString: (NSString*)value forTxtKey: (NSString*)key
187 NSData *data = [value dataUsingEncoding: NSUTF8StringEncoding];
188 if (!$equal(data, [_txtRecord objectForKey: key])) {
190 if (!_txtRecord) _txtRecord = [[NSMutableDictionary alloc] init];
191 [_txtRecord setObject: data forKey: key];
193 [_txtRecord removeObjectForKey: key];
194 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(updateTxtRecord) object: nil];
195 [self performSelector: @selector(updateTxtRecord) withObject: nil afterDelay: 0.1];
205 #pragma mark TESTING:
209 #import "MYBonjourQuery.h"
210 #import "MYAddressLookup.h"
212 @interface BonjourRegTester : NSObject
214 MYBonjourRegistration *_reg;
219 @implementation BonjourRegTester
222 NSDictionary *txt = $dict({@"time", $sprintf(@"%.3lf", CFAbsoluteTimeGetCurrent())});
223 _reg.txtRecord = txt;
224 [self performSelector: @selector(updateTXT) withObject: nil afterDelay: 3.0];
231 _reg = [[MYBonjourRegistration alloc] initWithServiceType: @"_foo._tcp" port: 12345];
232 [_reg addObserver: self forKeyPath: @"registered" options: NSKeyValueObservingOptionNew context: NULL];
233 [_reg addObserver: self forKeyPath: @"name" options: NSKeyValueObservingOptionNew context: NULL];
244 [_reg removeObserver: self forKeyPath: @"registered"];
245 [_reg removeObserver: self forKeyPath: @"name"];
250 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
252 LogTo(Bonjour,@"Observed change in %@: %@",keyPath,change);
257 TestCase(BonjourReg) {
258 EnableLogTo(Bonjour,YES);
259 EnableLogTo(DNS,YES);
260 [NSRunLoop currentRunLoop]; // create runloop
261 BonjourRegTester *tester = [[BonjourRegTester alloc] init];
262 [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 15]];
270 Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
272 Redistribution and use in source and binary forms, with or without modification, are permitted
273 provided that the following conditions are met:
275 * Redistributions of source code must retain the above copyright notice, this list of conditions
276 and the following disclaimer.
277 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
278 and the following disclaimer in the documentation and/or other materials provided with the
281 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
282 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
283 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
284 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
285 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
286 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
287 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
288 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.