Skip to content

Commit e65d197

Browse files
committed
First commit
1 parent 4950305 commit e65d197

File tree

8 files changed

+354
-0
lines changed

8 files changed

+354
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2014 JP Simard.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

PeerKit.xcodeproj/project.pbxproj

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
E89FD6C61A0C3CDC00C2FEF7 /* PeerKit.h in Headers */ = {isa = PBXBuildFile; fileRef = E89FD6C51A0C3CDC00C2FEF7 /* PeerKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
1111
E89FD6CC1A0C3CDC00C2FEF7 /* PeerKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E89FD6C01A0C3CDC00C2FEF7 /* PeerKit.framework */; };
1212
E89FD6D31A0C3CDC00C2FEF7 /* PeerKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89FD6D21A0C3CDC00C2FEF7 /* PeerKitTests.swift */; };
13+
E89FD6DE1A0C3CF800C2FEF7 /* PeerKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89FD6DD1A0C3CF800C2FEF7 /* PeerKit.swift */; };
14+
E89FD6E31A0C3D1000C2FEF7 /* Advertiser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89FD6DF1A0C3D1000C2FEF7 /* Advertiser.swift */; };
15+
E89FD6E41A0C3D1000C2FEF7 /* Browser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89FD6E01A0C3D1000C2FEF7 /* Browser.swift */; };
16+
E89FD6E51A0C3D1000C2FEF7 /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89FD6E11A0C3D1000C2FEF7 /* Session.swift */; };
17+
E89FD6E61A0C3D1000C2FEF7 /* Transceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = E89FD6E21A0C3D1000C2FEF7 /* Transceiver.swift */; };
1318
/* End PBXBuildFile section */
1419

1520
/* Begin PBXContainerItemProxy section */
@@ -29,6 +34,11 @@
2934
E89FD6CB1A0C3CDC00C2FEF7 /* PeerKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PeerKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3035
E89FD6D11A0C3CDC00C2FEF7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3136
E89FD6D21A0C3CDC00C2FEF7 /* PeerKitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerKitTests.swift; sourceTree = "<group>"; };
37+
E89FD6DD1A0C3CF800C2FEF7 /* PeerKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerKit.swift; sourceTree = "<group>"; };
38+
E89FD6DF1A0C3D1000C2FEF7 /* Advertiser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Advertiser.swift; sourceTree = "<group>"; };
39+
E89FD6E01A0C3D1000C2FEF7 /* Browser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Browser.swift; sourceTree = "<group>"; };
40+
E89FD6E11A0C3D1000C2FEF7 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = "<group>"; };
41+
E89FD6E21A0C3D1000C2FEF7 /* Transceiver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transceiver.swift; sourceTree = "<group>"; };
3242
/* End PBXFileReference section */
3343

