On this Core Bluetooth tutorial, you’ll learn to uncover, hook up with, and retrieve information from appropriate gadgets like a chest-worn coronary heart charge sensor.

Replace be aware: This tutorial has been written for Xcode 9 & iOS 11 by Jawwad Ahmad. The unique Goal-C model was written by Steven Daniel.

Given the proliferation of devices in in the present day’s world, communication between these gadgets can result in utilizing these devices, and the knowledge supplied by these devices, in more practical methods. To this finish, Apple has launched the Core Bluetooth framework, which might talk with many real-world gadgets resembling coronary heart charge sensors, digital thermostats, and exercise tools. In the event you can hook up with it by way of BLE (Bluetooth Low Power) wi-fi expertise, the Core Bluetooth framework can hook up with it.

On this tutorial, you’ll study the important thing ideas of the Core Bluetooth framework and methods to uncover, hook up with, and retrieve information from appropriate gadgets. You’ll use these expertise to construct a coronary heart charge monitoring software that communicates with a Bluetooth coronary heart charge sensor.

The center charge sensor we use on this tutorial is the Polar H7 Bluetooth Coronary heart Fee Sensor, however some other Bluetooth coronary heart charge sensor ought to work as properly.

First, let’s take a second to go over a couple of Bluetooth-specific phrases: centrals, peripherals, providers, and traits.

Centrals and Peripherals

A Bluetooth system could be both a central or peripheral:

  • Central: the thing that receives the info from a Bluetooth system.
  • Peripheral: the Bluetooth system that publishes information to be consumed by different gadgets.

On this tutorial, the iOS system would be the central, receiving coronary heart charge information from the peripheral.

Promoting Packets

Bluetooth peripherals broadcast a few of the information they’ve within the type of promoting packets. These packets can include info such because the peripheral’s title and important performance. They’ll additionally embrace additional info associated to what sort of information the peripheral can present.

The job of the central is to scan for these promoting packets, establish any peripherals it finds related, and hook up with particular person gadgets for extra info.

Providers and Traits

Promoting packets are very small and can’t include quite a lot of info. To share extra information, a central wants to connect with a peripheral.

The peripheral’s information is organized into providers and traits:

  • Service: a group of information and related behaviors describing a selected perform or characteristic of a peripheral. For instance, a coronary heart charge sensor has a Coronary heart Fee service. A peripheral can have multiple service.
  • Attribute: offers additional particulars a couple of peripheral’s service. For instance, the Coronary heart Fee service incorporates a Coronary heart Fee Measurement attribute that incorporates the beats per minute information. A service can have multiple attribute. One other attribute that the Coronary heart Fee service could have is Physique Sensor Location, which is solely a string that describes the meant physique location of the sensor.

Every service and attribute is represented by a UUID which could be both a 16-bit or a 128-bit worth.

Getting Began

First, obtain the starter mission for this tutorial. It’s a quite simple app to show the meant physique sensor location and coronary heart charge. The starter mission has placeholders for the info to be retrieved from the center charge monitor.

Starter Project

Be aware: The iOS Simulator doesn’t help Bluetooth – you’ll must construct and run on an precise system.

Earlier than you begin coding, you’ll must set the Workforce to your mission. Choose the mission root within the mission navigator, choose the HeartRateMonitor goal, and within the Common ▸ Signing part, set the Workforce to your Apple ID. (You may additionally must set the bundle Identifier for the mission to one thing else …)

As soon as that’s carried out, run the app. In the event you see an error, it’s worthwhile to navigate to the Settings app in your system, go to Common ▸ Machine Manangement, and Belief your Apple ID. After that, it is possible for you to to run the app from Xcode in your iOS system.

Getting ready for Core Bluetooth

You’ll first import the Core Bluetooth framework. Open HRMViewController.swift and add the next:

import CoreBluetooth

A lot of the work within the Core Bluetooth framework will likely be carried out by way of delegate strategies. The central is represented by CBCentralManager and its delegate is CBCentralManagerDelegate. CBPeripheral is the peripheral and its delegate is CBPeripheralDelegate.

You’ll lean on Xcode that will help you add the required strategies. The very first thing you’ll do is add conformance to CBCentralManagerDelegate, however you’ll use Xcode’s fix-it characteristic so as to add the required protocol methodology.

Add the next extension to the tip of HRMViewController.swift, exterior the category:

extension HRMViewController: CBCentralManagerDelegate {

}

