├── .gitignore ├── .jazzy.yml ├── .swift-version ├── .swiftlint.yml ├── .travis.yml ├── Assets ├── banner.png └── entitlements.png ├── CONTRIBUTING.md ├── Dangerfile ├── Gemfile ├── LICENSE ├── MultiPeer.podspec ├── MultiPeer.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── MultiPeer iOS.xcscheme │ ├── MultiPeer macOS.xcscheme │ └── MultiPeer tvOS.xcscheme ├── Package.swift ├── README.md ├── Sources ├── MultiPeer.h ├── MultiPeer.swift ├── MultiPeerDelegate.swift ├── MultiPeer_tvOS.h ├── Peer.swift └── Supporting Files │ ├── Info-iOS.plist │ ├── Info-macOS.plist │ └── Info-tvOS.plist └── docs ├── Classes.html ├── Classes └── MultiPeer.html ├── Extensions.html ├── Extensions └── Data.html ├── Protocols.html ├── Protocols └── MultiPeerDelegate.html ├── Typealiases.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── MultiPeer.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ └── MultiPeer.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ └── Data.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ └── MultiPeerDelegate.html │ │ ├── Typealiases.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ └── search.json │ │ └── docSet.dsidx └── MultiPeer.tgz ├── img ├── carat.png ├── dash.png └── gh.png ├── index.html ├── js ├── jazzy.js └── jquery.min.js ├── search.json └── undocumented.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build/ 3 | DerivedData/ 4 | xcuserdata 5 | -------------------------------------------------------------------------------- /.jazzy.yml: -------------------------------------------------------------------------------- 1 | module: MultiPeer 2 | github_url: https://github.com/dingwilson/MultiPeer 3 | 4 | clean: true 5 | xcodebuild_arguments: [-scheme,MultiPeer iOS] -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | - line_length 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode10.2 2 | language: objective-c 3 | xcode_project: MultiPeer.xcodeproj 4 | 5 | branches: 6 | only: 7 | master 8 | 9 | before_install: 10 | - git clone https://github.com/dingwilson/devops-ci.git 11 | 12 | script: 13 | - set -o pipefail 14 | 15 | - swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.11" 16 | 17 | - pod spec lint 18 | 19 | - bundle exec danger --fail-on-errors=true 20 | 21 | after_success: 22 | - source ./devops-ci/gen_jazzy_docs.sh 23 | -------------------------------------------------------------------------------- /Assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/Assets/banner.png -------------------------------------------------------------------------------- /Assets/entitlements.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/Assets/entitlements.png -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidlines 2 | 3 | Thank you for your interest in contributing to MultiPeer! Please use [SwiftLint](https://github.com/realm/SwiftLint) and also use tests to ensure overall code quality and readability. 4 | 5 | Feel free to contact [dingwilson](https://github.com/dingwilson) if you have any questions about this project. 6 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet 2 | warn('PR is classed as Work in Progress', sticky: false) if github.pr_title.include? '[WIP]' 3 | 4 | # Run SwiftLint 5 | swiftlint.lint_files 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "danger" 6 | gem "danger-swiftlint" 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Wilson Ding 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. 22 | -------------------------------------------------------------------------------- /MultiPeer.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "MultiPeer" 3 | s.version = "0.2.0" 4 | s.summary = "MultiPeer makes it easy to automatically connect to multiple nearby devices and share information using MultipeerConnectivity." 5 | s.description = "MultiPeer is a wrapper for the MultipeerConnectivity framework for offline data transmission between devices. This framework makes it easy to automatically connect to multiple nearby devices and share information using either bluetooth or wifi radios." 6 | s.homepage = "https://github.com/dingwilson/MultiPeer" 7 | s.license = { :type => "MIT", :file => "LICENSE" } 8 | s.author = { "Wilson Ding" => "hello@wilsonding.com" } 9 | s.ios.deployment_target = '8.0' 10 | s.osx.deployment_target = '10.11' 11 | s.tvos.deployment_target = '9.10' 12 | s.source = { :git => "https://github.com/dingwilson/MultiPeer.git", :tag => s.version } 13 | s.source_files = "Sources/*.{h,m,swift}" 14 | s.documentation_url = 'http://wilsonding.com/MultiPeer/' 15 | end 16 | -------------------------------------------------------------------------------- /MultiPeer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /MultiPeer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MultiPeer.xcodeproj/xcshareddata/xcschemes/MultiPeer iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /MultiPeer.xcodeproj/xcshareddata/xcschemes/MultiPeer macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /MultiPeer.xcodeproj/xcshareddata/xcschemes/MultiPeer tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "MultiPeer", 7 | products: [ 8 | .library( 9 | name: "MultiPeer", 10 | targets: ["MultiPeer"]) 11 | ], 12 | dependencies: [ 13 | ], 14 | targets: [ 15 | .target( 16 | name: "MultiPeer", 17 | dependencies: [], 18 | path: "Sources") 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | [![Build Status](https://travis-ci.org/dingwilson/MultiPeer.svg?branch=master)](https://travis-ci.org/dingwilson/MultiPeer) 6 | [![CocoaPods Version Status](https://img.shields.io/cocoapods/v/MultiPeer.svg)](https://cocoapods.org/pods/MultiPeer) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-Compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) 8 | [![doccov](https://wilsonding.com/MultiPeer/badge.svg)](https://wilsonding.com/MultiPeer) 9 | ![iOS](https://img.shields.io/badge/os-iOS-green.svg?style=flat) 10 | ![MacOS](https://img.shields.io/badge/os-MacOS-green.svg?style=flat) 11 | ![tvOS](https://img.shields.io/badge/os-tvOS-green.svg?style=flat) 12 | [![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org) 13 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) 14 | 15 | A wrapper for Apple's MultipeerConnectivity framework for offline data transmission between Apple devices. This framework makes it easy to automatically connect to multiple nearby devices and share information using either bluetooth or wifi radios. 16 | 17 | 1. [Features](#features) 18 | 2. [Integration](#integration) 19 | - [CocoaPods](#cocoapods) 20 | - [Carthage](#carthage) 21 | - [Swift Package Manager](#swift-package-manager) 22 | 3. [Usage](#usage) 23 | 4. [Example](#example) 24 | 5. [License](#license) 25 | 6. [Authors](#authors) 26 | 27 | ## Features 28 | 29 | - [x] Supports iOS/macOS/tvOS 30 | - [x] Auto Connection 31 | - [x] Auto Invitations/Advertising 32 | - [x] Send/Receive data via MultipeerConnectivity Framework 33 | - [x] Specify data types for easy handling 34 | 35 | ## Integration 36 | 37 | #### CocoaPods 38 | You can use [CocoaPods](http://cocoapods.org/) to install `MultiPeer` by adding it to your `Podfile`: 39 | 40 | ```ruby 41 | pod 'MultiPeer' 42 | ``` 43 | 44 | #### Carthage 45 | You can use [Carthage](https://github.com/Carthage/Carthage) to install `MultiPeer` by adding it to your `Cartfile`: 46 | 47 | ``` 48 | github "dingwilson/MultiPeer" 49 | ``` 50 | 51 | #### Swift Package Manager 52 | For [SPM](https://swift.org/package-manager/), add the following to your package dependencies: 53 | 54 | ``` 55 | .package(url: "https://github.com/dingwilson/MultiPeer.git", .upToNextMinor(from: "0.0.0")) 56 | ``` 57 | 58 | ## Usage 59 | 60 | To get started, import MultiPeer. 61 | 62 | ```swift 63 | import MultiPeer 64 | ``` 65 | 66 | Then, simply initialize MultiPeer with the name of your session (`serviceType`). There are two modes of connections (`advertiser` and `browser`). To utilize both, simply use `.autoConnect()`. 67 | 68 | ```swift 69 | MultiPeer.instance.initialize(serviceType: "demo-app") 70 | MultiPeer.instance.autoConnect() 71 | ``` 72 | 73 | Any data transmitted by MultiPeer will always be accompanied by a numerical "type", to ensure other peers know what kind of data is being received, and how to properly process it. You can manage this by creating a `UInt32` enum, as shown below: 74 | 75 | ```swift 76 | enum DataType: UInt32 { 77 | case string = 1 78 | case image = 2 79 | // ... 80 | } 81 | ``` 82 | 83 | To send data, simply use the `.send(object: type:)` function: 84 | 85 | ```swift 86 | MultiPeer.instance.send(object: "Hello World!", type: DataType.string.rawValue) 87 | ``` 88 | 89 | To receive data, we must conform to the `MultiPeerDelegate` protocol: 90 | 91 | ```swift 92 | func multiPeer(didReceiveData data: Data, ofType type: UInt32, from peerID: MCPeerID) { 93 | switch type { 94 | case DataType.string.rawValue: 95 | let string = data.convert() as! String 96 | // do something with the received string 97 | break 98 | 99 | case DataType.image.rawValue: 100 | let image = UIImage(data: data) 101 | // do something with the received UIImage 102 | break 103 | 104 | default: 105 | break 106 | } 107 | } 108 | 109 | func multiPeer(connectedDevicesChanged devices: [String]) { 110 | } 111 | ``` 112 | 113 | Ensure that you set the MultiPeer delegate. 114 | 115 | ```swift 116 | MultiPeer.instance.delegate = self 117 | ``` 118 | 119 | Finally you'll need to enable incoming / outgoing connections in your entitlements. 120 |

121 | 122 |

123 | 124 | Congratulations! You have successfully sent data using MultiPeer! For more detailed information (including details of other functions), please see the [docs](http://wilsonding.com/MultiPeer). 125 | 126 | ## Example 127 | For an example app using MultiPeer, checkout [MultiPeer_Sample](https://github.com/dingwilson/MultiPeer_Sample). 128 | 129 | ## License 130 | `MultiPeer` is released under an [MIT License](http://opensource.org/licenses/MIT). See [LICENSE](LICENSE) for details. 131 | 132 | ## Authors 133 | 134 | - [Wilson Ding](https://github.com/dingwilson) 135 | 136 | Project heavily inspired by [Apple-Signal](https://github.com/kirankunigiri/Apple-Signal). 137 | -------------------------------------------------------------------------------- /Sources/MultiPeer.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiPeer.h 3 | // MultiPeer 4 | // 5 | // Created by Wilson Ding on 2/2/18. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for MultiPeer_iOS. 11 | FOUNDATION_EXPORT double MultiPeer_iOSVersionNumber; 12 | 13 | //! Project version string for MultiPeer_iOS. 14 | FOUNDATION_EXPORT const unsigned char MultiPeer_iOSVersionString[]; 15 | -------------------------------------------------------------------------------- /Sources/MultiPeer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultiPeer.swift 3 | // MultiPeer 4 | // 5 | // Created by Wilson Ding on 2/1/18. 6 | // 7 | 8 | import Foundation 9 | import MultipeerConnectivity 10 | 11 | /// Main Class for MultiPeer 12 | public class MultiPeer: NSObject { 13 | 14 | /// Singleton instance - call via MultiPeer.instance 15 | public static let instance = MultiPeer() 16 | 17 | // MARK: Properties 18 | 19 | /** Conforms to MultiPeerDelegate: Handles receiving data and changes in connections */ 20 | public weak var delegate: MultiPeerDelegate? 21 | 22 | /** Name of MultiPeer session: Up to one hyphen (-) and 15 characters */ 23 | var serviceType: String! 24 | 25 | /** Device's name */ 26 | var devicePeerID: MCPeerID! 27 | 28 | /** Advertises session */ 29 | var serviceAdvertiser: MCNearbyServiceAdvertiser! 30 | 31 | /** Browses for sessions */ 32 | var serviceBrowser: MCNearbyServiceBrowser! 33 | 34 | /// Amount of time to spend connecting before timeout 35 | public var connectionTimeout = 10.0 36 | 37 | /// Peers available to connect to 38 | public var availablePeers: [Peer] = [] 39 | 40 | /// Peers connected to 41 | public var connectedPeers: [Peer] = [] 42 | 43 | /// Names of all connected devices 44 | public var connectedDeviceNames: [String] { 45 | return session.connectedPeers.map({$0.displayName}) 46 | } 47 | 48 | /// Prints out all errors and status updates 49 | public var debugMode = false 50 | 51 | /** Main session object that manages the current connections */ 52 | lazy var session: MCSession = { 53 | let session = MCSession(peer: self.devicePeerID, securityIdentity: nil, encryptionPreference: .none) 54 | session.delegate = self 55 | return session 56 | }() 57 | 58 | // MARK: - Initializers 59 | 60 | /// Initializes the MultiPeer service with a serviceType and the default deviceName 61 | /// - Parameters: 62 | /// - serviceType: String with name of MultiPeer service. Up to one hyphen (-) and 15 characters. 63 | /// Uses default device name 64 | public func initialize(serviceType: String) { 65 | #if os(iOS) 66 | initialize(serviceType: serviceType, deviceName: UIDevice.current.name) 67 | #elseif os(macOS) 68 | initialize(serviceType: serviceType, deviceName: Host.current().name!) 69 | #elseif os(tvOS) 70 | initialize(serviceType: serviceType, deviceName: UIDevice.current.name) 71 | #endif 72 | } 73 | 74 | /// Initializes the MultiPeer service with a serviceType and a custom deviceName 75 | /// - Parameters: 76 | /// - serviceType: String with name of MultiPeer service. Up to one hyphen (-) and 15 characters. 77 | /// - deviceName: String containing custom name for device 78 | public func initialize(serviceType: String, deviceName: String) { 79 | // Setup device/session properties 80 | self.serviceType = serviceType 81 | self.devicePeerID = MCPeerID(displayName: deviceName) 82 | 83 | // Setup the service advertiser 84 | self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: self.devicePeerID, 85 | discoveryInfo: nil, 86 | serviceType: serviceType) 87 | self.serviceAdvertiser.delegate = self 88 | 89 | // Setup the service browser 90 | self.serviceBrowser = MCNearbyServiceBrowser(peer: self.devicePeerID, 91 | serviceType: serviceType) 92 | self.serviceBrowser.delegate = self 93 | } 94 | 95 | // deinit: stop advertising and browsing services 96 | deinit { 97 | disconnect() 98 | } 99 | 100 | // MARK: - Methods 101 | 102 | /// HOST: Automatically browses and invites all found devices 103 | public func startInviting() { 104 | self.serviceBrowser.startBrowsingForPeers() 105 | } 106 | 107 | /// JOIN: Automatically advertises and accepts all invites 108 | public func startAccepting() { 109 | self.serviceAdvertiser.startAdvertisingPeer() 110 | } 111 | 112 | /// HOST and JOIN: Uses both advertising and browsing to connect. 113 | public func autoConnect() { 114 | startInviting() 115 | startAccepting() 116 | } 117 | 118 | /// Stops the invitation process 119 | public func stopInviting() { 120 | self.serviceBrowser.stopBrowsingForPeers() 121 | } 122 | 123 | /// Stops accepting invites and becomes invisible on the network 124 | public func stopAccepting() { 125 | self.serviceAdvertiser.stopAdvertisingPeer() 126 | } 127 | 128 | /// Stops all invite/accept services 129 | public func stopSearching() { 130 | stopAccepting() 131 | stopInviting() 132 | } 133 | 134 | /// Disconnects from the current session and stops all searching activity 135 | public func disconnect() { 136 | session.disconnect() 137 | connectedPeers.removeAll() 138 | availablePeers.removeAll() 139 | } 140 | 141 | /// Stops all invite/accept services, disconnects from the current session, and stops all searching activity 142 | public func end() { 143 | stopSearching() 144 | disconnect() 145 | } 146 | 147 | /// Returns true if there are any connected peers 148 | public var isConnected: Bool { 149 | return connectedPeers.count > 0 150 | } 151 | 152 | /// Sends an object (and type) to all connected peers. 153 | /// - Parameters: 154 | /// - object: Object (Any) to send to all connected peers. 155 | /// - type: Type of data (UInt32) sent 156 | /// After sending the object, you can use the extension for Data, `convertData()` to convert it back into an object. 157 | public func send(object: Any, type: UInt32) { 158 | if isConnected { 159 | let data = NSKeyedArchiver.archivedData(withRootObject: object) 160 | 161 | send(data: data, type: type) 162 | } 163 | } 164 | 165 | /// Sends Data (and type) to all connected peers. 166 | /// - Parameters: 167 | /// - data: Data (Data) to send to all connected peers. 168 | /// - type: Type of data (UInt32) sent 169 | /// After sending the data, you can use the extension for Data, `convertData()` to convert it back into data. 170 | public func send(data: Data, type: UInt32) { 171 | if isConnected { 172 | do { 173 | let container: [Any] = [data, type] 174 | let item = NSKeyedArchiver.archivedData(withRootObject: container) 175 | try session.send(item, toPeers: session.connectedPeers, with: MCSessionSendDataMode.reliable) 176 | } catch let error { 177 | printDebug(error.localizedDescription) 178 | } 179 | } 180 | } 181 | 182 | /** Prints only if in debug mode */ 183 | fileprivate func printDebug(_ string: String) { 184 | if debugMode { 185 | print(string) 186 | } 187 | } 188 | 189 | } 190 | 191 | // MARK: - Advertiser Delegate 192 | extension MultiPeer: MCNearbyServiceAdvertiserDelegate { 193 | 194 | /// Received invitation 195 | public func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { 196 | 197 | OperationQueue.main.addOperation { 198 | invitationHandler(true, self.session) 199 | } 200 | } 201 | 202 | /// Error, could not start advertising 203 | public func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didNotStartAdvertisingPeer error: Error) { 204 | printDebug("Could not start advertising due to error: \(error)") 205 | } 206 | 207 | } 208 | 209 | // MARK: - Browser Delegate 210 | extension MultiPeer: MCNearbyServiceBrowserDelegate { 211 | 212 | /// Found a peer, update the list of available peers 213 | public func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String: String]?) { 214 | printDebug("Found peer: \(peerID)") 215 | 216 | // Update the list of available peers 217 | availablePeers.append(Peer(peerID: peerID, state: .notConnected)) 218 | 219 | browser.invitePeer(peerID, to: session, withContext: nil, timeout: connectionTimeout) 220 | } 221 | 222 | /// Lost a peer, update the list of available peers 223 | public func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { 224 | printDebug("Lost peer: \(peerID)") 225 | 226 | // Update the lost peer 227 | availablePeers = availablePeers.filter { $0.peerID != peerID } 228 | } 229 | 230 | /// Error, could not start browsing 231 | public func browser(_ browser: MCNearbyServiceBrowser, didNotStartBrowsingForPeers error: Error) { 232 | printDebug("Could not start browsing due to error: \(error)") 233 | } 234 | 235 | } 236 | 237 | // MARK: - Session Delegate 238 | extension MultiPeer: MCSessionDelegate { 239 | 240 | /// Peer changed state, update all connected peers and send new connection list to delegate connectedDevicesChanged 241 | public func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { 242 | // If the new state is connected, then remove it from the available peers 243 | // Otherwise, update the state 244 | if state == .connected { 245 | availablePeers = availablePeers.filter { $0.peerID != peerID } 246 | printDebug("Peer \(peerID.displayName) changed to connected.") 247 | } else { 248 | availablePeers.filter { $0.peerID == peerID }.first?.state = state 249 | printDebug("Peer \(peerID.displayName) changed to not connected.") 250 | } 251 | 252 | // Update all connected peers 253 | connectedPeers = session.connectedPeers.map { Peer(peerID: $0, state: .connected) } 254 | 255 | // Send new connection list to delegate 256 | OperationQueue.main.addOperation { 257 | self.delegate?.multiPeer(connectedDevicesChanged: session.connectedPeers.map({$0.displayName})) 258 | } 259 | } 260 | 261 | /// Received data, update delegate didRecieveData 262 | public func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { 263 | printDebug("Received data: \(data.count) bytes") 264 | 265 | guard let container = data.convert() as? [Any] else { return } 266 | guard let item = container[0] as? Data else { return } 267 | guard let type = container[1] as? UInt32 else { return } 268 | 269 | OperationQueue.main.addOperation { 270 | self.delegate?.multiPeer(didReceiveData: item, ofType: type, from: peerID) 271 | } 272 | 273 | } 274 | 275 | /// Received stream 276 | public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { 277 | printDebug("Received stream") 278 | } 279 | 280 | /// Started receiving resource 281 | public func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { 282 | printDebug("Started receiving resource with name: \(resourceName)") 283 | } 284 | 285 | /// Finished receiving resource 286 | public func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { 287 | printDebug("Finished receiving resource with name: \(resourceName)") 288 | } 289 | 290 | } 291 | 292 | // MARK: - Data extension for conversion 293 | extension Data { 294 | 295 | /// Unarchive data into an object and return as type `Any`. 296 | public func convert() -> Any { 297 | return NSKeyedUnarchiver.unarchiveObject(with: self)! 298 | } 299 | 300 | /// Converts an object into Data using NSKeyedArchiver 301 | public static func toData(object: Any) -> Data { 302 | return NSKeyedArchiver.archivedData(withRootObject: object) 303 | } 304 | 305 | } 306 | -------------------------------------------------------------------------------- /Sources/MultiPeerDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultiPeerDelegate.swift 3 | // MultiPeer 4 | // 5 | // Created by Wilson Ding on 2/1/18. 6 | // 7 | 8 | import Foundation 9 | import MultipeerConnectivity 10 | 11 | /// MultiPeerDelegate 12 | /// 13 | /// - Delegate for MultiPeer. Conform to this interface to recieve data and be notified when connection/disconnection events occur 14 | /// 15 | /// multiPeer(didRecieveData: Data, ofType: UInt32) 16 | /// multiPeer(connectedDevicesChanged: [String]) 17 | 18 | public typealias MCPeerID = MultipeerConnectivity.MCPeerID 19 | public protocol MultiPeerDelegate: class { 20 | /// didReceiveData: delegate runs on receiving data from another peer 21 | func multiPeer(didReceiveData data: Data, ofType type: UInt32, from peerID: MCPeerID) 22 | 23 | /// connectedDevicesChanged: delegate runs on connection/disconnection event in session 24 | func multiPeer(connectedDevicesChanged devices: [String]) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Sources/MultiPeer_tvOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiPeer_tvOS.h 3 | // MultiPeer tvOS 4 | // 5 | // Created by Martynets Ruslan on 3/2/19. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for MultiPeer_tvOS. 11 | FOUNDATION_EXPORT double MultiPeer_tvOSVersionNumber; 12 | 13 | //! Project version string for MultiPeer_tvOS. 14 | FOUNDATION_EXPORT const unsigned char MultiPeer_tvOSVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /Sources/Peer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Peer.swift 3 | // MultiPeer 4 | // 5 | // Created by Wilson Ding on 2/1/18. 6 | // 7 | 8 | import Foundation 9 | import MultipeerConnectivity 10 | 11 | /// Class containing peerID and session state 12 | public class Peer { 13 | 14 | var peerID: MCPeerID 15 | var state: MCSessionState 16 | 17 | init(peerID: MCPeerID, state: MCSessionState) { 18 | self.peerID = peerID 19 | self.state = state 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Supporting Files/Info-iOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Supporting Files/Info-macOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Supporting Files/Info-tvOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | MultiPeer 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Classes

72 |

The following classes are available globally.

73 | 74 |
75 |
76 |
77 |
    78 |
  • 79 |
    80 | 81 | 82 | 83 | MultiPeer 84 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |

    Main Class for MultiPeer

    92 | 93 | See more 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    public class MultiPeer : NSObject
    100 |
    extension MultiPeer: MCNearbyServiceAdvertiserDelegate
    101 |
    extension MultiPeer: MCNearbyServiceBrowserDelegate
    102 |
    extension MultiPeer: MCSessionDelegate
    103 | 104 |
    105 |
    106 |
    107 |
    108 |
  • 109 |
  • 110 |
    111 | 112 | 113 | 114 | Peer 115 | 116 |
    117 |
    118 |
    119 |
    120 |
    121 |
    122 |

    Class containing peerID and session state

    123 | 124 |
    125 |
    126 |

    Declaration

    127 |
    128 |

    Swift

    129 |
    public class Peer
    130 | 131 |
    132 |
    133 |
    134 |
    135 |
  • 136 |
137 |
138 |
139 |
140 | 144 |
145 |
146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Extensions

72 |

The following extensions are available globally.

73 | 74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 | 82 |

Data extension for conversion

83 |

84 |
85 |
86 |
    87 |
  • 88 |
    89 | 90 | 91 | 92 | Data 93 | 94 |
    95 |
    96 |
    97 |
    98 |
    99 |
    100 | 101 | See more 102 |
    103 |
    104 |

    Declaration

    105 |
    106 |

    Swift

    107 |
    extension Data
    108 | 109 |
    110 |
    111 |
    112 |
    113 |
  • 114 |
115 |
116 |
117 |
118 | 122 |
123 |
124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/Extensions/Data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Data Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Data

72 |
73 |
74 |
extension Data
75 | 76 |
77 |
78 | 79 |
80 |
81 |
82 |
    83 |
  • 84 |
    85 | 86 | 87 | 88 | convert() 89 | 90 |
    91 |
    92 |
    93 |
    94 |
    95 |
    96 |

    Unarchive data into an object and return as type Any.

    97 | 98 |
    99 |
    100 |

    Declaration

    101 |
    102 |

    Swift

    103 |
    public func convert() -> Any
    104 | 105 |
    106 |
    107 |
    108 |
    109 |
  • 110 |
  • 111 |
    112 | 113 | 114 | 115 | toData(object:) 116 | 117 |
    118 |
    119 |
    120 |
    121 |
    122 |
    123 |

    Converts an object into Data using NSKeyedArchiver

    124 | 125 |
    126 |
    127 |

    Declaration

    128 |
    129 |

    Swift

    130 |
    public static func toData(object: Any) -> Data
    131 | 132 |
    133 |
    134 |
    135 |
    136 |
  • 137 |
138 |
139 |
140 |
141 | 145 |
146 |
147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Protocols

72 |

The following protocols are available globally.

73 | 74 |
75 |
76 |
77 |
    78 |
  • 79 |
    80 | 81 | 82 | 83 | MultiPeerDelegate 84 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |

    Undocumented

    92 | 93 | See more 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    public protocol MultiPeerDelegate : AnyObject
    100 | 101 |
    102 |
    103 |
    104 |
    105 |
  • 106 |
107 |
108 |
109 |
110 | 114 |
115 |
116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/Protocols/MultiPeerDelegate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MultiPeerDelegate Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

MultiPeerDelegate

72 |
73 |
74 |
public protocol MultiPeerDelegate : AnyObject
75 | 76 |
77 |
78 |

Undocumented

79 | 80 |
81 |
82 |
83 |
    84 |
  • 85 |
    86 | 87 | 88 | 89 | multiPeer(didReceiveData:ofType:from:) 90 | 91 |
    92 |
    93 |
    94 |
    95 |
    96 |
    97 |

    didReceiveData: delegate runs on receiving data from another peer

    98 | 99 |
    100 |
    101 |

    Declaration

    102 |
    103 |

    Swift

    104 |
    func multiPeer(didReceiveData data: Data, ofType type: UInt32, from peerID: MCPeerID)
    105 | 106 |
    107 |
    108 |
    109 |
    110 |
  • 111 |
  • 112 |
    113 | 114 | 115 | 116 | multiPeer(connectedDevicesChanged:) 117 | 118 |
    119 |
    120 |
    121 |
    122 |
    123 |
    124 |

    connectedDevicesChanged: delegate runs on connection/disconnection event in session

    125 | 126 |
    127 |
    128 |

    Declaration

    129 |
    130 |

    Swift

    131 |
    func multiPeer(connectedDevicesChanged devices: [String])
    132 | 133 |
    134 |
    135 |
    136 |
    137 |
  • 138 |
139 |
140 |
141 |
142 | 146 |
147 |
148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Type Aliases

72 |

The following type aliases are available globally.

73 | 74 |
75 |
76 |
77 |
    78 |
  • 79 |
    80 | 81 | 82 | 83 | MCPeerID 84 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |

    MultiPeerDelegate

    92 | 93 |
      94 |
    • Delegate for MultiPeer. Conform to this interface to recieve data and be notified when connection/disconnection events occur
    • 95 |
    96 | 97 |

    multiPeer(didRecieveData: Data, ofType: UInt32) 98 | multiPeer(connectedDevicesChanged: [String])

    99 | 100 |
    101 |
    102 |

    Declaration

    103 |
    104 |

    Swift

    105 |
    public typealias MCPeerID = MultipeerConnectivity.MCPeerID
    106 | 107 |
    108 |
    109 |
    110 |
    111 |
  • 112 |
113 |
114 |
115 |
116 | 120 |
121 |
122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 97% 23 | 24 | 25 | 97% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 2; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 2; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | .main-content .section-name p { 173 | margin-bottom: inherit; 174 | line-height: inherit; } 175 | .main-content .section-name code { 176 | background-color: inherit; 177 | padding: inherit; 178 | color: inherit; } 179 | 180 | .section { 181 | padding: 0 25px; } 182 | 183 | .highlight { 184 | background-color: #eee; 185 | padding: 10px 12px; 186 | border: 1px solid #e2e2e2; 187 | border-radius: 4px; 188 | overflow-x: auto; } 189 | 190 | .declaration .highlight { 191 | overflow-x: initial; 192 | padding: 0 40px 40px 0; 193 | margin-bottom: -25px; 194 | background-color: transparent; 195 | border: none; } 196 | 197 | .section-name { 198 | margin: 0; 199 | margin-left: 18px; } 200 | 201 | .task-group-section { 202 | padding-left: 6px; 203 | border-top: 1px solid #e2e2e2; } 204 | 205 | .task-group { 206 | padding-top: 0px; } 207 | 208 | .task-name-container a[name]:before { 209 | content: ""; 210 | display: block; 211 | padding-top: 70px; 212 | margin: -70px 0 0; } 213 | 214 | .section-name-container { 215 | position: relative; 216 | display: inline-block; } 217 | .section-name-container .section-name-link { 218 | position: absolute; 219 | top: 0; 220 | left: 0; 221 | bottom: 0; 222 | right: 0; 223 | margin-bottom: 0; } 224 | .section-name-container .section-name { 225 | position: relative; 226 | pointer-events: none; 227 | z-index: 1; } 228 | .section-name-container .section-name a { 229 | pointer-events: auto; } 230 | 231 | .item { 232 | padding-top: 8px; 233 | width: 100%; 234 | list-style-type: none; } 235 | .item a[name]:before { 236 | content: ""; 237 | display: block; 238 | padding-top: 70px; 239 | margin: -70px 0 0; } 240 | .item code { 241 | background-color: transparent; 242 | padding: 0; } 243 | .item .token, .item .direct-link { 244 | padding-left: 3px; 245 | margin-left: 15px; 246 | font-size: 11.9px; 247 | transition: all 300ms; } 248 | .item .token-open { 249 | margin-left: 0px; } 250 | .item .discouraged { 251 | text-decoration: line-through; } 252 | .item .declaration-note { 253 | font-size: .85em; 254 | color: gray; 255 | font-style: italic; } 256 | 257 | .pointer-container { 258 | border-bottom: 1px solid #e2e2e2; 259 | left: -23px; 260 | padding-bottom: 13px; 261 | position: relative; 262 | width: 110%; } 263 | 264 | .pointer { 265 | background: #f9f9f9; 266 | border-left: 1px solid #e2e2e2; 267 | border-top: 1px solid #e2e2e2; 268 | height: 12px; 269 | left: 21px; 270 | top: -7px; 271 | -webkit-transform: rotate(45deg); 272 | -moz-transform: rotate(45deg); 273 | -o-transform: rotate(45deg); 274 | transform: rotate(45deg); 275 | position: absolute; 276 | width: 12px; } 277 | 278 | .height-container { 279 | display: none; 280 | left: -25px; 281 | padding: 0 25px; 282 | position: relative; 283 | width: 100%; 284 | overflow: hidden; } 285 | .height-container .section { 286 | background: #f9f9f9; 287 | border-bottom: 1px solid #e2e2e2; 288 | left: -25px; 289 | position: relative; 290 | width: 100%; 291 | padding-top: 10px; 292 | padding-bottom: 5px; } 293 | 294 | .aside, .language { 295 | padding: 6px 12px; 296 | margin: 12px 0; 297 | border-left: 5px solid #dddddd; 298 | overflow-y: hidden; } 299 | .aside .aside-title, .language .aside-title { 300 | font-size: 9px; 301 | letter-spacing: 2px; 302 | text-transform: uppercase; 303 | padding-bottom: 0; 304 | margin: 0; 305 | color: #aaa; 306 | -webkit-user-select: none; } 307 | .aside p:last-child, .language p:last-child { 308 | margin-bottom: 0; } 309 | 310 | .language { 311 | border-left: 5px solid #cde9f4; } 312 | .language .aside-title { 313 | color: #4b8afb; } 314 | 315 | .aside-warning, .aside-deprecated, .aside-unavailable { 316 | border-left: 5px solid #ff6666; } 317 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 318 | color: #ff0000; } 319 | 320 | .graybox { 321 | border-collapse: collapse; 322 | width: 100%; } 323 | .graybox p { 324 | margin: 0; 325 | word-break: break-word; 326 | min-width: 50px; } 327 | .graybox td { 328 | border: 1px solid #e2e2e2; 329 | padding: 5px 25px 5px 10px; 330 | vertical-align: middle; } 331 | .graybox tr td:first-of-type { 332 | text-align: right; 333 | padding: 7px; 334 | vertical-align: top; 335 | word-break: normal; 336 | width: 40px; } 337 | 338 | .slightly-smaller { 339 | font-size: 0.9em; } 340 | 341 | #footer { 342 | position: relative; 343 | top: 10px; 344 | bottom: 0px; 345 | margin-left: 25px; } 346 | #footer p { 347 | margin: 0; 348 | color: #aaa; 349 | font-size: 0.8em; } 350 | 351 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 352 | display: none; } 353 | 354 | html.dash .main-content { 355 | width: 980px; 356 | margin-left: 0; 357 | border: none; 358 | width: 100%; 359 | top: 0; 360 | padding-bottom: 0; } 361 | 362 | html.dash .height-container { 363 | display: block; } 364 | 365 | html.dash .item .token { 366 | margin-left: 0; } 367 | 368 | html.dash .content-wrapper { 369 | width: auto; } 370 | 371 | html.dash #footer { 372 | position: static; } 373 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.multipeer 7 | CFBundleName 8 | MultiPeer 9 | DocSetPlatformFamily 10 | multipeer 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Classes

72 |

The following classes are available globally.

73 | 74 |
75 |
76 |
77 |
    78 |
  • 79 |
    80 | 81 | 82 | 83 | MultiPeer 84 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |

    Main Class for MultiPeer

    92 | 93 | See more 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    public class MultiPeer : NSObject
    100 |
    extension MultiPeer: MCNearbyServiceAdvertiserDelegate
    101 |
    extension MultiPeer: MCNearbyServiceBrowserDelegate
    102 |
    extension MultiPeer: MCSessionDelegate
    103 | 104 |
    105 |
    106 |
    107 |
    108 |
  • 109 |
  • 110 |
    111 | 112 | 113 | 114 | Peer 115 | 116 |
    117 |
    118 |
    119 |
    120 |
    121 |
    122 |

    Class containing peerID and session state

    123 | 124 |
    125 |
    126 |

    Declaration

    127 |
    128 |

    Swift

    129 |
    public class Peer
    130 | 131 |
    132 |
    133 |
    134 |
    135 |
  • 136 |
137 |
138 |
139 |
140 | 144 |
145 |
146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Extensions

72 |

The following extensions are available globally.

73 | 74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 | 82 |

Data extension for conversion

83 |

84 |
85 |
86 |
    87 |
  • 88 |
    89 | 90 | 91 | 92 | Data 93 | 94 |
    95 |
    96 |
    97 |
    98 |
    99 |
    100 | 101 | See more 102 |
    103 |
    104 |

    Declaration

    105 |
    106 |

    Swift

    107 |
    extension Data
    108 | 109 |
    110 |
    111 |
    112 |
    113 |
  • 114 |
115 |
116 |
117 |
118 | 122 |
123 |
124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/Extensions/Data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Data Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Data

72 |
73 |
74 |
extension Data
75 | 76 |
77 |
78 | 79 |
80 |
81 |
82 |
    83 |
  • 84 |
    85 | 86 | 87 | 88 | convert() 89 | 90 |
    91 |
    92 |
    93 |
    94 |
    95 |
    96 |

    Unarchive data into an object and return as type Any.

    97 | 98 |
    99 |
    100 |

    Declaration

    101 |
    102 |

    Swift

    103 |
    public func convert() -> Any
    104 | 105 |
    106 |
    107 |
    108 |
    109 |
  • 110 |
  • 111 |
    112 | 113 | 114 | 115 | toData(object:) 116 | 117 |
    118 |
    119 |
    120 |
    121 |
    122 |
    123 |

    Converts an object into Data using NSKeyedArchiver

    124 | 125 |
    126 |
    127 |

    Declaration

    128 |
    129 |

    Swift

    130 |
    public static func toData(object: Any) -> Data
    131 | 132 |
    133 |
    134 |
    135 |
    136 |
  • 137 |
138 |
139 |
140 |
141 | 145 |
146 |
147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Protocols

72 |

The following protocols are available globally.

73 | 74 |
75 |
76 |
77 |
    78 |
  • 79 |
    80 | 81 | 82 | 83 | MultiPeerDelegate 84 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |

    Undocumented

    92 | 93 | See more 94 |
    95 |
    96 |

    Declaration

    97 |
    98 |

    Swift

    99 |
    public protocol MultiPeerDelegate : AnyObject
    100 | 101 |
    102 |
    103 |
    104 |
    105 |
  • 106 |
107 |
108 |
109 |
110 | 114 |
115 |
116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/Protocols/MultiPeerDelegate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MultiPeerDelegate Protocol Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

MultiPeerDelegate

72 |
73 |
74 |
public protocol MultiPeerDelegate : AnyObject
75 | 76 |
77 |
78 |

Undocumented

79 | 80 |
81 |
82 |
83 |
    84 |
  • 85 |
    86 | 87 | 88 | 89 | multiPeer(didReceiveData:ofType:from:) 90 | 91 |
    92 |
    93 |
    94 |
    95 |
    96 |
    97 |

    didReceiveData: delegate runs on receiving data from another peer

    98 | 99 |
    100 |
    101 |

    Declaration

    102 |
    103 |

    Swift

    104 |
    func multiPeer(didReceiveData data: Data, ofType type: UInt32, from peerID: MCPeerID)
    105 | 106 |
    107 |
    108 |
    109 |
    110 |
  • 111 |
  • 112 |
    113 | 114 | 115 | 116 | multiPeer(connectedDevicesChanged:) 117 | 118 |
    119 |
    120 |
    121 |
    122 |
    123 |
    124 |

    connectedDevicesChanged: delegate runs on connection/disconnection event in session

    125 | 126 |
    127 |
    128 |

    Declaration

    129 |
    130 |

    Swift

    131 |
    func multiPeer(connectedDevicesChanged devices: [String])
    132 | 133 |
    134 |
    135 |
    136 |
    137 |
  • 138 |
139 |
140 |
141 |
142 | 146 |
147 |
148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

MultiPeer Docs (97% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 68 |
69 |
70 |
71 |

Type Aliases

72 |

The following type aliases are available globally.

73 | 74 |
75 |
76 |
77 |
    78 |
  • 79 |
    80 | 81 | 82 | 83 | MCPeerID 84 | 85 |
    86 |
    87 |
    88 |
    89 |
    90 |
    91 |

    MultiPeerDelegate

    92 | 93 |
      94 |
    • Delegate for MultiPeer. Conform to this interface to recieve data and be notified when connection/disconnection events occur
    • 95 |
    96 | 97 |

    multiPeer(didRecieveData: Data, ofType: UInt32) 98 | multiPeer(connectedDevicesChanged: [String])

    99 | 100 |
    101 |
    102 |

    Declaration

    103 |
    104 |

    Swift

    105 |
    public typealias MCPeerID = MultipeerConnectivity.MCPeerID
    106 | 107 |
    108 |
    109 |
    110 |
    111 |
  • 112 |
113 |
114 |
115 |
116 | 120 |
121 |
122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 2; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 2; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | .main-content .section-name p { 173 | margin-bottom: inherit; 174 | line-height: inherit; } 175 | .main-content .section-name code { 176 | background-color: inherit; 177 | padding: inherit; 178 | color: inherit; } 179 | 180 | .section { 181 | padding: 0 25px; } 182 | 183 | .highlight { 184 | background-color: #eee; 185 | padding: 10px 12px; 186 | border: 1px solid #e2e2e2; 187 | border-radius: 4px; 188 | overflow-x: auto; } 189 | 190 | .declaration .highlight { 191 | overflow-x: initial; 192 | padding: 0 40px 40px 0; 193 | margin-bottom: -25px; 194 | background-color: transparent; 195 | border: none; } 196 | 197 | .section-name { 198 | margin: 0; 199 | margin-left: 18px; } 200 | 201 | .task-group-section { 202 | padding-left: 6px; 203 | border-top: 1px solid #e2e2e2; } 204 | 205 | .task-group { 206 | padding-top: 0px; } 207 | 208 | .task-name-container a[name]:before { 209 | content: ""; 210 | display: block; 211 | padding-top: 70px; 212 | margin: -70px 0 0; } 213 | 214 | .section-name-container { 215 | position: relative; 216 | display: inline-block; } 217 | .section-name-container .section-name-link { 218 | position: absolute; 219 | top: 0; 220 | left: 0; 221 | bottom: 0; 222 | right: 0; 223 | margin-bottom: 0; } 224 | .section-name-container .section-name { 225 | position: relative; 226 | pointer-events: none; 227 | z-index: 1; } 228 | .section-name-container .section-name a { 229 | pointer-events: auto; } 230 | 231 | .item { 232 | padding-top: 8px; 233 | width: 100%; 234 | list-style-type: none; } 235 | .item a[name]:before { 236 | content: ""; 237 | display: block; 238 | padding-top: 70px; 239 | margin: -70px 0 0; } 240 | .item code { 241 | background-color: transparent; 242 | padding: 0; } 243 | .item .token, .item .direct-link { 244 | padding-left: 3px; 245 | margin-left: 15px; 246 | font-size: 11.9px; 247 | transition: all 300ms; } 248 | .item .token-open { 249 | margin-left: 0px; } 250 | .item .discouraged { 251 | text-decoration: line-through; } 252 | .item .declaration-note { 253 | font-size: .85em; 254 | color: gray; 255 | font-style: italic; } 256 | 257 | .pointer-container { 258 | border-bottom: 1px solid #e2e2e2; 259 | left: -23px; 260 | padding-bottom: 13px; 261 | position: relative; 262 | width: 110%; } 263 | 264 | .pointer { 265 | background: #f9f9f9; 266 | border-left: 1px solid #e2e2e2; 267 | border-top: 1px solid #e2e2e2; 268 | height: 12px; 269 | left: 21px; 270 | top: -7px; 271 | -webkit-transform: rotate(45deg); 272 | -moz-transform: rotate(45deg); 273 | -o-transform: rotate(45deg); 274 | transform: rotate(45deg); 275 | position: absolute; 276 | width: 12px; } 277 | 278 | .height-container { 279 | display: none; 280 | left: -25px; 281 | padding: 0 25px; 282 | position: relative; 283 | width: 100%; 284 | overflow: hidden; } 285 | .height-container .section { 286 | background: #f9f9f9; 287 | border-bottom: 1px solid #e2e2e2; 288 | left: -25px; 289 | position: relative; 290 | width: 100%; 291 | padding-top: 10px; 292 | padding-bottom: 5px; } 293 | 294 | .aside, .language { 295 | padding: 6px 12px; 296 | margin: 12px 0; 297 | border-left: 5px solid #dddddd; 298 | overflow-y: hidden; } 299 | .aside .aside-title, .language .aside-title { 300 | font-size: 9px; 301 | letter-spacing: 2px; 302 | text-transform: uppercase; 303 | padding-bottom: 0; 304 | margin: 0; 305 | color: #aaa; 306 | -webkit-user-select: none; } 307 | .aside p:last-child, .language p:last-child { 308 | margin-bottom: 0; } 309 | 310 | .language { 311 | border-left: 5px solid #cde9f4; } 312 | .language .aside-title { 313 | color: #4b8afb; } 314 | 315 | .aside-warning, .aside-deprecated, .aside-unavailable { 316 | border-left: 5px solid #ff6666; } 317 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 318 | color: #ff0000; } 319 | 320 | .graybox { 321 | border-collapse: collapse; 322 | width: 100%; } 323 | .graybox p { 324 | margin: 0; 325 | word-break: break-word; 326 | min-width: 50px; } 327 | .graybox td { 328 | border: 1px solid #e2e2e2; 329 | padding: 5px 25px 5px 10px; 330 | vertical-align: middle; } 331 | .graybox tr td:first-of-type { 332 | text-align: right; 333 | padding: 7px; 334 | vertical-align: top; 335 | word-break: normal; 336 | width: 40px; } 337 | 338 | .slightly-smaller { 339 | font-size: 0.9em; } 340 | 341 | #footer { 342 | position: relative; 343 | top: 10px; 344 | bottom: 0px; 345 | margin-left: 25px; } 346 | #footer p { 347 | margin: 0; 348 | color: #aaa; 349 | font-size: 0.8em; } 350 | 351 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 352 | display: none; } 353 | 354 | html.dash .main-content { 355 | width: 980px; 356 | margin-left: 0; 357 | border: none; 358 | width: 100%; 359 | top: 0; 360 | padding-bottom: 0; } 361 | 362 | html.dash .height-container { 363 | display: block; } 364 | 365 | html.dash .item .token { 366 | margin-left: 0; } 367 | 368 | html.dash .content-wrapper { 369 | width: auto; } 370 | 371 | html.dash #footer { 372 | position: static; } 373 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/docsets/MultiPeer.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/docsets/MultiPeer.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/docsets/MultiPeer.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MultiPeer Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

MultiPeer Docs (97% documented)

17 |

View on GitHub

18 |
19 |
20 |
21 | 26 |
27 |
28 | 67 |
68 |
69 |
70 | 71 |

72 | 73 |

74 | 75 |

Build Status 76 | CocoaPods Version Status 77 | Carthage compatible 78 | doccov 79 | iOS 80 | MacOS 81 | tvOS 82 | Swift 83 | MIT License

84 | 85 |

A wrapper for Apple’s MultipeerConnectivity framework for offline data transmission between Apple devices. This framework makes it easy to automatically connect to multiple nearby devices and share information using either bluetooth or wifi radios.

86 | 87 |
    88 |
  1. Features
  2. 89 |
  3. Integration 90 | 91 |
  4. 96 |
  5. Usage
  6. 97 |
  7. Example
  8. 98 |
  9. License
  10. 99 |
  11. Authors
  12. 100 |
101 |

Features

102 | 103 |
    104 |
  • [x] Supports iOS/macOS/tvOS
  • 105 |
  • [x] Auto Connection
  • 106 |
  • [x] Auto Invitations/Advertising
  • 107 |
  • [x] Send/Receive data via MultipeerConnectivity Framework
  • 108 |
  • [x] Specify data types for easy handling
  • 109 |
110 |

Integration

111 |

CocoaPods

112 | 113 |

You can use CocoaPods to install MultiPeer by adding it to your Podfile:

114 |
pod 'MultiPeer'
115 | 
116 |

Carthage

117 | 118 |

You can use Carthage to install MultiPeer by adding it to your Cartfile:

119 |
github "dingwilson/MultiPeer"
120 | 
121 |

Swift Package Manager

122 | 123 |

For SPM, add the following to your package dependencies:

124 |
.package(url: "https://github.com/dingwilson/MultiPeer.git", .upToNextMinor(from: "0.0.0"))
125 | 
126 |

Usage

127 | 128 |

To get started, import MultiPeer.

129 |
import MultiPeer
130 | 
131 | 132 |

Then, simply initialize MultiPeer with the name of your session (serviceType). There are two modes of connections (advertiser and browser). To utilize both, simply use .autoConnect().

133 |
MultiPeer.instance.initialize(serviceType: "demo-app")
134 | MultiPeer.instance.autoConnect()
135 | 
136 | 137 |

Any data transmitted by MultiPeer will always be accompanied by a numerical “type”, to ensure other peers know what kind of data is being received, and how to properly process it. You can manage this by creating a UInt32 enum, as shown below:

138 |
enum DataType: UInt32 {
139 |   case string = 1
140 |   case image = 2
141 |   // ...
142 | }
143 | 
144 | 145 |

To send data, simply use the .send(object: type:) function:

146 |
MultiPeer.instance.send(object: "Hello World!", type: DataType.string.rawValue)
147 | 
148 | 149 |

To receive data, we must conform to the MultiPeerDelegate protocol:

150 |
func multiPeer(didReceiveData data: Data, ofType type: UInt32, from peerID: MCPeerID) {
151 |   switch type {
152 |     case DataType.string.rawValue:
153 |       let string = data.convert() as! String
154 |       // do something with the received string
155 |       break
156 | 
157 |     case DataType.image.rawValue:
158 |       let image = UIImage(data: data)
159 |       // do something with the received UIImage
160 |       break
161 | 
162 |     default:
163 |       break
164 |   }
165 | }
166 | 
167 | func multiPeer(connectedDevicesChanged devices: [String]) {
168 | }
169 | 
170 | 171 |

Ensure that you set the MultiPeer delegate.

172 |
MultiPeer.instance.delegate = self
173 | 
174 | 175 |

Finally you’ll need to enable incoming / outgoing connections in your entitlements.

176 | 177 |

178 | 179 |

180 | 181 |

Congratulations! You have successfully sent data using MultiPeer! For more detailed information (including details of other functions), please see the docs.

182 |

Example

183 | 184 |

For an example app using MultiPeer, checkout MultiPeer_Sample.

185 |

License

186 | 187 |

MultiPeer is released under an MIT License. See LICENSE for details.

188 |

Authors

189 | 190 | 193 | 194 |

Project heavily inspired by Apple-Signal.

195 | 196 |
197 |
198 | 202 |
203 |
204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {"Typealiases.html#/s:9MultiPeer8MCPeerIDa":{"name":"MCPeerID","abstract":"

MultiPeerDelegate

"},"Protocols/MultiPeerDelegate.html#/s:9MultiPeer0aB8DelegateP05multiB014didReceiveData6ofType4fromy10Foundation0G0V_s6UInt32VSo8MCPeerIDCtF":{"name":"multiPeer(didReceiveData:ofType:from:)","abstract":"

didReceiveData: delegate runs on receiving data from another peer

","parent_name":"MultiPeerDelegate"},"Protocols/MultiPeerDelegate.html#/s:9MultiPeer0aB8DelegateP05multiB023connectedDevicesChangedySaySSG_tF":{"name":"multiPeer(connectedDevicesChanged:)","abstract":"

connectedDevicesChanged: delegate runs on connection/disconnection event in session

","parent_name":"MultiPeerDelegate"},"Protocols/MultiPeerDelegate.html":{"name":"MultiPeerDelegate","abstract":"

Undocumented

"},"Extensions/Data.html#/s:10Foundation4DataV9MultiPeerE7convertypyF":{"name":"convert()","abstract":"

Unarchive data into an object and return as type Any.

","parent_name":"Data"},"Extensions/Data.html#/s:10Foundation4DataV9MultiPeerE02toB06objectACyp_tFZ":{"name":"toData(object:)","abstract":"

Converts an object into Data using NSKeyedArchiver

","parent_name":"Data"},"Extensions/Data.html":{"name":"Data"},"Classes/MultiPeer.html#/s:9MultiPeerAAC8instanceABvpZ":{"name":"instance","abstract":"

Singleton instance - call via MultiPeer.instance

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC8delegateAA0aB8Delegate_pSgvp":{"name":"delegate","abstract":"

Conforms to MultiPeerDelegate: Handles receiving data and changes in connections

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC17connectionTimeoutSdvp":{"name":"connectionTimeout","abstract":"

Amount of time to spend connecting before timeout

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC14availablePeersSayAA0B0CGvp":{"name":"availablePeers","abstract":"

Peers available to connect to

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC14connectedPeersSayAA0B0CGvp":{"name":"connectedPeers","abstract":"

Peers connected to

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC20connectedDeviceNamesSaySSGvp":{"name":"connectedDeviceNames","abstract":"

Names of all connected devices

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC9debugModeSbvp":{"name":"debugMode","abstract":"

Prints out all errors and status updates

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC10initialize11serviceTypeySS_tF":{"name":"initialize(serviceType:)","abstract":"

Initializes the MultiPeer service with a serviceType and the default deviceName

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC10initialize11serviceType10deviceNameySS_SStF":{"name":"initialize(serviceType:deviceName:)","abstract":"

Initializes the MultiPeer service with a serviceType and a custom deviceName

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC13startInvitingyyF":{"name":"startInviting()","abstract":"

HOST: Automatically browses and invites all found devices

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC14startAcceptingyyF":{"name":"startAccepting()","abstract":"

JOIN: Automatically advertises and accepts all invites

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC11autoConnectyyF":{"name":"autoConnect()","abstract":"

HOST and JOIN: Uses both advertising and browsing to connect.

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC12stopInvitingyyF":{"name":"stopInviting()","abstract":"

Stops the invitation process

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC13stopAcceptingyyF":{"name":"stopAccepting()","abstract":"

Stops accepting invites and becomes invisible on the network

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC13stopSearchingyyF":{"name":"stopSearching()","abstract":"

Stops all invite/accept services

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC10disconnectyyF":{"name":"disconnect()","abstract":"

Disconnects from the current session and stops all searching activity

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC3endyyF":{"name":"end()","abstract":"

Stops all invite/accept services, disconnects from the current session, and stops all searching activity

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC11isConnectedSbvp":{"name":"isConnected","abstract":"

Returns true if there are any connected peers

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC4send6object4typeyyp_s6UInt32VtF":{"name":"send(object:type:)","abstract":"

Sends an object (and type) to all connected peers.

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC4send4data4typey10Foundation4DataV_s6UInt32VtF":{"name":"send(data:type:)","abstract":"

Sends Data (and type) to all connected peers.

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)advertiser:didReceiveInvitationFromPeer:withContext:invitationHandler:":{"name":"advertiser(_:didReceiveInvitationFromPeer:withContext:invitationHandler:)","abstract":"

Received invitation

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)advertiser:didNotStartAdvertisingPeer:":{"name":"advertiser(_:didNotStartAdvertisingPeer:)","abstract":"

Error, could not start advertising

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)browser:foundPeer:withDiscoveryInfo:":{"name":"browser(_:foundPeer:withDiscoveryInfo:)","abstract":"

Found a peer, update the list of available peers

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)browser:lostPeer:":{"name":"browser(_:lostPeer:)","abstract":"

Lost a peer, update the list of available peers

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)browser:didNotStartBrowsingForPeers:":{"name":"browser(_:didNotStartBrowsingForPeers:)","abstract":"

Error, could not start browsing

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:peer:didChangeState:":{"name":"session(_:peer:didChange:)","abstract":"

Peer changed state, update all connected peers and send new connection list to delegate connectedDevicesChanged

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didReceiveData:fromPeer:":{"name":"session(_:didReceive:fromPeer:)","abstract":"

Received data, update delegate didRecieveData

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didReceiveStream:withName:fromPeer:":{"name":"session(_:didReceive:withName:fromPeer:)","abstract":"

Received stream

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didStartReceivingResourceWithName:fromPeer:withProgress:":{"name":"session(_:didStartReceivingResourceWithName:fromPeer:with:)","abstract":"

Started receiving resource

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:":{"name":"session(_:didFinishReceivingResourceWithName:fromPeer:at:withError:)","abstract":"

Finished receiving resource

","parent_name":"MultiPeer"},"Classes/MultiPeer.html":{"name":"MultiPeer","abstract":"

Main Class for MultiPeer

"},"Classes.html#/s:9MultiPeer0B0C":{"name":"Peer","abstract":"

Class containing peerID and session state

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}} -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/docsets/MultiPeer.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/MultiPeer.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/docsets/MultiPeer.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dingwilson/MultiPeer/e2ea072710fb566b0070a6b5d78b45c29307c218/docs/img/gh.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MultiPeer Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

MultiPeer Docs (97% documented)

17 |

View on GitHub

18 |
19 |
20 |
21 | 26 |
27 |
28 | 67 |
68 |
69 |
70 | 71 |

72 | 73 |

74 | 75 |

Build Status 76 | CocoaPods Version Status 77 | Carthage compatible 78 | doccov 79 | iOS 80 | MacOS 81 | tvOS 82 | Swift 83 | MIT License

84 | 85 |

A wrapper for Apple’s MultipeerConnectivity framework for offline data transmission between Apple devices. This framework makes it easy to automatically connect to multiple nearby devices and share information using either bluetooth or wifi radios.

86 | 87 |
    88 |
  1. Features
  2. 89 |
  3. Integration 90 | 91 |
  4. 96 |
  5. Usage
  6. 97 |
  7. Example
  8. 98 |
  9. License
  10. 99 |
  11. Authors
  12. 100 |
101 |

Features

102 | 103 |
    104 |
  • [x] Supports iOS/macOS/tvOS
  • 105 |
  • [x] Auto Connection
  • 106 |
  • [x] Auto Invitations/Advertising
  • 107 |
  • [x] Send/Receive data via MultipeerConnectivity Framework
  • 108 |
  • [x] Specify data types for easy handling
  • 109 |
110 |

Integration

111 |

CocoaPods

112 | 113 |

You can use CocoaPods to install MultiPeer by adding it to your Podfile:

114 |
pod 'MultiPeer'
115 | 
116 |

Carthage

117 | 118 |

You can use Carthage to install MultiPeer by adding it to your Cartfile:

119 |
github "dingwilson/MultiPeer"
120 | 
121 |

Swift Package Manager

122 | 123 |

For SPM, add the following to your package dependencies:

124 |
.package(url: "https://github.com/dingwilson/MultiPeer.git", .upToNextMinor(from: "0.0.0"))
125 | 
126 |

Usage

127 | 128 |

To get started, import MultiPeer.

129 |
import MultiPeer
130 | 
131 | 132 |

Then, simply initialize MultiPeer with the name of your session (serviceType). There are two modes of connections (advertiser and browser). To utilize both, simply use .autoConnect().

133 |
MultiPeer.instance.initialize(serviceType: "demo-app")
134 | MultiPeer.instance.autoConnect()
135 | 
136 | 137 |

Any data transmitted by MultiPeer will always be accompanied by a numerical “type”, to ensure other peers know what kind of data is being received, and how to properly process it. You can manage this by creating a UInt32 enum, as shown below:

138 |
enum DataType: UInt32 {
139 |   case string = 1
140 |   case image = 2
141 |   // ...
142 | }
143 | 
144 | 145 |

