Description
A Swift Package to communicate with a VanMoof S3 & X3 Bike.
VanMoofKit alternatives and similar libraries
Based on the "UI" category.
Alternatively, view VanMoofKit alternatives based on common mentions on social networks and blogs.
-
Animated Tab Bar
:octocat: RAMAnimatedTabBarController is a Swift UI module library for adding animation to iOS tabbar items and icons. iOS library made by @Ramotion -
FSPagerView
FSPagerView is an elegant Screen Slide Library. It is extremely helpful for making Banner View、Product Show、Welcome/Guide Pages、Screen/ViewController Sliders. -
JTAppleCalendar
The Unofficial Apple iOS Swift Calendar View. Swift calendar Library. iOS calendar Control. 100% Customizable -
Pagemenu
A paging menu controller built from other view controllers placed inside a scroll view (like Spotify, Windows Phone, Instagram) -
SwiftEntryKit
SwiftEntryKit is a presentation library for iOS. It can be used to easily display overlays within your iOS apps. -
SwipeCellKit
Swipeable UITableViewCell/UICollectionViewCell based on the stock Mail.app, implemented in Swift. -
Alerts Pickers
Advanced usage of UIAlertController and pickers based on it: Telegram, Contacts, Location, PhotoLibrary, Country, Phone Code, Currency, Date... -
Scrollable-GraphView
An adaptive scrollable graph view for iOS to visualise simple discrete datasets. Written in Swift. -
ESTabBarController
:octocat: ESTabBarController is a Swift model for customize UI, badge and adding animation to tabbar items. Support lottie! -
Material Components for iOS
[In maintenance mode] Modular and customizable Material Design UI components for iOS -
SideMenu
Simple side/slide menu control for iOS, no code necessary! Lots of customization. Add it to your project in 5 minutes or less. -
NotificationBanner
The easiest way to display highly customizable in app notification banners in iOS -
ActiveLabel
UILabel drop-in replacement supporting Hashtags (#), Mentions (@) and URLs (http://) written in Swift -
SlideMenuControllerSwift
iOS Slide Menu View based on Google+, iQON, Feedly, Ameba iOS app. It is written in pure swift. -
PopupDialog
A simple, customizable popup dialog for iOS written in Swift. Replaces UIAlertController alert style. -
TLYShyNavBar
DISCONTINUED. Unlike all those arrogant UINavigationBar, this one is shy and humble! Easily create auto-scrolling navigation bars! -
StarWars.iOS
This component implements transition animation to crumble view-controller into tiny pieces. -
KMNavigationBarTransition
A drop-in universal library helps you to manage the navigation bar styles and makes transition animations smooth between different navigation bar styles while pushing or popping a view controller for all orientations. And you don't need to write any line of code for it, it all happens automatically. -
Whisper
:mega: Whisper is a component that will make the task of display messages and in-app notifications simple. It has three different views inside -
CircleMenu
:octocat: ⭕️ CircleMenu is a simple, elegant UI menu with a circular layout and material design animations. Swift UI library made by @Ramotion -
RazzleDazzle
A simple keyframe-based animation framework for iOS, written in Swift. Perfect for scrolling app intros. -
PaperOnboarding
:octocat: PaperOnboarding is a material design UI slider. Swift UI library by @Ramotion
InfluxDB - Power Real-Time Data Analytics at Scale
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of VanMoofKit or a related project?
README
VanMoofKit
A Swift Package to communicate with a VanMoof S3 & X3 Bike 🚲
import VanMoofKit
let vanMoof = VanMoof()
try await vanMoof.login(
username: "[email protected]",
password: "********"
)
let bikes = try await vanMoof.bikes()
for bike in bikes {
try await bike.connect()
try await bike.playSound()
}
Disclaimer
VanMoofKit is not an official library of VanMoof B.V. This Swift Package makes certain features of the bike accessible which may be illegal to use in certain jurisdictions. As this library hasn't reached an official stable version some features are not yet available or may not working as expected.
Features
- [x] Access the VanMoof Web API through native Swift Code 👨💻👩💻
- [x] Establish a bluetooth connection to a VanMoof Bike using async/await 🚲.
- [x] Easily change the configuration of the bike such as bell sound, power level, light mode and many more ⚙️
- [x] Combine support to react to changes of certain functions ⛓️
Example
Check out the example application to see VanMoofKit in action. Simply open the Example/Example.xcodeproj
and run the "Example" scheme.
Installation
Swift Package Manager
To integrate using Apple's Swift Package Manager, add the following as a dependency to your Package.swift
:
dependencies: [
.package(url: "https://github.com/SvenTiigi/VanMoofKit.git", from: "0.0.1")
]
Or navigate to your Xcode project then select Swift Packages
, click the “+” icon and search for VanMoofKit
.
Info.plist
As the VanMoofKit is using the CoreBluetooth
framework to establish a BLE connection to a bike the NSBluetoothAlwaysUsageDescription
key needs to be added to the Info.plist of your application.
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Establishing a bluetooth connection to your VanMoof Bike.</string>
VanMoof
To retrieve bikes of a VanMoof account you first need to initialize an instance of VanMoof
.
let vanMoof = VanMoof()
To authenticate simply call the login
function on instance of a VanMoof
object.
try await vanMoof.login(
username: "[email protected]",
password: "********"
)
The login credentials aren't persisted or logged they are only used to authenticate against the VanMoof API.
Make use of the vanMoof.isAuthenticated
property to check if the user is already logged in.
if vanMoof.isAuthenticated {
// ...
}
The login produces a VanMoof.Token
which is automatically stored in an instance of a VanMoofTokenStore
. You can configure the persistence of the token when creating an instance of VanMoof
.
let vanMoof = VanMoof(
// Specify an instance which conforms to the `VanMoofTokenStore` protocol.
// Predefined implementations:
// - KeychainVanMoofTokenStore
// - UbiquitousVanMoofTokenStore (iCloud Key-Value Store)
// - UserDefaultsVanMoofTokenStore
// - InMemoryVanMoofTokenStore
tokenStore: UserDefaultsVanMoofTokenStore()
)
In default the
UserDefaultsVanMoofTokenStore
will be used to store theVanMoof.Token
.
After the login has succeeded you can retrieve the user profile and the associated bikes.
// Retrieve the user
let user: VanMoof.User = try await vanMoof.user()
print("Available Bikes", user.bikes)
// If you want to directly retrieve the bikes call:
let bikes: [VanMoof.Bike] = try vanMoof.bikes()
To logout the current user call:
vanMoof.logout()
Note: Logging out an user has no effect on any available VanMoof.Bike instance. It is the developer responsibility to terminate any open connection to a VanMoof.Bike.
VanMoof.Bike 🚲
Some information such as the name of the bike, frame number and more are available without an active connection to the bike. You can access those information via the bike.details
property.
let details: VanMoof.Bike.Details = bike.details
print(details.name)
print(details.macAddress)
print(details.frameNumber)
// Or access the details properties directly
// (powered by the @dynamicMemberLookup attribute)
print(bike.name, bike.macAddress, bike.frameNumber)
Connection
To establish a connection to a VanMoof.Bike
call:
// Try to connect to the bike
try await bike.connect()
You can retrieve the current state of the connection in the following way.
// Switch on connectionState
switch bike.connectionState {
case .disconnected:
print("Disconnected")
case .discovering:
print("Disconvering / Searching")
case .connecting:
print("Connecting")
case .connected:
print("Connected")
case .disconnecting:
print("Disconnecting")
}
// Or make use of convience properties such as:
let isConnected: Bool = bike.isConnected
let isDisconnected: Bool = bike.isDiconnected
// Alternatively you can use a Publisher
bike.connectionStatePublisher
.sink { connectionState in
// ...
}
// Or a specialized publisher which only emits connection errors
bike.connectionErrorPublisher
.sink { connectionError in
// ...
}
Additionally, you can monitor the signal strength of the connection via:
// Retrieve the current signal strength
let signalStrength: VanMoof.Bike.SignalStrength = try await bike.signalStrength
// A Publisher that emits the current signal strength in a given update interval
bike.signalStrengthPublisher(
updateInterval: 5
).sink { signalStrength in
// ...
}
If you wish to terminate the connection simply call:
// Disconnect from the bike
try await bike.disconnect()
ModuleState
switch try await bike.moduleState {
case .on:
break
case .off:
break
default:
break
}
bike.moduleStatePublisher.sink { moduleState in
// ...
}
try await bike.set(moduleState: .on)
// Alias for setting the module state to on
try await bike.wakeUp()
LockState
switch try await bike.lockState {
case .unlocked:
break
case .locked:
break
case .awaitingUnlock:
break
}
bike.lockStatePublisher.sink { lockState in
// ...
}
// Unlock Bike
try await bike.unlock()
Battery Level
let batteryLevel = try await bike.batteryLevel
bike.batteryLevelPublisher.sink { batteryLevel in
// ...
}
Battery State
switch try await bike.batteryState {
case .notCharging;
break
case .charging:
break
}
bike.batteryStatePublisher.sink { batteryState in
// ...
}
Speed Limit
switch try await bike.speedLimit {
case .europe:
print("Europe 25 km/h")
case .unitedStates:
print("United States 32 km/h")
case .japan:
print("Japan 24 km/h")
}
bike.speedLimitPublisher.sink { speedLimit in
// ...
}
⚠️ Changing the speed limit may be illegal in certain jurisdictions.
try await bike.set(speedLimit: .unitedStates)
Power Level
switch try await bike.powerLevel {
case .off:
break
case .one:
break
case .two:
break
case .three:
break
case .four:
break
}
bike.powerLevelPublisher.sink { powerLevel in
// ...
}
try await bike.set(powerLevel: .four)
Light Mode
switch try await bike.lightMode {
case .auto:
break
case .alwaysOn:
break
case .off:
break
}
bike.lightModePublisher.sink { lightMode in
// ...
}
try await bike.set(lightMode: .alwaysOn)
Bell Sound
switch try await bike.bellSound {
case .sonar:
break
case .bell:
break
case .party:
break
case .foghorn:
break
}
bike.bellSoundPublisher.sink { bellSound in
// ...
}
try await bike.set(bellSound: .party)
Play Sound
try await bike.play(sound: .scrollingTone)
try await bike.play(sound: .beepPositive)
try await bike.play(sound: .alarmStageOne)
Sound Volume
// An integer in the range from 0 to 100
let soundVolume: Int = try await bike.soundVolume
Total Distance
let totalDistance: VanMoof.Bike.Distance = try await bike.totalDistance
bike.totalDistancePublisher.sink { totalDistance in
// ...
}
Unit System
switch try await bike.unitSystem {
case .metric:
break
case .imperial:
break
}
bike.unitSystemPublisher.sink { unitSystem in
// ...
}
try await bike.set(unitSystem: .metric)
Firmware Versions
let bikeFirmwareVersion: String = try await bike.firmwareVersion
let bleChipFirmwareVersion: String = try await bike.bleChipFirmwareVersion
let eShifterFirmwareVersion: String = try await bike.eShifterFirmwareVersion
Credits
AES ECB Crypto
VanMoof Bike Bluetooth API Reverse Engineering
GitHub Workflow Files and Issue Templates
License
VanMoofKit
Copyright (c) 2022 Sven Tiigi [email protected]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*Note that all licence references and agreements mentioned in the VanMoofKit README section above
are relevant to that project's source code only.