diff --git a/Classes/Helpers/PeerBrowser.h b/Classes/Helpers/PeerBrowser.h new file mode 100644 index 0000000..4766023 --- /dev/null +++ b/Classes/Helpers/PeerBrowser.h @@ -0,0 +1,23 @@ +// +// PeerBrowser.h +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import + +@class PeerProxy; + +@interface PeerBrowser : NSObject + +@property (nonatomic, retain) NSMutableArray *peerArray; +@property (nonatomic, retain) NSNetServiceBrowser *peerBrowser; + +- (void)startSearchingForPeers; +- (void)stopSearchingForPeers; +- (NSInteger)connectedPeersCount; +- (PeerProxy *)peerAtIndex:(NSUInteger)index; + +@end diff --git a/Classes/Helpers/PeerBrowser.m b/Classes/Helpers/PeerBrowser.m new file mode 100644 index 0000000..1d38b83 --- /dev/null +++ b/Classes/Helpers/PeerBrowser.m @@ -0,0 +1,104 @@ +// +// PeerBrowser.m +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import "PeerBrowser.h" +#import "Protocol.h" +#import "PeerProxy.h" + + +@implementation PeerBrowser + +@synthesize peerArray = _peerArray; +@synthesize peerBrowser = _peerBrowser; + +- (id)init +{ + self = [super init]; + if (self) + { + _peerArray = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dealloc +{ + [self stopSearchingForPeers]; + + [_peerArray release]; + + [super dealloc]; +} + +#pragma mark - Public methods + +- (NSInteger)connectedPeersCount +{ + return [self.peerArray count]; +} + +- (void)startSearchingForPeers +{ + self.peerBrowser = [[[NSNetServiceBrowser alloc] init] autorelease]; + [self.peerBrowser setDelegate:self]; + [self.peerBrowser searchForServicesOfType:BLUEWOKI_PROTOCOL_NAME + inDomain:@""]; +} + +- (void)stopSearchingForPeers +{ + [self.peerBrowser stop]; + self.peerBrowser = nil; +} + +- (PeerProxy *)peerAtIndex:(NSUInteger)index +{ + return [self.peerArray objectAtIndex:index]; +} + +#pragma mark - NSNetServiceBrowser delegate methods + +- (void)netServiceBrowser:(NSNetServiceBrowser *)browser + didFindService:(NSNetService *)service + moreComing:(BOOL)more +{ + if (!more) + { + PeerProxy *peer = [PeerProxy proxyWithService:service]; + [peer connect]; + [self.peerArray addObject:peer]; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_SCREEN" object:self]; + } +} + +- (void)netServiceBrowser:(NSNetServiceBrowser *)browser + didRemoveService:(NSNetService *)service + moreComing:(BOOL)more +{ + if (!more) + { + PeerProxy *peerToRemove = nil; + for (PeerProxy *proxy in self.peerArray) + { + if ([proxy.serviceName isEqualToString:[service name]]) + { + peerToRemove = proxy; + } + } + + if (peerToRemove != nil) + { + [self.peerArray removeObject:peerToRemove]; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_SCREEN" object:self]; + } +} + +@end diff --git a/Classes/Helpers/PeerProxy.h b/Classes/Helpers/PeerProxy.h new file mode 100644 index 0000000..3966a16 --- /dev/null +++ b/Classes/Helpers/PeerProxy.h @@ -0,0 +1,37 @@ +// +// PeerProxy.h +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import +#import +#import +#import +#import "MessageBrokerDelegate.h" +#import "PeerProxyDelegate.h" + +@interface PeerProxy : NSObject + +@property (nonatomic, retain) NSNetService *netService; +@property (nonatomic, retain) GKSession *chatSession; +@property (nonatomic, getter = isConnected) BOOL connected; +@property (nonatomic, retain) MessageBroker *messageBroker; +@property (nonatomic, assign) id delegate; +@property (nonatomic, readonly) NSString *serviceName; + ++ (id)proxyWithService:(NSNetService *)service; +- (id)initWithService:(NSNetService *)service; +- (void)connect; +- (void)startService; +- (void)stopService; +- (void)sendVoiceCallRequest; +- (void)answerToCallFromPeerID:(NSString *)peerID; + +@end diff --git a/Classes/Helpers/PeerProxy.m b/Classes/Helpers/PeerProxy.m new file mode 100644 index 0000000..b1abb24 --- /dev/null +++ b/Classes/Helpers/PeerProxy.m @@ -0,0 +1,291 @@ +// +// PeerProxy.m +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import "PeerProxy.h" +#import "Protocol.h" +#import "MessageBroker.h" +#import "AsyncSocket.h" +#import "Message.h" + +@interface PeerProxy () + +@property (readwrite, retain) AsyncSocket *listeningSocket; +@property (readwrite, retain) AsyncSocket *connectionSocket; +@property (nonatomic, retain) NSNetService *service; +@property (nonatomic, retain) AsyncSocket *socket; + +@end + + +@implementation PeerProxy + +@synthesize listeningSocket = _listeningSocket; +@synthesize connectionSocket = _connectionSocket; +@synthesize netService = _netService; +@synthesize messageBroker = _messageBroker; +@synthesize service = _service; +@synthesize connected = _connected; +@synthesize socket = _socket; +@synthesize chatSession = _chatSession; +@synthesize delegate = _delegate; +@dynamic serviceName; + ++ (id)proxyWithService:(NSNetService *)service +{ + return [[[[self class] alloc] initWithService:service] autorelease]; +} + +- (id)initWithService:(NSNetService *)service +{ + self = [super init]; + if (self) + { + _connected = NO; + + _service = [service retain]; + _service.delegate = self; + + NSError *myErr; + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&myErr]; + [audioSession setActive:YES error:&myErr]; + + // Routing default audio to external speaker + AudioSessionInitialize(NULL, NULL, NULL, NULL); + UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; + AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, + sizeof(audioRouteOverride), + &audioRouteOverride); + AudioSessionSetActive(true); + + [GKVoiceChatService defaultVoiceChatService].client = self; + + NSString *sessionId = @"workgroup"; + NSString *name = [[UIDevice currentDevice] name]; + self.chatSession = [[[GKSession alloc] initWithSessionID:sessionId + displayName:name + sessionMode:GKSessionModePeer] autorelease]; + self.chatSession.delegate = self; + self.chatSession.available = YES; + } + return self; +} + +- (void)dealloc +{ + [self stopService]; + [_chatSession release]; + [_messageBroker release]; + [_service release]; + [_socket release]; + + _delegate = nil; + + [super dealloc]; +} + +#pragma mark - Public methods + +- (NSString *)serviceName +{ + return [self.service name]; +} + +- (void)connect +{ + [self.service resolveWithTimeout:0]; +} + +-(void)startService +{ + NSError *error; + self.listeningSocket = [[[AsyncSocket alloc] init] autorelease]; + [self.listeningSocket setDelegate:self]; + if (![self.listeningSocket acceptOnPort:0 error:&error]) + { + NSLog(@"Failed to create listening socket"); + return; + } + + self.netService = [[[NSNetService alloc] initWithDomain:@"" + type:BLUEWOKI_PROTOCOL_NAME + name:[UIDevice currentDevice].name + port:self.listeningSocket.localPort] autorelease]; + self.netService.delegate = self; + [self.netService publish]; +} + +-(void)stopService +{ + self.listeningSocket = nil; + self.connectionSocket = nil; + self.messageBroker.delegate = nil; + self.messageBroker = nil; + + [self.netService stop]; + self.netService = nil; +} + +- (void)sendVoiceCallRequest +{ + Message *newMessage = [[[Message alloc] init] autorelease]; + newMessage.kind = MessageKindVoiceCallRequest; + newMessage.body = [self.chatSession.peerID dataUsingEncoding:NSUTF8StringEncoding]; + [self.messageBroker sendMessage:newMessage]; +} + +- (void)answerToCallFromPeerID:(NSString *)peerID +{ + [self.chatSession connectToPeer:peerID withTimeout:60]; +} + +#pragma mark - Socket Callbacks + +- (BOOL)onSocketWillConnect:(AsyncSocket *)sock +{ + if (self.connectionSocket == nil) + { + self.connectionSocket = sock; + return YES; + } + return NO; +} + +-(void)onSocketDidDisconnect:(AsyncSocket *)sock +{ + if (sock == self.connectionSocket) + { + self.connectionSocket = nil; + self.messageBroker = nil; + self.connected = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_SCREEN" object:nil]; + } +} + +-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port +{ + self.messageBroker = [[[MessageBroker alloc] initWithAsyncSocket:sock] autorelease]; + self.messageBroker.delegate = self; + self.connected = YES; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_SCREEN" object:nil]; +} + +#pragma mark - Net Service Delegate Methods + +- (void)netService:(NSNetService *)aNetService didNotPublish:(NSDictionary *)dict +{ + NSLog(@"Failed to publish: %@", dict); +} + +#pragma mark - MessageBroker Delegate Methods + +- (void)messageBroker:(MessageBroker *)server didReceiveMessage:(Message *)message +{ + switch (message.kind) + { + case MessageKindVoiceCallRequest: + { + NSString *peerID = [[[NSString alloc] initWithData:message.body + encoding:NSUTF8StringEncoding] autorelease]; + if ([self.delegate respondsToSelector:@selector(proxy:didReceiveCallRequestFromPeer:)]) + { + [self.delegate proxy:self didReceiveCallRequestFromPeer:peerID]; + } + break; + } + + case MessageKindEndVoiceCall: + { + [self.chatSession disconnectFromAllPeers]; + self.chatSession = nil; + break; + } + + default: + break; + } +} + +#pragma mark - GKSessionDelegate methods + +- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID +{ + if (session == self.chatSession) + { + [self.chatSession acceptConnectionFromPeer:peerID + error:nil]; + [self.chatSession connectToPeer:peerID withTimeout:60]; + } +} + +- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state +{ + switch (state) + { + case GKPeerStateAvailable: + break; + + case GKPeerStateUnavailable: + break; + + case GKPeerStateConnected: + { + [self.chatSession setDataReceiveHandler:self + withContext:nil]; + [[GKVoiceChatService defaultVoiceChatService] startVoiceChatWithParticipantID:peerID + error:nil]; + + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) + { + [UIApplication sharedApplication].idleTimerDisabled = YES; + [UIDevice currentDevice].proximityMonitoringEnabled = YES; + } + break; + } + + case GKPeerStateDisconnected: + break; + + case GKPeerStateConnecting: + break; + + default: + break; + } +} + +- (void)session:(GKSession *)session connectionWithPeerFailed:(NSString *)peerID withError:(NSError *)error +{ + if (session == self.chatSession) + { + } +} + +#pragma mark - GKVoiceChatClient methods + +- (NSString *)participantID +{ + return self.chatSession.peerID; +} + +- (void)voiceChatService:(GKVoiceChatService *)voiceChatService sendData:(NSData *)data toParticipantID:(NSString *)participantID +{ + [self.chatSession sendData:data + toPeers:[NSArray arrayWithObject:participantID] + withDataMode:GKSendDataReliable + error: nil]; +} + +- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context; +{ + [[GKVoiceChatService defaultVoiceChatService] receivedData:data + fromParticipantID:peer]; +} + +@end diff --git a/Classes/Helpers/PeerProxyDelegate.h b/Classes/Helpers/PeerProxyDelegate.h new file mode 100644 index 0000000..4fa60b4 --- /dev/null +++ b/Classes/Helpers/PeerProxyDelegate.h @@ -0,0 +1,19 @@ +// +// PeerProxyDelegate.h +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import + +@class PeerProxy; + +@protocol PeerProxyDelegate + +@optional + +- (void)proxy:(PeerProxy *)proxy didReceiveCallRequestFromPeer:(NSString *)peerID; + +@end diff --git a/Classes/Helpers/PeerService.h b/Classes/Helpers/PeerService.h new file mode 100644 index 0000000..5dac3e3 --- /dev/null +++ b/Classes/Helpers/PeerService.h @@ -0,0 +1,25 @@ +// +// PeerService.h +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import +#import "MessageBrokerDelegate.h" + +@class MessageBroker; + + +@interface PeerService : NSObject + +@property (nonatomic, retain) NSNetService *netService; +@property (nonatomic, retain) MessageBroker *messageBroker; +@property (nonatomic, getter = isConnected) BOOL connected; + +- (void)startService; +- (void)stopService; + +@end diff --git a/Classes/Helpers/PeerService.m b/Classes/Helpers/PeerService.m new file mode 100644 index 0000000..c4dbf8f --- /dev/null +++ b/Classes/Helpers/PeerService.m @@ -0,0 +1,109 @@ +// +// PeerService.m +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import "PeerService.h" +#import "Protocol.h" +#import "MessageBroker.h" +#import "AsyncSocket.h" +#import "Message.h" + + +@interface PeerService () + +@property (readwrite, retain) AsyncSocket *listeningSocket; +@property (readwrite, retain) AsyncSocket *connectionSocket; + +@end + + +@implementation PeerService + +@synthesize messageBroker = _messageBroker; +@synthesize listeningSocket = _listeningSocket; +@synthesize connectionSocket = _connectionSocket; +@synthesize netService = _netService; +@synthesize connected = _connected; + +- (void)dealloc +{ + [self stopService]; + [_messageBroker release]; + + [super dealloc]; +} + +#pragma mark - Public methods + +-(void)startService +{ + NSError *error; + self.listeningSocket = [[[AsyncSocket alloc] init] autorelease]; + [self.listeningSocket setDelegate:self]; + if (![self.listeningSocket acceptOnPort:0 error:&error]) + { + NSLog(@"Failed to create listening socket"); + return; + } + + self.netService = [[[NSNetService alloc] initWithDomain:@"" + type:BLUEWOKI_PROTOCOL_NAME + name:[UIDevice currentDevice].name + port:self.listeningSocket.localPort] autorelease]; + self.netService.delegate = self; + [self.netService publish]; +} + +-(void)stopService +{ + self.listeningSocket = nil; + self.connectionSocket = nil; + + [self.netService stop]; + self.netService = nil; +} + +#pragma mark - Socket Callbacks + +- (BOOL)onSocketWillConnect:(AsyncSocket *)sock +{ + if (self.connectionSocket == nil) + { + self.connectionSocket = sock; + return YES; + } + return NO; +} + +-(void)onSocketDidDisconnect:(AsyncSocket *)sock +{ + if (sock == self.connectionSocket) + { + self.connectionSocket = nil; + self.messageBroker = nil; + self.connected = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_SCREEN" object:nil]; + } +} + +-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port +{ + self.messageBroker = [[[MessageBroker alloc] initWithAsyncSocket:sock] autorelease]; + self.messageBroker.delegate = self; + self.connected = YES; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_SCREEN" object:nil]; +} + +#pragma mark - Net Service Delegate Methods + +- (void)netService:(NSNetService *)aNetService didNotPublish:(NSDictionary *)dict +{ + NSLog(@"Failed to publish: %@", dict); +} + +@end diff --git a/Classes/Helpers/Protocol.h b/Classes/Helpers/Protocol.h new file mode 100644 index 0000000..3d26112 --- /dev/null +++ b/Classes/Helpers/Protocol.h @@ -0,0 +1,9 @@ +// +// Protocol.h +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +extern NSString * const BLUEWOKI_PROTOCOL_NAME; diff --git a/Classes/Helpers/Protocol.m b/Classes/Helpers/Protocol.m new file mode 100644 index 0000000..4ed2b9b --- /dev/null +++ b/Classes/Helpers/Protocol.m @@ -0,0 +1,11 @@ +// +// Protocol.m +// bluewoki +// +// Created by Adrian on 5/21/11. +// Copyright 2011 akosma software. All rights reserved. +// + +#import "Protocol.h" + +NSString * const BLUEWOKI_PROTOCOL_NAME = @"_bluewoki._tcp."; diff --git a/bluewoki.xcodeproj/project.pbxproj b/bluewoki.xcodeproj/project.pbxproj index a3d3fdd..aed5fb2 100755 --- a/bluewoki.xcodeproj/project.pbxproj +++ b/bluewoki.xcodeproj/project.pbxproj @@ -24,6 +24,10 @@ 3A891AA813881F8000804131 /* MessageBroker.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A891AA313881F8000804131 /* MessageBroker.m */; }; 3A891AAD13881FAE00804131 /* AsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A891AAC13881FAE00804131 /* AsyncSocket.m */; }; 3A891AAF13881FDD00804131 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A891AAE13881FDD00804131 /* CFNetwork.framework */; }; + 3A891AB21388224000804131 /* PeerProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A891AB11388224000804131 /* PeerProxy.m */; }; + 3A891ABD138823D000804131 /* Protocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A891ABC138823D000804131 /* Protocol.m */; }; + 3A891AC21388280000804131 /* PeerService.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A891AC11388280000804131 /* PeerService.m */; }; + 3A891AC513882A2700804131 /* PeerBrowser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A891AC413882A2700804131 /* PeerBrowser.m */; }; 3AF6C9450FF8B7F000312D0A /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3AF6C9440FF8B7F000312D0A /* MainWindow.xib */; }; 3AF6F7490FF8DEA50083EA15 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AF6F7450FF8DEA50083EA15 /* Localizable.strings */; }; /* End PBXBuildFile section */ @@ -54,6 +58,15 @@ 3A891AAB13881FAE00804131 /* AsyncSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncSocket.h; sourceTree = ""; }; 3A891AAC13881FAE00804131 /* AsyncSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AsyncSocket.m; sourceTree = ""; }; 3A891AAE13881FDD00804131 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; + 3A891AB01388224000804131 /* PeerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeerProxy.h; sourceTree = ""; }; + 3A891AB11388224000804131 /* PeerProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PeerProxy.m; sourceTree = ""; }; + 3A891ABB138823CF00804131 /* Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Protocol.h; sourceTree = ""; }; + 3A891ABC138823D000804131 /* Protocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Protocol.m; sourceTree = ""; }; + 3A891ABF138825D900804131 /* PeerProxyDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeerProxyDelegate.h; sourceTree = ""; }; + 3A891AC0138827FF00804131 /* PeerService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeerService.h; sourceTree = ""; }; + 3A891AC11388280000804131 /* PeerService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PeerService.m; sourceTree = ""; }; + 3A891AC313882A2700804131 /* PeerBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeerBrowser.h; sourceTree = ""; }; + 3A891AC413882A2700804131 /* PeerBrowser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PeerBrowser.m; sourceTree = ""; }; 3AF6C9440FF8B7F000312D0A /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 3AF6F7460FF8DEA50083EA15 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 3AF6F7470FF8DEA50083EA15 /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; @@ -180,6 +193,15 @@ 3A891AA213881F8000804131 /* MessageBroker.h */, 3A891AA313881F8000804131 /* MessageBroker.m */, 3A891AA413881F8000804131 /* MessageBrokerDelegate.h */, + 3A891AB01388224000804131 /* PeerProxy.h */, + 3A891AB11388224000804131 /* PeerProxy.m */, + 3A891ABF138825D900804131 /* PeerProxyDelegate.h */, + 3A891ABB138823CF00804131 /* Protocol.h */, + 3A891ABC138823D000804131 /* Protocol.m */, + 3A891AC0138827FF00804131 /* PeerService.h */, + 3A891AC11388280000804131 /* PeerService.m */, + 3A891AC313882A2700804131 /* PeerBrowser.h */, + 3A891AC413882A2700804131 /* PeerBrowser.m */, ); path = Helpers; sourceTree = ""; @@ -293,6 +315,10 @@ 3A891AA713881F8000804131 /* Message.m in Sources */, 3A891AA813881F8000804131 /* MessageBroker.m in Sources */, 3A891AAD13881FAE00804131 /* AsyncSocket.m in Sources */, + 3A891AB21388224000804131 /* PeerProxy.m in Sources */, + 3A891ABD138823D000804131 /* Protocol.m in Sources */, + 3A891AC21388280000804131 /* PeerService.m in Sources */, + 3A891AC513882A2700804131 /* PeerBrowser.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };