├── .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 | [](https://travis-ci.org/dingwilson/MultiPeer)
6 | [](https://cocoapods.org/pods/MultiPeer)
7 | [](https://github.com/Carthage/Carthage)
8 | [](https://wilsonding.com/MultiPeer)
9 | 
10 | 
11 | 
12 | [](https://swift.org)
13 | [](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 |
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.
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().
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:
funcmultiPeer(didReceiveDatadata:Data,ofTypetype:UInt32,frompeerID:MCPeerID){
151 | switchtype{
152 | caseDataType.string.rawValue:
153 | letstring=data.convert()as!String
154 | // do something with the received string
155 | break
156 |
157 | caseDataType.image.rawValue:
158 | letimage=UIImage(data:data)
159 | // do something with the received UIImage
160 | break
161 |
162 | default:
163 | break
164 | }
165 | }
166 |
167 | funcmultiPeer(connectedDevicesChangeddevices:[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.
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.
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().
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:
funcmultiPeer(didReceiveDatadata:Data,ofTypetype:UInt32,frompeerID:MCPeerID){
151 | switchtype{
152 | caseDataType.string.rawValue:
153 | letstring=data.convert()as!String
154 | // do something with the received string
155 | break
156 |
157 | caseDataType.image.rawValue:
158 | letimage=UIImage(data:data)
159 | // do something with the received UIImage
160 | break
161 |
162 | default:
163 | break
164 | }
165 | }
166 |
167 | funcmultiPeer(connectedDevicesChangeddevices:[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.