Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP initial commit for issue 327: Add Swift binding #342

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
88a657d
WIP initial commit for issue 327: Add Swift binding
ScottThomasMiller Sep 9, 2021
5affb16
completed DataFilter.swift
ScottThomasMiller Sep 15, 2021
4623103
Added strongly typed BoardDescription. Improved implementation of Bra…
ScottThomasMiller Sep 19, 2021
6945a3d
phase 1 DataFilter unit tests complete. widespread adoption of enums …
ScottThomasMiller Sep 22, 2021
7b1b065
introducing MLModule.swift, and new BrainFlowCItests.swift.
ScottThomasMiller Sep 24, 2021
14f6654
built brainflow/lib/ using patch from Andrey via Slack
ScottThomasMiller Oct 5, 2021
faed7e1
somehow missed the patched cmake files last time
ScottThomasMiller Oct 5, 2021
7e29b87
fixing build error
ScottThomasMiller Oct 5, 2021
9353313
still trying to fix the build
ScottThomasMiller Oct 5, 2021
bcf8ee3
reorganized into standard Swift package layout
ScottThomasMiller Oct 5, 2021
7193cf8
rebuilt Swift pkg this time using library type
ScottThomasMiller Oct 6, 2021
d416daf
fixed build errors
ScottThomasMiller Oct 6, 2021
ecc5587
removed build artifacts. updated top-level .gitignore
ScottThomasMiller Oct 7, 2021
7abad5e
restoring accidentally deleted binaries
ScottThomasMiller Oct 7, 2021
2ffd587
once again added dylib and swift-package/brainflow/lib/ to .gitignore
ScottThomasMiller Oct 7, 2021
2f4ef9d
fixed tests
ScottThomasMiller Oct 10, 2021
e99372e
Swift Package and build script
ScottThomasMiller May 11, 2022
6f6f7b8
removed trailing space
ScottThomasMiller May 15, 2022
4525f96
fixed and simplified script for building Swift pkg
ScottThomasMiller May 16, 2022
4cd7580
updated docs. reverted to cmake build.
ScottThomasMiller May 24, 2022
8f1811f
examples
ScottThomasMiller May 26, 2022
2b54630
CI tests
ScottThomasMiller Jun 19, 2022
b11e4e2
fixed merge conflicts
ScottThomasMiller Jun 19, 2022
a02aa07
Merge remote-tracking branch 'upstream/master' into swift-bindings
ScottThomasMiller Jun 24, 2022
c2de9da
v5 upgrades
ScottThomasMiller Jun 28, 2022
8ed0bd0
updated Swift bindings for latest version of BF
ScottThomasMiller Mar 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions docs/BuildBrainFlow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,47 @@ Rust
cd brainflow
cargo build --features generate_binding

Swift
-------

The following instructions are tested on a MacBook Pro M1 using Xcode 13.4 and macOS Monterey 12.3.1.

The Xcode build scheme is for destination "My Mac (Rosetta)". The dylibs are universal (built for macos-arm64_x86_64.) The target build architecture is set to $(ARCHS_STANDARD).

High level steps:
- Build the universal dylibs
- Copy the entire BrainFlow package from swift-package into the top level folder of your Xcode project.
- Copy the BrainFlowBindings subfolder from the package into your target app group.
- Set the briding header
- Compile the package
- Set the package dependency
- Build

Assumptions:
- the BrainFlow repo is cloned to /Users/myusername/brainflow
- your Xcode project is located at /Users/myusername/myproject
- within your Xcode project is a group named myapp

Detailed steps:
- in a shell such as iTerm2:

cd /Users/myusername/brainflow/swift-package
./build_package_macOS.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its better to add it to build.py and get rid off shell scripts

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also build.py builds dylibs so it will decrease the number of steps

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will take a look.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the examples, do you want something like this:

>ls -l ./r_package/examples
total 80
-rw-r--r--  1 scottmiller  staff   957 Jun 24 06:17 band_power.R
-rw-r--r--  1 scottmiller  staff   706 Jun 24 06:17 band_power_all.R
-rw-r--r--  1 scottmiller  staff   356 Jun 24 06:17 brainflow_get_data.R
-rw-r--r--  1 scottmiller  staff   555 Jun 24 06:17 denoising.R
-rw-r--r--  1 scottmiller  staff   586 Jun 24 06:17 downsampling.R
-rw-r--r--  1 scottmiller  staff  1090 Jun 24 06:17 eeg_metrics.R
-rw-r--r--  1 scottmiller  staff   384 Jun 24 06:17 markers.R
-rw-r--r--  1 scottmiller  staff   541 Jun 24 06:17 serialization.R
-rw-r--r--  1 scottmiller  staff   708 Jun 24 06:17 signal_filtering.R
-rw-r--r--  1 scottmiller  staff   709 Jun 24 06:17 transforms.R

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, exactly. R is not the best example but you got the idea. Would be better to use python as a reference. You can also find them here https://github.com/brainflow-dev/brainflow/blob/master/docs/Examples.rst(will also need to add swift examples to this page)


- Copy the BrainFlow folder and its entire contents from /Users/myusername/brainflow/swift-package to /Users/myusername/myproject.
- In Xcode select the top-level project and then click File->Add Packages...Add Local...
- Select /Users/myusername/myproject/BrainFlow. Click Open.
- Drag-and-drop the BrainFlowBindings subfolder from myproject/Packages/BrainFLow/Sources into myproject/myapp.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets create a github actions artifacts with all precompiled libraries, example can be found here https://github.com/brainflow-dev/brainflow/actions/runs/2572045283 https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/deploy_cpp_libs.yml#L104

We can trigger all required scripts to build it(build_package_macos.sh that should be moved to build.py) in CI and upload binaries, so other people will not even need to run it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe instead github artifacts we can publish to aws(run_unix workflow does it currently) I can take care of publishing and packaging but need to build package inside CI first

- A dialog will pop up. In the dialog check the "Copy items if needed" box, select "Create groups", and check the box(es) next to your target(s).
- In the build settings of your build target, search for "bridging". Set the Objective-C Bridging Header to "BrainFlow/BrainFlow.h".
- In the scheme selector at the top of the window, select the BrainFlow scheme for "My Mac (Rosetta)".
- Press Command-B (build) to build the package.
- In the General settings of your build target, scroll down to Frameworks, Libraries and Embedded Content.
- Click the plus button and select the BrainFlow package under the Workspace group.
- In the scheme selector at the top of the window, select the myapp scheme for "My Mac (Rosetta)".
- Press Command-B (build) to build the target.


Docker Image
--------------

Expand Down
6 changes: 6 additions & 0 deletions docs/Examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,9 @@ Notebooks
./notebooks/brainflow_mne
./notebooks/denoising
./notebooks/band_power

Swift
------

.. literalinclude:: ../swift-package/BrainFlow/Tests/BrainFlowTests/BrainFlowCITests.swift
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we separate them to match the structure with other languages? all of them have exactly the same code samples and they are separated. also lets add it before notebooks

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

:language: swift
10 changes: 10 additions & 0 deletions docs/UserAPI.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,13 @@ Example:

.. literalinclude:: ../rust_package/brainflow/examples/get_data.rs
:language: rust

Swift API Reference
---------------------

The Swift bindings call the C++ API via Objective-C. The public headers are resolved by Xcode via a bridging header (see build instructions for details.)
Example:

.. literalinclude:: ../swift-package/examples/Headset.swift
:language: swift

Binary file added src/.DS_Store
Binary file not shown.
Binary file added src/board_controller/.DS_Store
Binary file not shown.
Binary file added src/data_handler/.DS_Store
Binary file not shown.
Binary file added src/ml/.DS_Store
Binary file not shown.
Binary file added src/utils/.DS_Store
Binary file not shown.
7 changes: 7 additions & 0 deletions swift-package/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
13 changes: 13 additions & 0 deletions swift-package/BrainFlow/BrainFlow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Created by Scott Miller on 4/9/22.
//

#ifndef BrainFlow_h
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets replace by pragma once, its used in all other headers and lets make it consistent

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