To send data, simply use the .send(object: type:) function:

146 |
MultiPeer.instance.send(object: "Hello World!", type: DataType.string.rawValue)
147 | 
148 | 149 |

To receive data, we must conform to the MultiPeerDelegate protocol:

150 |
func multiPeer(didReceiveData data: Data, ofType type: UInt32, from peerID: MCPeerID) {
151 |   switch type {
152 |     case DataType.string.rawValue:
153 |       let string = data.convert() as! String
154 |       // do something with the received string
155 |       break
156 | 
157 |     case DataType.image.rawValue:
158 |       let image = UIImage(data: data)
159 |       // do something with the received UIImage
160 |       break
161 | 
162 |     default:
163 |       break
164 |   }
165 | }
166 | 
167 | func multiPeer(connectedDevicesChanged devices: [String]) {
168 | }
169 | 
170 | 171 |

Ensure that you set the MultiPeer delegate.

172 |
MultiPeer.instance.delegate = self
173 | 
174 | 175 |

Finally you’ll need to enable incoming / outgoing connections in your entitlements.

176 | 177 |

178 | 179 |

180 | 181 |

Congratulations! You have successfully sent data using MultiPeer! For more detailed information (including details of other functions), please see the docs.

182 |

Example

183 | 184 |

For an example app using MultiPeer, checkout MultiPeer_Sample.

