Skip to content

Commit

Permalink
Add PeripheralReadViewController
Browse files Browse the repository at this point in the history
  • Loading branch information
Mikołaj Piechocki committed Oct 30, 2020
1 parent 603b7a4 commit f12d57c
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 14 deletions.
36 changes: 34 additions & 2 deletions ExampleApp/ExampleApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
/* Begin PBXBuildFile section */
8D4839A2254AAC0900266106 /* CharacteristicsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4839A1254AAC0900266106 /* CharacteristicsViewController.swift */; };
8D61CC8B254C321200952B2B /* CharacteristicInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D61CC8A254C321200952B2B /* CharacteristicInfo.swift */; };
8D61CC92254C479900952B2B /* PeripheralViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D61CC91254C479900952B2B /* PeripheralViewController.swift */; };
8D61CC96254C47BF00952B2B /* PeripheralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D61CC95254C47BF00952B2B /* PeripheralView.swift */; };
8D61CC9A254C4A7800952B2B /* PeripheralReadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D61CC99254C4A7800952B2B /* PeripheralReadViewController.swift */; };
8D61CC9E254C4AA000952B2B /* PeripheralReadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D61CC9D254C4AA000952B2B /* PeripheralReadView.swift */; };
8D72C58A2539C65000456D1A /* RxBluetoothKit in Frameworks */ = {isa = PBXBuildFile; productRef = 8D72C5892539C65000456D1A /* RxBluetoothKit */; };
8D72C58F2539C87300456D1A /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D72C58E2539C87300456D1A /* WelcomeView.swift */; };
8D76F72B2546B5D200FF4DDB /* CentralServicesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D76F72A2546B5D200FF4DDB /* CentralServicesViewController.swift */; };
Expand All @@ -34,6 +38,10 @@
/* Begin PBXFileReference section */
8D4839A1254AAC0900266106 /* CharacteristicsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacteristicsViewController.swift; sourceTree = "<group>"; };
8D61CC8A254C321200952B2B /* CharacteristicInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacteristicInfo.swift; sourceTree = "<group>"; };
8D61CC91254C479900952B2B /* PeripheralViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralViewController.swift; sourceTree = "<group>"; };
8D61CC95254C47BF00952B2B /* PeripheralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralView.swift; sourceTree = "<group>"; };
8D61CC99254C4A7800952B2B /* PeripheralReadViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralReadViewController.swift; sourceTree = "<group>"; };
8D61CC9D254C4AA000952B2B /* PeripheralReadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralReadView.swift; sourceTree = "<group>"; };
8D72C58E2539C87300456D1A /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
8D76F72A2546B5D200FF4DDB /* CentralServicesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CentralServicesViewController.swift; sourceTree = "<group>"; };
8D76F7342546C4D900FF4DDB /* CentralServiceCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CentralServiceCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -79,6 +87,24 @@
path = CentralServiceChacacteristics;
sourceTree = "<group>";
};
8D61CC90254C478200952B2B /* Peripheral */ = {
isa = PBXGroup;
children = (
8D61CC91254C479900952B2B /* PeripheralViewController.swift */,
8D61CC95254C47BF00952B2B /* PeripheralView.swift */,
);
path = Peripheral;
sourceTree = "<group>";
};
8D61CC98254C4A6800952B2B /* PeripheralRead */ = {
isa = PBXGroup;
children = (
8D61CC99254C4A7800952B2B /* PeripheralReadViewController.swift */,
8D61CC9D254C4AA000952B2B /* PeripheralReadView.swift */,
);
path = PeripheralRead;
sourceTree = "<group>";
};
8D72C5812539C32100456D1A /* Application */ = {
isa = PBXGroup;
children = (
Expand All @@ -102,11 +128,13 @@
8D72C5832539C34400456D1A /* Screens */ = {
isa = PBXGroup;
children = (
8D61CC98254C4A6800952B2B /* PeripheralRead */,
8D896D502542FFA900FD5FE5 /* Central */,
8D896D492542FCB100FD5FE5 /* CentralList */,
8D4839A0254AABDC00266106 /* CentralServiceChacacteristics */,
8D76F7292546B5C500FF4DDB /* CentralServices */,
8D896D502542FFA900FD5FE5 /* Central */,
8D72C5862539C3BD00456D1A /* CentralSpecific */,
8D896D492542FCB100FD5FE5 /* CentralList */,
8D61CC90254C478200952B2B /* Peripheral */,
8D72C5872539C3C700456D1A /* PeripheralUpdate */,
8D72C58D2539C86600456D1A /* Welcome */,
);
Expand Down Expand Up @@ -287,10 +315,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8D61CC92254C479900952B2B /* PeripheralViewController.swift in Sources */,
8DF924C3254AE4F40027627D /* Error+Printable.swift in Sources */,
8D896D552542FFE000FD5FE5 /* CentralView.swift in Sources */,
8D61CC8B254C321200952B2B /* CharacteristicInfo.swift in Sources */,
8D4839A2254AAC0900266106 /* CharacteristicsViewController.swift in Sources */,
8D61CC9E254C4AA000952B2B /* PeripheralReadView.swift in Sources */,
8D61CC96254C47BF00952B2B /* PeripheralView.swift in Sources */,
8D76F7352546C4DA00FF4DDB /* CentralServiceCell.swift in Sources */,
8DB15AA9253D804A00DECF92 /* CentralSpecificView.swift in Sources */,
8DA476AA25399CFA00B79A0A /* WelcomeViewController.swift in Sources */,
Expand All @@ -299,6 +330,7 @@
8DA476A625399CFA00B79A0A /* AppDelegate.swift in Sources */,
8D896D522542FFB300FD5FE5 /* CentralViewController.swift in Sources */,
8D896D472542D7C500FD5FE5 /* AlertPresenter.swift in Sources */,
8D61CC9A254C4A7800952B2B /* PeripheralReadViewController.swift in Sources */,
8D76F72B2546B5D200FF4DDB /* CentralServicesViewController.swift in Sources */,
8D896D5825430E7200FD5FE5 /* CentralListCell.swift in Sources */,
8D896D4B2542FCE400FD5FE5 /* CentralListViewController.swift in Sources */,
Expand Down
57 changes: 57 additions & 0 deletions ExampleApp/ExampleApp/Screens/Peripheral/PeripheralView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import UIKit

class PeripheralView: UIView {

init() {
super.init(frame: .zero)
backgroundColor = .systemBackground
setupLayout()
}

required init?(coder: NSCoder) { nil }

// MARK: - Subviews

let updateButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Update", for: .normal)
button.setImage(UIImage(systemName: "sun.min"), for: .normal)
return button
}()

let readButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Read", for: .normal)
button.setImage(UIImage(systemName: "phone.fill.arrow.up.right"), for: .normal)
return button
}()

let writeButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Write", for: .normal)
button.setImage(UIImage(systemName: "phone.fill.arrow.down.left"), for: .normal)
return button
}()

let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 20.0
return stackView
}()