You must see an Xcode error seem shortly. Click on on the crimson dot to broaden the message after which click on Repair to have Xcode add the required protocol methodology for you.

Xcode ought to have added centralManagerDidUpdateState(_:) for you. Add an empty change assertion to the strategy to deal with the assorted states of the central supervisor:

change central.state {

}

In a second, you’ll see an error stating that change should be exhaustive. Click on on the crimson dot and click on on Repair to have Xcode add the entire circumstances for you:

Xcode will helpfully add the next code:

You possibly can change the placeholders with acceptable values from the next code or simply change the entire change assertion in case you choose to chop and paste:

change central.state {
  case .unknown:
    print("central.state is .unknown")
  case .resetting:
    print("central.state is .resetting")
  case .unsupported:
    print("central.state is .unsupported")
  case .unauthorized:
    print("central.state is .unauthorized")
  case .poweredOff:
    print("central.state is .poweredOff")
  case .poweredOn:
    print("central.state is .poweredOn")
}

In the event you construct and run at this level, nothing will likely be printed to the console since you haven’t really created the CBCentralManager.

Add the next occasion variable proper beneath the bodySensorLocationLabel outlet:

var centralManager: CBCentralManager!

Subsequent, add the next to the start of viewDidLoad() to initialize the brand new variable:

centralManager = CBCentralManager(delegate: self, queue: nil)

Construct and run, and you need to see the next printed to the console:

central.state is .poweredOn

Be aware: In the event you had Bluetooth turned off in your system, you’ll see central.state is .poweredOff as an alternative. On this case, activate Bluetooth and run the app once more.

See also  Credits

Now that the central has been powered on, the subsequent step is for the central to find the center charge monitor. In Bluetooth-speak, the central might want to scan for peripherals.

Scanning for Peripherals

For most of the strategies you’ll be including, as an alternative of providing you with the strategy title outright, I’ll provide you with a touch on methods to discover the strategy that you’d want. On this case, you need to see if there’s a methodology on centralManager with which you’ll be able to scan.

On the road after initializing centralManager, begin typing centralManager.scan and see if you’ll find a way you need to use:

The scanForPeripherals(withServices: [CBUUID]?, choices: [String: Any]?) methodology appears promising. Choose it, use nil for the withServices: parameter and take away the choices: parameter because you received’t be utilizing it. You must find yourself with the next code:

centralManager.scanForPeripherals(withServices: nil)

Construct and run. Check out the console and be aware the API MISUSE message:

API MISUSE: <CBCentralManager: 0x1c4462180> can solely settle for this command whereas within the powered on state

Nicely, that actually is sensible proper? You’ll need to scan after central.state has been set to .poweredOn.

Transfer the scanForPeripherals line out of viewDidLoad() and into centralManagerDidUpdateState(_:), proper below the .poweredOn case. You must now have the next for the .poweredOn case:

case .poweredOn:
  print("central.state is .poweredOn")
  centralManager.scanForPeripherals(withServices: nil)
}

Construct and run, after which examine the console. The API MISUSE message is not there. Nice! However has it discovered the center charge sensor?

It in all probability has; you merely must implement a delegate methodology to verify that it has discovered the peripheral. In Bluetooth-speak, discovering a peripheral is named discovering, so the delegate methodology you’ll need to use could have the phrase uncover in it.

Beneath the tip of the centralManagerDidUpdateState(_:) methodology, begin typing the phrase uncover. The strategy is just too lengthy to learn absolutely, however the methodology beginning with centralManager would be the appropriate one:

Choose that methodology and change the code placeholder with print(peripheral).

You must now have the next:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,
                    advertisementData: [String: Any], rssi RSSI: NSNumber) {
  print(peripheral)
}

Construct and run; you need to see quite a lot of Bluetooth gadgets relying on what number of devices you’ve gotten in your neighborhood:

<CBPeripheral: 0x1c4105fa0, identifier = D69A9892-...21E4, title = Your Laptop Title, state = disconnected>
<CBPeripheral: 0x1c010a710, identifier = CBE94B09-...0C8A, title = Tile, state = disconnected>
<CBPeripheral: 0x1c010ab00, identifier = FCA1F687-...DC19, title = Your Apple Watch, state = disconnected>
<CBPeripheral: 0x1c010ab00, identifier = BB8A7450-...A69B, title = Polar H7 DCB69F17, state = disconnected>