3444
/* Begin PBXFrameworksBuildPhase section */
@@ -72,6 +82,11 @@
7282
isa = PBXGroup;
7383
children = (
7484
E89FD6C51A0C3CDC00C2FEF7 /* PeerKit.h */,
85+
E89FD6DD1A0C3CF800C2FEF7 /* PeerKit.swift */,
86+
E89FD6DF1A0C3D1000C2FEF7 /* Advertiser.swift */,
87+
E89FD6E01A0C3D1000C2FEF7 /* Browser.swift */,
88+
E89FD6E11A0C3D1000C2FEF7 /* Session.swift */,
89+
E89FD6E21A0C3D1000C2FEF7 /* Transceiver.swift */,
7590
E89FD6C31A0C3CDC00C2FEF7 /* Supporting Files */,
7691
);
7792
path = PeerKit;
@@ -209,6 +224,11 @@
209224
isa = PBXSourcesBuildPhase;
210225
buildActionMask = 2147483647;
211226
files = (
227+
E89FD6E51A0C3D1000C2FEF7 /* Session.swift in Sources */,
228+
E89FD6E31A0C3D1000C2FEF7 /* Advertiser.swift in Sources */,
229+
E89FD6DE1A0C3CF800C2FEF7 /* PeerKit.swift in Sources */,
230+
E89FD6E61A0C3D1000C2FEF7 /* Transceiver.swift in Sources */,
231+
E89FD6E41A0C3D1000C2FEF7 /* Browser.swift in Sources */,
212232
);
213233
runOnlyForDeploymentPostprocessing = 0;
214234
};
@@ -319,6 +339,7 @@
319339
E89FD6D71A0C3CDC00C2FEF7 /* Debug */ = {
320340
isa = XCBuildConfiguration;
321341
buildSettings = {
342+
CLANG_ENABLE_MODULES = YES;
322343
DEFINES_MODULE = YES;
323344
DYLIB_COMPATIBILITY_VERSION = 1;
324345
DYLIB_CURRENT_VERSION = 1;
@@ -328,12 +349,14 @@
328349
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
329350
PRODUCT_NAME = "$(TARGET_NAME)";
330351
SKIP_INSTALL = YES;
352+
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
331353
};
332354
name = Debug;
333355
};
334356
E89FD6D81A0C3CDC00C2FEF7 /* Release */ = {
335357
isa = XCBuildConfiguration;
336358
buildSettings = {
359+
CLANG_ENABLE_MODULES = YES;
337360
DEFINES_MODULE = YES;
338361
DYLIB_COMPATIBILITY_VERSION = 1;
339362
DYLIB_CURRENT_VERSION = 1;

PeerKit/Advertiser.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// Advertiser.swift
3+
// CardsAgainst
4+
//
5+
// Created by JP Simard on 11/3/14.
6+
// Copyright (c) 2014 JP Simard. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MultipeerConnectivity
11+
12+
class Advertiser: Session, MCNearbyServiceAdvertiserDelegate {
13+
private var advertiser: MCNearbyServiceAdvertiser?
14+
15+
func startAdvertising(#serviceType: String, discoveryInfo: [String: String]? = nil) {
16+
advertiser = MCNearbyServiceAdvertiser(peer: myPeerID, discoveryInfo: discoveryInfo, serviceType: serviceType)
17+
advertiser?.delegate = self
18+
advertiser?.startAdvertisingPeer()
19+
}
20+
21+
func stopAdvertising() {
22+
advertiser?.delegate = nil
23+
advertiser?.stopAdvertisingPeer()
24+
}
25+
26+
func advertiser(advertiser: MCNearbyServiceAdvertiser!, didReceiveInvitationFromPeer peerID: MCPeerID!, withContext context: NSData!, invitationHandler: ((Bool, MCSession!) -> Void)!) {
27+
var runningTime = -timeStarted.timeIntervalSinceNow
28+
var peerRunningTime = NSTimeInterval()
29+
context.getBytes(&peerRunningTime)
30+
let isPeerOlder = (peerRunningTime > runningTime)
31+
invitationHandler(isPeerOlder, mcSession)
32+
if isPeerOlder {
33+
stopAdvertising()
34+
}
35+
}
36+
}

PeerKit/Browser.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Browser.swift
3+
// CardsAgainst
4+
//
5+
// Created by JP Simard on 11/3/14.
6+
// Copyright (c) 2014 JP Simard. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MultipeerConnectivity
11+
12+
let timeStarted = NSDate()
13+
14+
class Browser: Session, MCNearbyServiceBrowserDelegate {
15+
16+
var mcBrowser: MCNearbyServiceBrowser?
17+
18+
func startBrowsing(serviceType: String) {
19+
mcBrowser = MCNearbyServiceBrowser(peer: myPeerID, serviceType: serviceType)
20+
mcBrowser?.delegate = self
21+
mcBrowser?.startBrowsingForPeers()
22+
}
23+
24+
func stopBrowsing() {
25+
mcBrowser?.delegate = nil
26+
mcBrowser?.stopBrowsingForPeers()
27+
}
28+
29+
func browser(browser: MCNearbyServiceBrowser!, foundPeer peerID: MCPeerID!, withDiscoveryInfo info: [NSObject : AnyObject]!) {
30+
var runningTime = -timeStarted.timeIntervalSinceNow
31+
let context = NSData(bytes: &runningTime, length: sizeof(NSTimeInterval))
32+
browser.invitePeer(peerID, toSession: mcSession, withContext: context, timeout: 30)
33+
}
34+
35+
func browser(browser: MCNearbyServiceBrowser!, lostPeer peerID: MCPeerID!) {
36+
// unused
37+
}
38+
}

PeerKit/PeerKit.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// PeerKit.swift
3+
// CardsAgainst
4+
//
5+
// Created by JP Simard on 11/5/14.
6+
// Copyright (c) 2014 JP Simard. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MultipeerConnectivity
11+
12+
// MARK: Type Aliases
13+
14+
public typealias PeerBlock = ((peerID: MCPeerID) -> Void)
15+
public typealias EventBlock = ((peerID: MCPeerID, event: String, object: AnyObject?) -> Void)
16+
public typealias ObjectBlock = ((peerID: MCPeerID, object: AnyObject?) -> Void)
17+
18+
// MARK: Event Blocks
19+
20+
public var onConnect: PeerBlock?
21+
public var onDisconnect: PeerBlock?
22+
public var onEvent: EventBlock?
23+
public var onEventObject: ObjectBlock?
24+
public var eventBlocks = [String: ObjectBlock]()
25+
26+
// MARK: PeerKit Globals
27+
28+
let myName = UIDevice.currentDevice().name
29+
private let transceiver = Transceiver()
30+
public var session: MCSession?
31+
32+
// MARK: Event Handling
33+
34+
func didConnect(peer: MCPeerID) {
35+
if session == nil {
36+
session = transceiver.sessionForPeer(peer)
37+
}
38+
if let onConnect = onConnect {
39+
dispatch_async(dispatch_get_main_queue()) {
40+
onConnect(peerID: peer)
41+
}
42+
}
43+
}
44+
45+
func didDisconnect(peer: MCPeerID) {
46+
if let onDisconnect = onDisconnect {
47+
dispatch_async(dispatch_get_main_queue()) {
48+
onDisconnect(peerID: peer)
49+
}
50+
}
51+
}
52+
53+
func didReceiveData(data: NSData, fromPeer peer: MCPeerID) {
54+
let dict = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [String: AnyObject]
55+
let event = dict["event"] as String
56+
let object: AnyObject? = dict["object"]
57+
dispatch_async(dispatch_get_main_queue()) {
58+
if let onEvent = onEvent {
59+
onEvent(peerID: peer, event: event, object: object)
60+
}
61+
if let eventBlock = eventBlocks[event] {
62+
eventBlock(peerID: peer, object: object)
63+
}
64+
}
65+
}
66+
67+
// MARK: Advertise/Browse
68+
69+
public func transceive(serviceType: String, discoveryInfo: [String: String]? = nil) {
70+
transceiver.startTransceiving(serviceType: serviceType, discoveryInfo: discoveryInfo)
71+
}
72+
73+
public func advertise(serviceType: String, discoveryInfo: [String: String]? = nil) {
74+
transceiver.startAdvertising(serviceType: serviceType, discoveryInfo: discoveryInfo)
75+
}
76+
77+
public func browse(serviceType: String) {
78+
transceiver.startBrowsing(serviceType: serviceType)
79+
}
80+
81+
// MARK: Events
82+
83+
public func sendEvent(event: String, object: AnyObject? = nil, toPeers peers: [MCPeerID]? = session?.connectedPeers as [MCPeerID]?) {
84+
if peers == nil {
85+
return
86+
}
87+
var rootObject: [String: AnyObject] = ["event": event]
88+
if object != nil {
89+
rootObject["object"] = object!
90+
}
91+
let data = NSKeyedArchiver.archivedDataWithRootObject(rootObject)
92+
session?.sendData(data, toPeers: peers, withMode: .Reliable, error: nil)
93+
}

PeerKit/Session.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// Session.swift
3+
// CardsAgainst
4+
//
5+
// Created by JP Simard on 11/3/14.
6+
// Copyright (c) 2014 JP Simard. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MultipeerConnectivity
11+
12+
public protocol SessionDelegate {
13+
func connecting(myPeerID: MCPeerID, toPeer peer: MCPeerID)
14+
func connected(myPeerID: MCPeerID, toPeer peer: MCPeerID)
15+
func disconnected(myPeerID: MCPeerID, fromPeer peer: MCPeerID)
16+
func receivedData(myPeerID: MCPeerID, data: NSData, fromPeer peer: MCPeerID)
17+
}
18+
19+
public class Session: NSObject, MCSessionDelegate {
20+
public private(set) var myPeerID: MCPeerID
21+
var delegate: SessionDelegate?
22+
public private(set) var mcSession: MCSession
23+
24+
public init(displayName: String, delegate: SessionDelegate? = nil) {
25+
myPeerID = MCPeerID(displayName: displayName)
26+
self.delegate = delegate
27+
mcSession = MCSession(peer: myPeerID)
28+
super.init()
29+
mcSession.delegate = self
30+
}
31+
32+
// MARK: MCSessionDelegate
33+
34+
public func session(session: MCSession!, peer peerID: MCPeerID!, didChangeState state: MCSessionState) {
35+
switch state {
36+
case .Connecting:
37+
delegate?.connecting(myPeerID, toPeer: peerID)
38+
case .Connected:
39+
delegate?.connected(myPeerID, toPeer: peerID)
40+
case .NotConnected:
41+
delegate?.disconnected(myPeerID, fromPeer: peerID)
42+
}
43+
}
44+
45+
public func session(session: MCSession!, didReceiveData data: NSData!, fromPeer peerID: MCPeerID!) {
46+
delegate?.receivedData(myPeerID, data: data, fromPeer: peerID)
47+
}
48+
49+
public func session(session: MCSession!, didReceiveStream stream: NSInputStream!, withName streamName: String!, fromPeer peerID: MCPeerID!) {
50+
// unused
51+
}
52+
53+
public func session(session: MCSession!, didStartReceivingResourceWithName resourceName: String!, fromPeer peerID: MCPeerID!, withProgress progress: NSProgress!) {
54+
// unused
55+
}
56+
57+
public func session(session: MCSession!, didFinishReceivingResourceWithName resourceName: String!, fromPeer peerID: MCPeerID!, atURL localURL: NSURL!, withError error: NSError!) {
58+
// unused
59+
}
60+
}

PeerKit/Transceiver.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// Transceiver.swift
3+
// CardsAgainst
4+
//
5+
// Created by JP Simard on 11/3/14.
6+
// Copyright (c) 2014 JP Simard. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import MultipeerConnectivity
11+
12+
enum TransceiverMode {
13+
case Browse, Advertise, Both
14+
}
15+
16+
public class Transceiver: SessionDelegate {
17+
18+
var transceiverMode = TransceiverMode.Both
19+
let advertiser = Advertiser(displayName: myName)
20+
let browser = Browser(displayName: myName)
21+
22+
init() {
23+
advertiser.delegate = self
24+
browser.delegate = self
25+
}
26+
27+
func startTransceiving(#serviceType: String, discoveryInfo: [String: String]? = nil) {
28+
advertiser.startAdvertising(serviceType: serviceType, discoveryInfo: discoveryInfo)
29+
browser.startBrowsing(serviceType)
30+
transceiverMode = .Both
31+
}
32+
33+
func startAdvertising(#serviceType: String, discoveryInfo: [String: String]? = nil) {
34+
advertiser.startAdvertising(serviceType: serviceType, discoveryInfo: discoveryInfo)
35+
transceiverMode = .Advertise
36+
}
37+
38+
func startBrowsing(#serviceType: String) {
39+
browser.startBrowsing(serviceType)
40+
transceiverMode = .Browse
41+
}
42+
43+
func sessionForPeer(peerID: MCPeerID) -> MCSession? {
44+
if (advertiser.mcSession.connectedPeers as [MCPeerID]).filter({ $0 == peerID }).count > 0 {
45+
return advertiser.mcSession
46+
}
47+
48+
if (browser.mcSession.connectedPeers as [MCPeerID]).filter({ $0 == peerID }).count > 0 {
49+
return browser.mcSession
50+
}
51+
52+
return nil
53+
}
54+
55+
public func connecting(myPeerID: MCPeerID, toPeer peer: MCPeerID) {
56+
// unsupported
57+
}
58+
59+
public func connected(myPeerID: MCPeerID, toPeer peer: MCPeerID) {
60+
didConnect(peer)
61+
}
62+
63+
public func disconnected(myPeerID: MCPeerID, fromPeer peer: MCPeerID) {
64+
didDisconnect(peer)
65+
}
66+
67+
public func receivedData(myPeerID: MCPeerID, data: NSData, fromPeer peer: MCPeerID) {
68+
didReceiveData(data, fromPeer: peer)
69+
}
70+
}

0 commit comments

Comments
 (0)