This tutorial examines the dependencies and permissions needed for the SDK.
Adding MetaWear to a Project
What's Ahead
In this tutorial series, we will tour the SDK by writing small logic layer use case components for an app that collects sensor data.
Next, we’ll train and implement a CoreML activity classifier for writing letters in the air. (Yes, at some point we will YMCA.)

Step 1
If you wish, you can clone a companion demo app, Streamy, that implements the components we build in this series.

Step 2
This tutorial will not cover UI or write every line of an app. Our focus is simply the ins and outs of this SDK.
Streamy happens to use SwiftUI, but this tutorial series is UI framework agnostic.
@main
struct MacApp: App {
private var app: AppDelegate
var body: some Scene {
MainWindowScene(factory: .init(root: app.root))
}
}
@main
struct iOSApp: App {
private var app: AppDelegate
var body: some Scene {
MainScene(factory: .init(root: app.root))
}
}
Step 3
You can also reference the source code of Metabase 5, our easy-mode app for collecting MetaWear sensor data.

Step 4
A final resource for tinkering is using the SDK’s integration test host to write one-off tests.
import XCTest
import Combine
import CoreBluetooth
@testable import MetaWear
@testable import MetaWearCpp
@testable import SwiftCombineSDKTestHost
class ExampleTests: XCTestCase {
func test_DiscoversDeviceInfo() {
TestDevices.useOnly(.metamotionS)
connectNearbyMetaWear(timeout: .read) { metawear, exp, subs in
let sut = metawear.info
XCTAssertEqual(sut.model, .motionS)
...
exp.fulfill()
}
}
func test_Read_LogLength_WhenPopulated() {
connectNearbyMetaWear(timeout: .download, useLogger: false) { metawear, exp, subs in
// Prepare
let log: some MWLoggable = .accelerometer(rate: .hz50, gravity: .g2)
metawear.publish()
.deleteLoggedEntries()
.delay(for: 2, tolerance: 0, scheduler: metawear.bleQueue)
.log(log)
._assertLoggers([log.signalName], metawear: metawear)
.delay(for: 2, tolerance: 0, scheduler: metawear.bleQueue)
// Act
.read(.logLength)
// Assert
.handleEvents(receiveOutput: { output in
XCTAssertGreaterThan(output.value, 1)
})
// Cleanup
.map { _ in metawear }
.command(.resetActivities)
.deleteLoggedEntries()
._sinkNoFailure(&subs, receiveValue: { _ in exp.fulfill() })
}
}
}

Swift Package Manager & Permissions
You can add the Swift Combine SDK to your project using Xcode’s native package dependency manager.

Step 1
In the File menu, choose Add Package… https://github.com/mbientlab/MetaWear-Swift-Combine-SDK
By default, Xcode will update the MetaWear package when minor versions are released, but not for a major release. You can also depend on a branch head or a commit.

Step 2
In this tutorial, we’ll use all frameworks except the one to update device Firmware.

Step 3
Xcode will likely only add these frameworks to one of your targets. Remedy this by tapping the + icon inside the Frameworks, Libraries and Embedded Content
section to add the same frameworks as above.

Step 4
Don’t forget to set the two Info.plist Bluetooth privacy usage descriptions. Your app will not function otherwise.

Step 5
For macOS, match permissions to this screenshot. Ensure App Sandbox -> Bluetooth and iCloud -> CloudKit permissions are enabled.

Step 6
For iOS, we need to check different boxes. Add Background Modes and check Uses Bluetooth LE accessories. Add iCloud, checking Key value storage.


The Core SDK Classes
We’ll use three core classes: Meta
, Meta
, and Meta
.
When debugging, the MWConsole
can visualize Bluetooth communications.

Step 1
Let’s look at how we could wire up the SDK inside an app’s root object.
Step 2
The Meta
abstracts the CoreBluetooth framework for you, finding nearby MetaWear devices. The shared
singleton enables CoreBluetooth to recognize this app and vend it previously used peripherals.
Step 3
To persist names, capabilities, and other data about previously connected MetaWears, the scanner and other SDK components use UserDefaults storage.
FYI - You can inspect the keys used and specify the container at User
. By default, the SDK uses .standard
.
let scanner: MetaWearScanner
private let localDefaults: UserDefaults
init() {
self.scanner = .sharedRestore
Step 4
Apple randomizes Bluetooth device identifiers between devices. It also blocks inspecting advertised MAC addresses.
The Meta
allows you to stably identify MetaWears across devices by saving de-identifying metadata to iCloud key value storage, including a device’s name, serial number, and sensor capabilities. If you choose to use this feature, obtain and manage MetaWears exclusively through the sync store, rather than the Scanner.
Step 5
An iCloud key value store must be synchronized at launch to obtain the latest values.
Step 6
When debugging, you might find the MWConsole
helpful. If you set activate
to true at startup, all MetaWears will report activities (e.g., Bluetooth packets) to the console in Debug mode.
Tip If you wish to log just one MetaWear, assign a delegate conforming to MWConsole
to its log
property.
self.localDefaults = .standard
self.cloudDefaults = .default
MWConsoleLogger.activateConsoleLoggingOnAllMetaWears = true
self.deviceLoader = MetaWeariCloudSyncLoader(localDefaults, cloudDefaults)
self.syncedDevices = .init(scanner: scanner, loader: deviceLoader)
import Foundation
class Root {
init() {
}
func start() {
}
}
Connecting to MetaWears
Learn how Streamy finds and manages both nearby and cloud-synced MetaWears.