Core Bluetooth in watchOS Tutorial
Learn how to communicate with external BLE devices from within watchOS in this Core Bluetooth tutorial! By Audrey Tam.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Core Bluetooth in watchOS Tutorial
30 mins
- Getting Started
- What is Core Bluetooth?
- Overview
- Central Manager
- Peripheral Manager
- Central Manager Delegate Protocol
- Peripheral Manager Delegate Protocol
- Peripheral Delegate Protocol
- watchOS vs iOS
- Building the Watch App
- Creating the Interface
- Reduce the Amount of Text to Send
- Copy-Pasting and Editing CentralViewController code
- Where to Go From Here?
Central Manager
A central manager’s main jobs are:
- If Bluetooth LE is available and turned on, the central manager scans for peripherals.
- If a peripheral’s signal is in range, it connects to the peripheral. It also discovers services and characteristics, which it may display to the user to select from, subscribes to characteristics, or requests to read or write a characteristic’s value.
Central Manager Methods & Properties:
- Initialize with delegate, queue and optional options.
- Connect to a peripheral, with options,
- Retrieve known peripherals (array of UUIDs) or connected peripherals (array of service UUIDs).
- Scan for peripherals with services and options, or stop scanning.
-
Properties:
delegate
,isScanning
Peripheral Manager
A peripheral manager’s main jobs are to manage and advertise the services in the GATT database of the peripheral device. You would implement this for an Apple device acting as a peripheral. Non-Apple accessories have their own manager APIs. Most of the sample BLE apps you can find online use non-Apple accessories like Arduino.
If Bluetooth LE is available and turned on, the peripheral manager sets up characteristics and services. And it can respond when a central device subscribes to a characteristic, requests to read or write a characteristic value, or unsubscribes from a characteristic.
Peripheral Manager Methods & Properties:
- Initialize with delegate, queue and optional options.
- Start or stop advertising peripheral manager data.
-
updateValue(_:for:onSubscribedCentrals:)
-
respond(_:withResult:)
- Add or remove services.
-
setDesiredConnectionLatency(_:for:)
-
Properties:
delegate
,isAdvertising
Central Manager Delegate Protocol
Methods in this protocol indicate availability of the central manager, and monitor discovering and connecting to peripherals. Follow along in the CBCentralManagerDelegate
extension of CentralViewController.swift, as you work through this list:
-
centralManagerDidUpdateState(_:)
is the only required method. If the central ispoweredOn
— Bluetooth LE is available and turned on — you should start scanning for peripherals. You can also handle the casespoweredOff
,resetting
,unauthorized
,unknown
andunsupported
, but you must not issue commands to the central manager when it isn’t powered on. -
When the central manager discovers a peripheral,
centralManager(_:didDiscover:advertisementData:rssi:)
should save a local copy of the peripheral. Check the received signal strength indicator (RSSI) to see if the peripheral’s signal is strong enough: -22dB is good, but two iOS devices placed right next to each other produce a much lower RSSI, often below -35dB. If the peripheral’s RSSI is acceptable, try to connect to it with the central manager’sconnect(_:options:)
method. -
If the connection attempt fails, you can check the error in the delegate method
centralManager(_:didFailToConnect:error:)
. If the error is something transient, you can callconnect(_:options:)
again. -
When the connection attempt succeeds, implement
centralManager(_:didConnect:)
to stop scanning, reset characteristic values, set the peripheral’sdelegate
property, then call the peripheral’sdiscoverServices(_:)
method. The argument is an array of service UUIDs that your app is interested in. After this, it’s up to the peripheral delegate protocol to discover characteristics of the services. -
In
centralManager(_:didDisconnectPeripheral:error:)
, you can clean up, then start scanning again.
Peripheral Manager Delegate Protocol
Methods in this protocol indicate availability of the peripheral manager, verify advertising, and monitor read, write and subcription requests from central devices. Follow along in the CBPeripheralManagerDelegate
extension of PeripheralViewController.swift, as you work through this list:
-
peripheralManagerDidUpdateState(_:)
is the only required method. You handle the same cases as the correspondingcentralManagerDidUpdateState(_:)
. If the peripheral ispoweredOn
, you should create the peripheral’s services, and their characteristics. -
peripheralManagerDidStartAdvertising(_:error:)
is called when the peripheral manager starts advertising the peripheral’s data. -
When the central subscribes to a characteristic, by enabling notifications,
peripheralManager(_:central:didSubscribeTo:)
should start sending the characteristic’s value.
-
When the central disables notifications for a characteristic, you can implement
peripheralManager(_:central:didUnsubscribeFrom:)
to stop sending updates of the characteristic’s value. -
To send a characteristic’s value,
sendData()
uses the peripheral manager methodupdateValue(_:for:onSubscribedCentrals:)
. This method returnsfalse
if the transmit queue is full. When the transmit queue has space, the peripheral manager callsperipheralManagerIsReady(toUpdateSubscribers:)
. You should implement this delegate method to resend the value. -
The central can send read or write requests, which the peripheral handles with
peripheralManager(_:didReceiveRead:)
orperipheralManager(_:didReceiveWrite:)
. When implementing these methods, you should call the peripheral manager methodperipheral.respond(to:withResult:)
exactly once. The sample app implements onlyperipheralManager(_:didReceiveWrite:)
; reading the text data is accomplished by subscribing totextCharacteristic
.
Peripheral Delegate Protocol
A peripheral delegate can respond when a central device discovers its services or characteristics, or requests to read a characteristic, or when a characteristic’s value is updated. It can also respond when a central device writes a characteristic’s value, or disconnects a peripheral. Follow along in the CBPeripheralDelegate
extension of CentralViewController.swift, as you work through this list:
-
The sample app just checks the error in
peripheral(_:didDiscoverServices:)
, but some apps might present a list ofperipheral.services
for the user to select from. -
And similarly for
peripheral(_:didDiscoverCharacteristicsFor:error:)
. -
When the peripheral manager updates a value that the central subscribed to, or requested to read, implement
peripheral(_:didUpdateValueFor:error:)
to use that value in your app. The sample app collects the chunks, then displays the complete text in the view controller’s text view. -
Implement
peripheral(_:didUpdateNotificationStateFor:error:)
to handle the central device enabling or disabling notifications for a characteristic. The sample app just logs the information. -
There’s a runtime warning if you don’t implement
peripheral(_:didModifyServices:)
, so I added this stub.
watchOS vs iOS
iOS apps can be central or peripheral, and can continue using CoreBluetooth in the background.
watchOS and tvOS both rely on Bluetooth as their main system input, so Core Bluetooth has restrictions, to ensure system activities can run. Both can be only the central device, and can use at most two peripherals at a time. Peripherals are disconnected when the app is suspended.And the minimum interval between connections is 30ms, instead of 15ms for iOS and macOS.
Now finally, you’re going to build the Watch app!
Building the Watch App
As you’ve done many times already, select Xcode\File\New\Target… and choose watchOS\WatchKit App:
Name the product BT_WatchKit_App, uncheck Include Notification Scene, and select Finish:
There are now three targets: TextMeMapMe, BT_WatchKit_App and BT_WatchKit_App Extension. Check that all three have the same team.
Creating the Interface
Open BT_WatchKit_App/Interface.storyboard, and drag two buttons, two labels, and a menu onto the scene. Set the background color of the buttons to different colors, and set their titles to wait…:
Select the two buttons, then select Editor\Embed in\Horizontal Group. Set each button’s width to 0.5 Relative to Container, and leave Height Size To Fit Content:
Set each label’s Font to Footnote, and Lines to 0, and set the second label’s Text to Transferred text appears here:
Set the Menu Item‘s Title to Reset, with Image Repeat:
Open the assistant editor, and create outlets (textButton
, mapButton
, statusLabel
, textLabel
) and actions (textMe
, mapMe
, resetCentral
) in InterfaceController.swift.