// MARK: - Private

private func setupLayout() {
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
[updateButton, readButton, writeButton].forEach(stackView.addArrangedSubview)

NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
stackView.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, constant: -32)
])
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import UIKit

class PeripheralViewController: UIViewController {

init() {
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) { nil }

// MARK: - View

private(set) lazy var peripheralView = PeripheralView()

override func loadView() {
view = peripheralView
}

override func viewDidLoad() {
super.viewDidLoad()

peripheralView.updateButton.addTarget(self, action: #selector(handleUpdateButton), for: .touchUpInside)
peripheralView.readButton.addTarget(self, action: #selector(handleReadButton), for: .touchUpInside)
peripheralView.writeButton.addTarget(self, action: #selector(handleWriteButton), for: .touchUpInside)
}

// MARK: - Private

@objc private func handleUpdateButton() {
let controller = PeripheralUpdateViewController()
navigationController?.pushViewController(controller, animated: true)
}

@objc private func handleReadButton() {
let controller = PeripheralReadViewController()
navigationController?.pushViewController(controller, animated: true)
}

@objc private func handleWriteButton() {}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import UIKit

class PeripheralReadView: UIView {

init() {
super.init(frame: .zero)
backgroundColor = .systemBackground
setupLayout()
}

required init?(coder: NSCoder) { nil }

// MARK: - Subviews

let serviceUuidTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Service UUID"
return textField
}()

let characteristicUuidTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Characteristic UUID"
return textField
}()

let valueTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Value (String)"
return textField
}()

let advertiseButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Advertise", for: .normal)
button.setImage(UIImage(systemName: "wave.3.right"), for: .normal)
return button
}()

let stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 20.0
return stackView
}()