One among them ought to be your coronary heart charge monitor, so long as you’re carrying it and have a sound coronary heart charge.

Scanning for Peripherals with Particular Providers

Wouldn’t or not it’s higher in case you might solely scan for coronary heart charge screens, since that’s the solely sort of peripheral you’re at the moment enthusiastic about? In Bluetooth-speak, you solely need to scan for peripherals that present the Coronary heart Fee service. To do this, you’ll want the UUID for the Coronary heart Fee service. Seek for coronary heart charge within the listing of providers on the Bluetooth providers specification web page and be aware the UUID for it; 0x180D.

From the UUID, you’ll create a CBUUID object and cross it to scanForPeripherals(withServices:), which really takes an array. So, on this case, will probably be an array with a single CBUUID object, because you’re solely within the coronary heart charge service.

Add the next to the highest of the file, proper beneath the import statements:

let heartRateServiceCBUUID = CBUUID(string: "0x180D")

Replace the scanForPeripherals(withServices: nil) line to the next:

centralManager.scanForPeripherals(withServices: [heartRateServiceCBUUID])

Construct and run, and you need to now solely see your coronary heart charge sensor being found:

<CBPeripheral: 0x1c0117220, identifier = BB8A7450-...A69B, title = Polar H7 DCB69F17, state = disconnected>
<CBPeripheral: 0x1c0117190, identifier = BB8A7450-...A69B, title = Polar H7 DCB69F17, state = disconnected>

Subsequent you’ll retailer a reference to the center charge peripheral after which can cease scanning for additional peripherals.

Add a heartRatePeripheral occasion variable of sort CBPeripheral on the prime, proper after the centralManager variable:

var heartRatePeripheral: CBPeripheral!

As soon as the peripheral is discovered, retailer a reference to it and cease scanning. In centralManager(_:didDiscover:advertisementData:rssi:), add the next after print(peripheral) :

heartRatePeripheral = peripheral
centralManager.stopScan()

Construct and run; you need to now see the peripheral printed simply as soon as.

<CBPeripheral: 0x1c010ccc0, identifier = BB8A7450-...A69B, title = Polar H7 DCB69F17, state = disconnected>

Connecting to a Peripheral

To acquire information from a peripheral you’ll want to connect with it. Proper beneath centralManager.stopScan(), begin typing centralManager.join and you need to see join(peripheral: CBPeripheral, choices: [String: Any]?) seem:

Choose it, use heartRatePeripheral for the primary parameter and delete the choices: parameter in order that you find yourself with the next:

centralManager.join(heartRatePeripheral)

Nice! Not solely have you ever found your coronary heart charge sensor, however you’ve gotten related to it as properly! However how are you going to affirm that you’re really related? There should be a delegate methodology for this with the phrase join in it. Proper after the centralManager(_:didDiscover:advertisementData:rssi:) delegate methodology, sort join and choose centralManager(_:didConnect:):

Exchange the code placeholder as follows:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
  print("Connected!")
}

Construct and run; you need to see Related! printed to the console confirming that you’re certainly related to it.

Related!

Discovering a Peripheral’s Providers

Now that you simply’re related, the subsequent step is to find the providers of the peripheral. Sure, despite the fact that you particularly requested a peripheral with the center charge service and you understand that this specific peripheral helps this, you continue to want to find the service to make use of it.

After connecting, name discoverServices(nil) on the peripheral to find its providers:

heartRatePeripheral.discoverServices(nil)

You possibly can cross in UUIDs for the providers right here, however for now you’ll uncover all out there providers to see what else the center charge monitor can do.

Construct and run and be aware the 2 API MISUSE messages within the console:

API MISUSE: Discovering providers for peripheral <CBPeripheral: 0x1c010f6f0, ...> whereas delegate is both nil or doesn't implement peripheral:didDiscoverServices:
API MISUSE: <CBPeripheral: 0x1c010f6f0, ...> can solely settle for instructions whereas within the related state

The second message signifies that the peripheral can solely settle for instructions whereas it’s related. The problem is that you simply initiated a connection to the peripheral, however didn’t look ahead to it to complete connecting earlier than you referred to as discoverServices(_:)!

Transfer heartRatePeripheral.discoverServices(nil) into centralManager(_:didConnect:) proper beneath print("Connected!"). centralManager(_:didConnect:) ought to now appear like this:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
  print("Connected!")
  heartRatePeripheral.discoverServices(nil)
}

Construct and run. Now you need to solely see the opposite API MISUSE message which is:

API MISUSE: Discovering providers for peripheral <CBPeripheral: ...> whereas delegate is both nil or doesn't implement peripheral:didDiscoverServices:

The Core Bluetooth framework is indicating that you simply’ve requested to find providers, however you haven’t carried out the peripheral(_:didDiscoverServices:) delegate methodology.

See also  [Guide] FIRE STICK Replace King/o Root with SuperSU

The title of the strategy tells you that this can be a delegate methodology for the peripheral, so that you’ll want to adapt to CBPeripheralDelegate to implement it.

Add the next extension to the tip of the file:

extension HRMViewController: CBPeripheralDelegate {

}

Xcode doesn’t provide so as to add methodology stubs for this since there are not any required delegate strategies.

Inside the extension, sort uncover and choose peripheral(_:didDiscoverServices:):

Be aware that this methodology doesn’t present you an inventory of found providers, solely that a number of providers has been found by the peripheral. It’s because the peripheral object has a property which provides you an inventory of providers. Add the next code to the newly added methodology:

guard let providers = peripheral.providers else { return }

for service in providers {
  print(service)
}

Construct and run, and examine the console. You received’t see something printed and, in actual fact, you’ll nonetheless see the API MISUSE methodology. Are you able to guess why?

It’s since you haven’t but pointed heartRatePeripheral at its delegate. Add the next after heartRatePeripheral = peripheral in centralManager(_:didDiscover:advertisementData:rssi:):

heartRatePeripheral.delegate = self

Construct and run, and also you’ll see the peripheral’s providers printed to the console:

<CBService: 0x1c046f280, isPrimary = YES, UUID = Coronary heart Fee>
<CBService: 0x1c046f5c0, isPrimary = YES, UUID = Machine Info>
<CBService: 0x1c046f600, isPrimary = YES, UUID = Battery>
<CBService: 0x1c046f680, isPrimary = YES, UUID = 6217FF4B-FB31-1140-AD5A-A45545D7ECF3>

To get simply the providers you’re enthusiastic about, you may cross the CBUUIDs of these providers into discoverServices(_:). Because you solely want the Coronary heart Fee service, replace the discoverServices(nil) name in centralManager(_:didConnect:) as follows:

heartRatePeripheral.discoverServices([heartRateServiceCBUUID])

Construct and run, and you need to solely see the Coronary heart Fee service printed to the console.

<CBService: 0x1c046f280, isPrimary = YES, UUID = Coronary heart Fee>

Discovering a Service’s Traits

The center charge measurement is a attribute of the center charge service. Add the next assertion proper beneath the print(service) line in peripheral(_:didDiscoverServices:):

print(service.traits ?? "characteristics are nil")

Construct and run to see what’s printed to the console:

traits are nil

To acquire the traits of a service, you’ll must explicitly request the invention of the service’s traits:

Exchange the print assertion you simply added with the next:

peripheral.discoverCharacteristics(nil, for: service)

Construct and run, and examine the console for some API MISUSE steerage on what ought to be carried out subsequent:

API MISUSE: Discovering traits on peripheral <CBPeripheral: 0x1c0119110, ...> whereas delegate is both nil or doesn't implement peripheral:didDiscoverCharacteristicsForService:error:

You’ll want to implement peripheral(_:didDiscoverCharacteristicsFor:error:). Add the next after peripheral(_:didDiscoverServices:) to print out the attribute objects:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService,
                error: Error?) {
  guard let traits = service.traits else { return }

  for attribute in traits {
    print(attribute)
  }
}

Construct and run. You must see the next printed to the console:

<CBCharacteristic: 0x1c00b0920, UUID = 2A37, properties = 0x10, worth = (null), notifying = NO>
<CBCharacteristic: 0x1c00af300, UUID = 2A38, properties = 0x2, worth = (null), notifying = NO>

This reveals you that the center charge service has two traits. In case you are utilizing a sensor aside from the Polar H7, you might even see extra traits. One with UUID 2A37, and the opposite with 2A38. Which one in every of these is the center charge measurement attribute? Yow will discover out by trying to find each numbers within the traits part of the Bluetooth specification.

On the Bluetooth specification web page, you’ll see that 2A37 represents Coronary heart Fee Measurement and 2A38 represents Physique Sensor Location.

Add constants for these on the prime of the file, beneath the road for heartRateServiceCBUUID. Including the 0x prefix to the UUID is optionally available:

let heartRateMeasurementCharacteristicCBUUID = CBUUID(string: "2A37")
let bodySensorLocationCharacteristicCBUUID = CBUUID(string: "2A38")

Every attribute has a property referred to as properties of sort CBCharacteristicProperties and is an OptionSet. You possibly can view the various kinds of properties within the documentation for CBCharacteristicProperties, however right here you’ll solely concentrate on two: .learn and .notify. You’ll must get hold of every attribute’s worth in a distinct method.

Checking a Attribute’s Properties

And the next code in peripheral(_:didDiscoverCharacteristicsFor:error:) after the print(attribute) to see the attribute’s properties:

if attribute.properties.incorporates(.learn) {
  print("(characteristic.uuid): properties contains .read")
}
if attribute.properties.incorporates(.notify) {
  print("(characteristic.uuid): properties contains .notify")
}

Construct and run. Within the console you’ll see:

2A37: properties incorporates .notify
2A38: properties incorporates .learn

The 2A37 attribute — the center charge measurement — will notify you when its worth updates, so that you’ll must subscribe to obtain updates from it. The 2A38 attribute — the physique sensor location — allows you to learn from it immediately…though not fairly immediately. You’ll see what I imply within the subsequent part.

Acquiring the Physique Sensor Location

Since getting the physique sensor location is simpler than getting the center charge, you’ll try this first.

Within the code you simply added, after print("(characteristic.uuid): properties contains .read"), add the next:

peripheral.readValue(for: attribute)

So the place is the worth learn to? Construct and run for some additional steerage from the Xcode console:

API MISUSE: Studying attribute worth for peripheral <CBPeripheral: 0x1c410b760, ...> whereas delegate is both nil or doesn't implement peripheral:didUpdateValueForCharacteristic:error:

The Core Bluetooth framework is telling you that you simply’ve requested to learn a attribute’s worth, however haven’t carried out peripheral(_:didUpdateValueFor:error:). At first look, this looks like a way that you simply’d must implement just for traits that may notify you of an replace, resembling the center charge. Nonetheless, you additionally must implement it for values that you simply learn. The learn operation is asynchronous: You request a learn, and are then notified when the worth has been learn.

Add the strategy to the CBPeripheralDelegate extension:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor attribute: CBCharacteristic,
                error: Error?) {
  change attribute.uuid {
    case bodySensorLocationCharacteristicCBUUID:
      print(attribute.worth ?? "no value")
    default:
      print("Unhandled Characteristic UUID: (characteristic.uuid)")
  }
}

Construct and run; you need to see a “1 bytes” message printed to the console, which is the kind of message you’d see if you print a Knowledge object immediately.

See also  QlikView Addict: Qlik UltraEdit

Decoding the Binary Knowledge of a Attribute’s Worth

To grasp methods to interpret the info from a attribute, you must seek advice from the Bluetooth specification for the attribute. Click on on the Physique Sensor Location hyperlink on the Bluetooth traits web page which is able to take you to the next web page:

The specification reveals you that Physique Sensor Location is represented by an 8-bit worth, so there are 255 prospects, and solely 0 – 6 are used at current. Based mostly on the specification, add the next helper methodology to the tip of the CBPeripheralDelegate extension:

non-public func bodyLocation(from attribute: CBCharacteristic) -> String {
  guard let characteristicData = attribute.worth,
    let byte = characteristicData.first else { return "Error" }

  change byte {
    case 0: return "Other"
    case 1: return "Chest"
    case 2: return "Wrist"
    case 3: return "Finger"
    case 4: return "Hand"
    case 5: return "Ear Lobe"
    case 6: return "Foot"
    default:
      return "Reserved for future use"
  }
}

Because the specification signifies the info consists of a single byte, you may name first on a Knowledge object to get its first byte.

Exchange peripheral(_:didUpdateValueFor:error:) with the next:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor attribute: CBCharacteristic,
                error: Error?) {
  change attribute.uuid {
    case bodySensorLocationCharacteristicCBUUID:
      let bodySensorLocation = bodyLocation(from: attribute)
      bodySensorLocationLabel.textual content = bodySensorLocation
    default:
      print("Unhandled Characteristic UUID: (characteristic.uuid)")
  }
}

This makes use of your new helper perform to replace the label on the UI. Construct and run, and also you’ll see the physique sensor location displayed:

Acquiring the Coronary heart Fee Measurement

Lastly, the second you’ve been ready for!

