Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
insidegui committed Feb 28, 2020
0 parents commit 32222ce
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
33 changes: 33 additions & 0 deletions Package.swift
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"]),
]
)
3 changes: 3 additions & 0 deletions README.md
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 Sources/MultipeerKit/Internal API/Extensions/MCPeerID+Me.swift
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 Sources/MultipeerKit/Internal API/MultipeerConnection.swift
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)
}

}
17 changes: 17 additions & 0 deletions Sources/MultipeerKit/Internal API/MultipeerKit.swift
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 Sources/MultipeerKit/Public API/MultipeerConfiguration.swift
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
)

}
51 changes: 51 additions & 0 deletions Sources/MultipeerKit/Public API/MultipeerReceiver.swift
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?
}

}
7 changes: 7 additions & 0 deletions Tests/LinuxMain.swift
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)
15 changes: 15 additions & 0 deletions Tests/MultipeerKitTests/MultipeerKitTests.swift
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),
]
}
9 changes: 9 additions & 0 deletions Tests/MultipeerKitTests/XCTestManifests.swift
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

0 comments on commit 32222ce

Please sign in to comment.