// MARK: - Private

private func setupLayout() {
stackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
[serviceUuidTextField, characteristicUuidTextField, valueTextField, advertiseButton].forEach(stackView.addArrangedSubview)

NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
stackView.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, constant: -32)
])
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import CoreBluetooth
import RxBluetoothKit
import RxSwift
import UIKit

class PeripheralReadViewController: UIViewController {

init() {
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) { nil }

// MARK: - View

private(set) lazy var peripheralReadView = PeripheralReadView()

override func loadView() {
view = peripheralReadView
}

override func viewDidLoad() {
super.viewDidLoad()

peripheralReadView.advertiseButton.addTarget(self, action: #selector(handleAdvertiseButton), for: .touchUpInside)
}

// MARK: - Private

private let disposeBag = DisposeBag()
private lazy var manager = PeripheralManager()
private var characteristic: CBMutableCharacteristic?
private var advertisement: Disposable?
private var isAdvertising = false {
didSet {
let text = isAdvertising ? "Stop Advertising" : "Advertise"
peripheralReadView.advertiseButton.setTitle(text, for: .normal)
}
}

@objc private func handleAdvertiseButton() {
isAdvertising ? handleAdvertisingStop() : handleAdvertisingStart()
}

private func handleAdvertisingStart() {
guard let serviceUuidString = peripheralReadView.serviceUuidTextField.text,
let characteristicUuidString = peripheralReadView.characteristicUuidTextField.text,
let value = peripheralReadView.valueTextField.text else { return }

let service = createService(uuidString: serviceUuidString)
let characteristic = createCharacteristic(uuidString: characteristicUuidString, value: value)
service.characteristics = [characteristic]

startAdvertising(service: service)
self.characteristic = characteristic
}

private func handleAdvertisingStop() {
advertisement?.dispose()
advertisement = nil
characteristic = nil
isAdvertising.toggle()
}

private func createService(uuidString: String) -> CBMutableService {
let serviceUuid = CBUUID(string: uuidString)
return CBMutableService(type: serviceUuid, primary: true)
}

private func createCharacteristic(uuidString: String, value: String) -> CBMutableCharacteristic {
let characteristicUuid = CBUUID(string: uuidString)
return CBMutableCharacteristic(
type: characteristicUuid,
properties: [.read],
value: value.data(using: .utf8),
permissions: [.readable]
)
}

private func startAdvertising(service: CBMutableService) {
let managerIsOn = manager.observeStateWithInitialValue()
.filter { $0 == .poweredOn }

advertisement = Observable.combineLatest(managerIsOn, Observable.just(manager)) { $1 }
.flatMap { $0.add(service) }
.flatMap { [manager] in manager.startAdvertising($0.advertisingData) }
.subscribe(
onNext: { [weak self] in
print("advertising started! \($0)")
self?.isAdvertising.toggle()
},
onError: { [weak self] in
AlertPresenter.presentError(with: $0.printable, on: self?.navigationController)
}
)
}

}
Loading

0 comments on commit f12d57c

Please sign in to comment.