185 |

License

186 | 187 |

MultiPeer is released under an MIT License. See LICENSE for details.

188 |

Authors

189 | 190 | 193 | 194 |

Project heavily inspired by Apple-Signal.

195 | 196 |
197 |
198 | 202 |
203 |
204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {"Typealiases.html#/s:9MultiPeer8MCPeerIDa":{"name":"MCPeerID","abstract":"

MultiPeerDelegate

"},"Protocols/MultiPeerDelegate.html#/s:9MultiPeer0aB8DelegateP05multiB014didReceiveData6ofType4fromy10Foundation0G0V_s6UInt32VSo8MCPeerIDCtF":{"name":"multiPeer(didReceiveData:ofType:from:)","abstract":"

didReceiveData: delegate runs on receiving data from another peer

","parent_name":"MultiPeerDelegate"},"Protocols/MultiPeerDelegate.html#/s:9MultiPeer0aB8DelegateP05multiB023connectedDevicesChangedySaySSG_tF":{"name":"multiPeer(connectedDevicesChanged:)","abstract":"

connectedDevicesChanged: delegate runs on connection/disconnection event in session

","parent_name":"MultiPeerDelegate"},"Protocols/MultiPeerDelegate.html":{"name":"MultiPeerDelegate","abstract":"

Undocumented

"},"Extensions/Data.html#/s:10Foundation4DataV9MultiPeerE7convertypyF":{"name":"convert()","abstract":"

Unarchive data into an object and return as type Any.

","parent_name":"Data"},"Extensions/Data.html#/s:10Foundation4DataV9MultiPeerE02toB06objectACyp_tFZ":{"name":"toData(object:)","abstract":"

Converts an object into Data using NSKeyedArchiver

","parent_name":"Data"},"Extensions/Data.html":{"name":"Data"},"Classes/MultiPeer.html#/s:9MultiPeerAAC8instanceABvpZ":{"name":"instance","abstract":"

Singleton instance - call via MultiPeer.instance

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC8delegateAA0aB8Delegate_pSgvp":{"name":"delegate","abstract":"

Conforms to MultiPeerDelegate: Handles receiving data and changes in connections

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC17connectionTimeoutSdvp":{"name":"connectionTimeout","abstract":"

Amount of time to spend connecting before timeout

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC14availablePeersSayAA0B0CGvp":{"name":"availablePeers","abstract":"

Peers available to connect to

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC14connectedPeersSayAA0B0CGvp":{"name":"connectedPeers","abstract":"

Peers connected to

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC20connectedDeviceNamesSaySSGvp":{"name":"connectedDeviceNames","abstract":"

Names of all connected devices

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC9debugModeSbvp":{"name":"debugMode","abstract":"

Prints out all errors and status updates

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC10initialize11serviceTypeySS_tF":{"name":"initialize(serviceType:)","abstract":"

Initializes the MultiPeer service with a serviceType and the default deviceName

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC10initialize11serviceType10deviceNameySS_SStF":{"name":"initialize(serviceType:deviceName:)","abstract":"

Initializes the MultiPeer service with a serviceType and a custom deviceName

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC13startInvitingyyF":{"name":"startInviting()","abstract":"

HOST: Automatically browses and invites all found devices

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC14startAcceptingyyF":{"name":"startAccepting()","abstract":"

JOIN: Automatically advertises and accepts all invites

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC11autoConnectyyF":{"name":"autoConnect()","abstract":"

HOST and JOIN: Uses both advertising and browsing to connect.

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC12stopInvitingyyF":{"name":"stopInviting()","abstract":"

Stops the invitation process

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC13stopAcceptingyyF":{"name":"stopAccepting()","abstract":"

Stops accepting invites and becomes invisible on the network

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC13stopSearchingyyF":{"name":"stopSearching()","abstract":"

Stops all invite/accept services

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC10disconnectyyF":{"name":"disconnect()","abstract":"

Disconnects from the current session and stops all searching activity

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC3endyyF":{"name":"end()","abstract":"

Stops all invite/accept services, disconnects from the current session, and stops all searching activity

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC11isConnectedSbvp":{"name":"isConnected","abstract":"

Returns true if there are any connected peers

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC4send6object4typeyyp_s6UInt32VtF":{"name":"send(object:type:)","abstract":"

Sends an object (and type) to all connected peers.

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/s:9MultiPeerAAC4send4data4typey10Foundation4DataV_s6UInt32VtF":{"name":"send(data:type:)","abstract":"

Sends Data (and type) to all connected peers.

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)advertiser:didReceiveInvitationFromPeer:withContext:invitationHandler:":{"name":"advertiser(_:didReceiveInvitationFromPeer:withContext:invitationHandler:)","abstract":"

Received invitation

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)advertiser:didNotStartAdvertisingPeer:":{"name":"advertiser(_:didNotStartAdvertisingPeer:)","abstract":"

