-
-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 32222ce
Showing
11 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.DS_Store | ||
/.build | ||
/Packages | ||
/*.xcodeproj | ||
xcuserdata/ |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// swift-tools-version:5.2 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "MultipeerKit", | ||
platforms: [ | ||
.macOS(.v10_14), | ||
.iOS(.v12), | ||
.tvOS(.v12) | ||
], | ||
products: [ | ||
// Products define the executables and libraries produced by a package, and make them visible to other packages. | ||
.library( | ||
name: "MultipeerKit", | ||
targets: ["MultipeerKit"]), | ||
], | ||
dependencies: [ | ||
// Dependencies declare other packages that this package depends on. | ||
// .package(url: /* package url */, from: "1.0.0"), | ||
], | ||
targets: [ | ||
// Targets are the basic building blocks of a package. A target can define a module or a test suite. | ||
// Targets can depend on other targets in this package, and on products in packages which this package depends on. | ||
.target( | ||
name: "MultipeerKit", | ||
dependencies: []), | ||
.testTarget( | ||
name: "MultipeerKitTests", | ||
dependencies: ["MultipeerKit"]), | ||
] | ||
) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# MultipeerKit | ||
|
||
A description of this package. |
44 changes: 44 additions & 0 deletions
44
Sources/MultipeerKit/Internal API/Extensions/MCPeerID+Me.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import Foundation | ||
import MultipeerConnectivity.MCPeerID | ||
import os.log | ||
|
||
extension MCPeerID { | ||
|
||
private static let defaultsKey = "_multipeerKit.mePeerID" | ||
|
||
private static func fetchExisting(with config: MultipeerConfiguration) -> MCPeerID? { | ||
guard let data = config.defaults.data(forKey: Self.defaultsKey) else { return nil } | ||
|
||
do { | ||
let peer = try NSKeyedUnarchiver.unarchivedObject(ofClass: MCPeerID.self, from: data) | ||
|
||
guard peer?.displayName == config.peerName else { return nil } | ||
|
||
return peer | ||
} catch { | ||
return nil | ||
} | ||
} | ||
|
||
static func fetchOrCreate(with config: MultipeerConfiguration) -> MCPeerID { | ||
fetchExisting(with: config) ?? MCPeerID(displayName: config.peerName) | ||
} | ||
|
||
} | ||
|
||
#if os(iOS) || os(tvOS) | ||
import UIKit | ||
|
||
public extension MCPeerID { | ||
static var defaultDisplayName: String { UIDevice.current.name } | ||
} | ||
|
||
#else | ||
|
||
import Cocoa | ||
|
||
public extension MCPeerID { | ||
static var defaultDisplayName: String { Host.current().localizedName ?? "Unknown Mac" } | ||
} | ||
|
||
#endif |
127 changes: 127 additions & 0 deletions
127
Sources/MultipeerKit/Internal API/MultipeerConnection.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import Foundation | ||
import MultipeerConnectivity | ||
import os.log | ||
|
||
final class MultipeerConnection: NSObject { | ||
|
||
enum Mode: Int { | ||
case receiver | ||
case transmitter | ||
} | ||
|
||
private let log = MultipeerKit.log(for: MultipeerConnection.self) | ||
|
||
let mode: Mode | ||
let configuration: MultipeerConfiguration | ||
let me: MCPeerID | ||
|
||
init(mode: Mode, configuration: MultipeerConfiguration = .default) { | ||
self.mode = mode | ||
self.configuration = configuration | ||
self.me = MCPeerID.fetchOrCreate(with: configuration) | ||
} | ||
|
||
var didReceiveData: ((Data, MCPeerID) -> Void)? | ||
|
||
func resume() { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
|
||
if mode == .receiver { | ||
advertiser.startAdvertisingPeer() | ||
} else { | ||
browser.startBrowsingForPeers() | ||
} | ||
} | ||
|
||
func stop() { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
|
||
if mode == .receiver { | ||
advertiser.stopAdvertisingPeer() | ||
} else { | ||
browser.stopBrowsingForPeers() | ||
} | ||
} | ||
|
||
private lazy var session: MCSession = { | ||
#warning("TODO: Allow customization of security identity and encryption preference") | ||
let s = MCSession(peer: me, securityIdentity: nil, encryptionPreference: .none) | ||
|
||
s.delegate = self | ||
|
||
return s | ||
}() | ||
|
||
private lazy var browser: MCNearbyServiceBrowser = { | ||
let b = MCNearbyServiceBrowser(peer: me, serviceType: configuration.serviceType) | ||
|
||
b.delegate = self | ||
|
||
return b | ||
}() | ||
|
||
private lazy var advertiser: MCNearbyServiceAdvertiser = { | ||
let a = MCNearbyServiceAdvertiser(peer: me, discoveryInfo: nil, serviceType: configuration.serviceType) | ||
|
||
a.delegate = self | ||
|
||
return a | ||
}() | ||
|
||
} | ||
|
||
// MARK: - Session delegate | ||
|
||
extension MultipeerConnection: MCSessionDelegate { | ||
|
||
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
} | ||
|
||
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
|
||
didReceiveData?(data, peerID) | ||
} | ||
|
||
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
} | ||
|
||
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
} | ||
|
||
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
} | ||
|
||
} | ||
|
||
// MARK: - Browser delegate | ||
|
||
extension MultipeerConnection: MCNearbyServiceBrowserDelegate { | ||
|
||
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
} | ||
|
||
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
} | ||
|
||
} | ||
|
||
// MARK: - Advertiser delegate | ||
|
||
extension MultipeerConnection: MCNearbyServiceAdvertiserDelegate { | ||
|
||
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
|
||
#warning("TODO: Create public API for handling invitations, maybe with a specific Decodable type registered to handle the context that comes in") | ||
|
||
invitationHandler(true, session) | ||
} | ||
|
||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Guilherme Rambo on 28/02/20. | ||
// | ||
|
||
import Foundation | ||
import os.log | ||
|
||
struct MultipeerKit { | ||
static let subsystemName = "codes.rambo.MultipeerKit" | ||
|
||
static func log(for type: AnyClass) -> OSLog { | ||
OSLog(subsystem: subsystemName, category: String(describing: type)) | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
Sources/MultipeerKit/Public API/MultipeerConfiguration.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import Foundation | ||
import MultipeerConnectivity.MCPeerID | ||
|
||
public struct MultipeerConfiguration { | ||
|
||
public let serviceType: String | ||
public let peerName: String | ||
public let defaults: UserDefaults | ||
|
||
public init(serviceType: String, peerName: String, defaults: UserDefaults) { | ||
self.serviceType = serviceType | ||
self.peerName = peerName | ||
self.defaults = defaults | ||
} | ||
|
||
public static let `default` = MultipeerConfiguration( | ||
serviceType: "MultipeerKit", | ||
peerName: MCPeerID.defaultDisplayName, | ||
defaults: .standard | ||
) | ||
|
||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import Foundation | ||
import MultipeerConnectivity.MCPeerID | ||
import os.log | ||
|
||
struct ReceiveHandler<T: Decodable> { | ||
let handle: (T) -> Void | ||
} | ||
|
||
struct MultipeerMessage: Hashable, Codable { | ||
let typeName: String | ||
let payload: Data? | ||
} | ||
|
||
public final class MultipeerReceiver { | ||
|
||
private let log = MultipeerKit.log(for: MultipeerReceiver.self) | ||
|
||
private let connection: MultipeerConnection | ||
|
||
public init(configuration: MultipeerConfiguration = .default) { | ||
self.connection = MultipeerConnection( | ||
mode: .receiver, | ||
configuration: configuration | ||
) | ||
|
||
connection.didReceiveData = { [weak self] data, peer in | ||
self?.handleDataReceived(data, from: peer) | ||
} | ||
} | ||
|
||
public func receive<T: Decodable>(_ type: T.Type, using closure: @escaping (T) -> Void) { | ||
// Problem: need to store the handler for later use. How do we do that, given that the | ||
// handler is generic? 🤔 | ||
} | ||
|
||
public func resume() { | ||
connection.resume() | ||
} | ||
|
||
public func stop() { | ||
connection.stop() | ||
} | ||
|
||
private func handleDataReceived(_ data: Data, from peer: MCPeerID) { | ||
os_log("%{public}@", log: log, type: .debug, #function) | ||
// Here we don't know the type of data so that we can find the correct | ||
// handler for it. Encapsulate every message as a `MultipeerMessage`? 🤔 | ||
// If we do encapsulate, how do we then fetch the correct handler to call? | ||
} | ||
|
||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import XCTest | ||
|
||
import MultipeerKitTests | ||
|
||
var tests = [XCTestCaseEntry]() | ||
tests += MultipeerKitTests.allTests() | ||
XCTMain(tests) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import XCTest | ||
@testable import MultipeerKit | ||
|
||
final class MultipeerKitTests: XCTestCase { | ||
func testExample() { | ||
// This is an example of a functional test case. | ||
// Use XCTAssert and related functions to verify your tests produce the correct | ||
// results. | ||
XCTAssertEqual(MultipeerKit().text, "Hello, World!") | ||
} | ||
|
||
static var allTests = [ | ||
("testExample", testExample), | ||
] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import XCTest | ||
|
||
#if !canImport(ObjectiveC) | ||
public func allTests() -> [XCTestCaseEntry] { | ||
return [ | ||
testCase(MultipeerKitTests.allTests), | ||
] | ||
} | ||
#endif |