The center charge measurement attribute’s properties contained .notify, so that you would wish to subscribe to obtain updates from it. The strategy you’ll must name appears a bit bizarre: it’s setNotifyValue(_:for:).

Add the next to peripheral(_:didDiscoverCharacteristicsFor:error:) after print("(characteristic.uuid): properties contains .notify"):

peripheral.setNotifyValue(true, for: attribute)

Construct and run, and also you’ll see quite a lot of “Unhandled Characteristic UUID: 2A37” messages printed out:

Unhandled Attribute UUID: 2A37
Unhandled Attribute UUID: 2A37
Unhandled Attribute UUID: 2A37
Unhandled Attribute UUID: 2A37
Unhandled Attribute UUID: 2A37
Unhandled Attribute UUID: 2A37

Congratulations! Inside that attribute’s worth is your coronary heart charge. The specification for the center charge measurement is a little more complicated than that for the physique sensor location. Have a look: coronary heart charge measurement attribute:

The primary byte incorporates quite a lot of flags, and the primary bit the primary byte signifies if the center charge measurement is an 8-bit worth or a 16-bit worth. If the primary bit is a 0 then the center charge worth format is UINT8, i.e. an 8-bit quantity, and if the primary byte is about to 1, the center charge worth format is UINT16, i.e. a 16-bit quantity.

The explanation for that is that normally, your coronary heart charge hopefully received’t go above 255 beats per minute, which could be represented in 8 bits. Within the distinctive case that your coronary heart charge does go over 255 bpm, then you definitely’d want an extra byte to symbolize the center charge. Though you’d then be coated for as much as 65,535 bpm!

So now you may decide if the center charge is represented by one or two bytes. The primary byte is reserved for varied flags, so the center charge will likely be present in both the second byte or the second and third bytes. You possibly can inform that the flags are contained in a single byte for the reason that Format column reveals 8bit for it.

Be aware that the final column, with the title Requires, reveals C1 when the worth of the bit is 0, and a C2 when the worth of the bit is 1.

Scroll right down to the C1 and C2 fields, which you’ll see instantly after the specification for the primary byte:

Add the next helper methodology to the tip of the CBPeripheralDelegate extension to acquire the center charge worth from the attribute:

non-public func heartRate(from attribute: CBCharacteristic) -> Int {
  guard let characteristicData = attribute.worth else { return -1 }
  let byteArray = [UInt8](characteristicData)

  let firstBitValue = byteArray[0] & 0x01
  if firstBitValue == 0 {
    // Coronary heart Fee Worth Format is within the 2nd byte
    return Int(byteArray[1])
  } else {
    // Coronary heart Fee Worth Format is within the 2nd and third bytes
    return (Int(byteArray[1]) << 8) + Int(byteArray[2])
  }
}

From attribute.worth, which is an object of sort Knowledge, you create an array of bytes. Relying on the worth of the primary bit within the first byte, you both take a look at the second byte, i.e. byteArray[1], otherwise you decide what the worth can be by combining the second and third bytes. The second byte is shifted by 8 bits, which is equal to multiplying by 256. So the worth on this case is (second byte worth * 256) + (third byte worth).

Lastly, add one other case assertion above the default case in peripheral(_:didUpdateValueFor:error:) to learn the center charge from the attribute.

case heartRateMeasurementCharacteristicCBUUID:
  let bpm = heartRate(from: attribute)
  onHeartRateReceived(bpm)

onHeartRateReceived(_:) updates the UI along with your coronary heart charge.

Construct and run your app, and you need to lastly see your coronary heart charge seem. Strive some mild train and watch your coronary heart charge rise!

The place to Go From Right here?

Right here is the finished last mission with the entire code from the above tutorial.

On this tutorial, you realized concerning the Core Bluetooth framework and the way you need to use it to connect with and procure information from Bluetooth gadgets.

You additionally might want to check out the Bluetooth Finest Practices part of Apple’s Power Effectivity Information for iOS Apps.

Need to study iBeacons? If that’s the case try our iBeacon Tutorial with iOS and Swift tutorial.

In case you have any questions or feedback, please be a part of the dialogue beneath!

raywenderlich.com Weekly

The raywenderlich.com publication is the simplest option to keep up-to-date on every little thing it’s worthwhile to know as a cell developer.

Get a weekly digest of our tutorials and programs, and obtain a free in-depth electronic mail course as a bonus!

Leave a Reply

Your email address will not be published.