Error, could not start advertising

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)browser:foundPeer:withDiscoveryInfo:":{"name":"browser(_:foundPeer:withDiscoveryInfo:)","abstract":"

Found a peer, update the list of available peers

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)browser:lostPeer:":{"name":"browser(_:lostPeer:)","abstract":"

Lost a peer, update the list of available peers

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)browser:didNotStartBrowsingForPeers:":{"name":"browser(_:didNotStartBrowsingForPeers:)","abstract":"

Error, could not start browsing

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:peer:didChangeState:":{"name":"session(_:peer:didChange:)","abstract":"

Peer changed state, update all connected peers and send new connection list to delegate connectedDevicesChanged

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didReceiveData:fromPeer:":{"name":"session(_:didReceive:fromPeer:)","abstract":"

Received data, update delegate didRecieveData

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didReceiveStream:withName:fromPeer:":{"name":"session(_:didReceive:withName:fromPeer:)","abstract":"

Received stream

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didStartReceivingResourceWithName:fromPeer:withProgress:":{"name":"session(_:didStartReceivingResourceWithName:fromPeer:with:)","abstract":"

Started receiving resource

","parent_name":"MultiPeer"},"Classes/MultiPeer.html#/c:@CM@MultiPeer@objc(cs)MultiPeer(im)session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:":{"name":"session(_:didFinishReceivingResourceWithName:fromPeer:at:withError:)","abstract":"

Finished receiving resource

","parent_name":"MultiPeer"},"Classes/MultiPeer.html":{"name":"MultiPeer","abstract":"

Main Class for MultiPeer

"},"Classes.html#/s:9MultiPeer0B0C":{"name":"Peer","abstract":"

Class containing peerID and session state

"},"Classes.html":{"name":"Classes","abstract":"

The following classes are available globally.

"},"Extensions.html":{"name":"Extensions","abstract":"

The following extensions are available globally.

"},"Protocols.html":{"name":"Protocols","abstract":"

The following protocols are available globally.

"},"Typealiases.html":{"name":"Type Aliases","abstract":"

The following type aliases are available globally.

"}} -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/travis/build/dingwilson/MultiPeer/Sources/MultiPeerDelegate.swift", 5 | "line": 19, 6 | "symbol": "MultiPeerDelegate", 7 | "symbol_kind": "source.lang.swift.decl.protocol", 8 | "warning": "undocumented" 9 | } 10 | ], 11 | "source_directory": "/Users/travis/build/dingwilson/MultiPeer" 12 | } --------------------------------------------------------------------------------