#define BrainFlow_h

#include <board_controller.h>
#include <data_handler.h>
#include <board_info_getter.h>
#include <ml_module.h>

#endif /* BrainFlow_h */
16 changes: 16 additions & 0 deletions swift-package/BrainFlow/Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this file be in the repo?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no

"object": {
"pins": [
{
"package": "swift-numerics",
"repositoryURL": "https://github.com/apple/swift-numerics.git",
"state": {
"branch": null,
"revision": "0a5bc04095a675662cf24757cc0640aa2204253b",
"version": "1.0.2"
}
}
]
},
"version": 1
}
50 changes: 50 additions & 0 deletions swift-package/BrainFlow/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// swift-tools-version:5.3
import PackageDescription

let package = Package(
name: "BrainFlow",
platforms: [
.macOS(.v10_15), .iOS(.v13)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(name: "BrainFlow",
targets: ["BrainFlow", "BoardController", "DataHandler", "MLModule", "BrainBitLib"])
],
dependencies: [
.package(name: "swift-numerics",
url: "https://github.com/apple/swift-numerics.git", .upToNextMajor(from: "1.0.0"))
],
targets: [
.target(
name: "BrainFlow",
dependencies: [.product(name: "Numerics", package: "swift-numerics"),
.target(name: "BoardController")]
),
.binaryTarget(
name: "BoardController",
path: "BoardController.xcframework"
),
.binaryTarget(
name: "DataHandler",
path: "DataHandler.xcframework"
),
.binaryTarget(
name: "MLModule",
path: "MLModule.xcframework"
),
.binaryTarget(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually also there are other binaries, I think we should also all libsimpleble.dylib,musebglib,ganglionbglib and libonnxruntime.dylib

To see some of these libs you need to compile using build.py with --ble and --onnx

name: "BrainBitLib",
path: "BrainBitLib.xcframework"
),
.testTarget(
name: "BrainFlowTests",
dependencies: ["BrainFlow", .product(name: "Numerics", package: "swift-numerics")],
sources: ["BoardShimTests.swift",
"BrainFlowCItests.swift",
"BrainFlowTests.swift",
"DataFilterTests.swift"]
)
]
)

4 changes: 4 additions & 0 deletions swift-package/BrainFlow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# BrainFlow

Swift bindings for the BrainFlow C++ API. Contributed by Scott Miller for Aeris Rising, LLC.

7 changes: 7 additions & 0 deletions swift-package/BrainFlow/Sources/BrainFlow/dummy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is it?

import Foundation

struct dummy {
let num = 1
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// BoardDescription.swift
// a Swift reimagining of BrainFlow's board_description data type
//
// Created by Scott Miller for Aeris Rising, LLC on 8/23/21.
//
import Foundation
//import BrainFlow

struct BoardDescription: Codable, Equatable {
let package_num_channel: Int32
let timestamp_channel: Int32
let accel_channels: [Int32]?
let ecg_channels: [Int32]?
let eeg_channels: [Int32]?
let eeg_names: String?
let emg_channels: [Int32]?
let eog_channels: [Int32]?
let marker_channel: Int32?
let name: String?
let num_rows: Int32?
let sampling_rate: Int32?
let battery_channel: Int32?
let eda_channels: [Int32]?
let gyro_channels: [Int32]?
let ppg_channels: [Int32]?
let resistance_channels: [Int32]?
let temperature_channels: [Int32]?

// decode the input JSON into self:
init(_ descriptionJSON: String) throws {
guard descriptionJSON != "" else {
throw BrainFlowException("Nil board description JSON", .JSON_NOT_FOUND_ERROR)
}

let decoder = JSONDecoder()
let jsonData = Data(descriptionJSON.utf8)

do {
let json = try decoder.decode(type(of: self), from: jsonData)
self = json
} catch {
try? BoardShim.logMessage(.LEVEL_CRITICAL, "board description JSON decoding error:\n \(error)")
throw BrainFlowException("Invalid board description JSON", .NO_SUCH_DATA_IN_JSON_ERROR)
}
}
}
Loading