Bike Sharing Guide
Simple guide for implementing bike sharing functionality using the MOSA SDK. This guide focuses on SDK method calls from the mobile app's perspective.
Overview
Bike sharing allows users to rent bikes from sharing stations and return them later. The flow involves:
- Renting: User unlocks a bike from a sharing station
- Returning: User locks the bike back at a sharing station
Renting a Bike
Step 1: Request Unlock Key
Request a rentBikeUnlock key for the sharing area:
- Android
- iOS
- Flutter
lifecycleScope.launch {
val unlockKey = sdk.requestUnlockKey(
type = MOSADockUnlockKeyType.RentBikeUnlock,
areaId = "area-123"
)
if (unlockKey != null) {
rentBike(unlockKey)
}
}
Task {
do {
let unlockKey = try await sdk.requestUnlockKey(
type: .rentBikeUnlock,
areaId: "area-123",
userId: "user-123"
)
await rentBike(unlockKey)
} catch {
showError("Failed to request unlock key: \(error)")
}
}
try {
final unlockKey = await mosaDockSdk.requestUnlockKey(
type: MosaDockUnlockKeyType.rentBikeUnlock,
areaId: 'area-123',
userId: 'user-123',
);
await rentBike(unlockKey);
} catch (e) {
showError('Failed to request unlock key: $e');
}
Step 2: Execute Unlock Operation
Call unlockSmartLock() with the unlock key and sharing location type:
- Android
- iOS
- Flutter
private fun rentBike(unlockKey: MOSADockKey) {
lifecycleScope.launch {
sdk.unlockSmartLock(
unlockKey = unlockKey,
gfaLocationType = MOSADockLocationType.SHARING,
callback = object : MOSADockSDKCallback {
override fun onNFCScanCompleted(mosaTag: MosaTag) {
// NFC scan completed
}
override fun onBleConnecting() {
// BLE connecting
}
override fun onBleConnected() {
// BLE connected
}
override fun onKeySent() {
// Key sent to dock
}
override fun onDockUnlocked() {
// Dock unlocked
}
override fun onDockChainRemoved() {
// Chain removed - bike ready to take, start rental timer
}
override fun onError(error: MosaError) {
// Handle error
}
// Implement other callbacks...
}
)
}
}
func rentBike(_ unlockKey: UnlockKey) async {
do {
try await sdk.unlockSmartLock(
with: unlockKey,
location: .sharing,
delegate: self
)
} catch {
showError("Rent operation failed: \(error)")
}
}
// Delegate callbacks
func onNFCScanCompleted(_ tag: MosaTag) {
// NFC scan completed
}
func onBleConnecting() {
// BLE connecting
}
func onBleConnected() {
// BLE connected
}
func onKeySent() {
// Key sent to dock
}
func onDockUnlocked() {
// Dock unlocked
}
func onDockChainRemoved() {
// Chain removed - bike ready to take, start rental timer
}
func onError(_ error: MosaError) {
// Handle error
}
Future<void> rentBike(MosaDockKey unlockKey) async {
try {
await mosaDockSdk.handleClientUnlock(
unlockKey: unlockKey,
locationType: MosaDockLocationType.sharing.value,
);
} catch (e) {
showError('Rent operation failed: $e');
}
}
// Callback handling
mosaDockSdk.dockCallbackEvents.listen((event) {
switch (event.type) {
case DockCallbackType.nfcScanCompleted:
// NFC scan completed
break;
case DockCallbackType.bleConnecting:
// BLE connecting
break;
case DockCallbackType.bleConnected:
// BLE connected
break;
case DockCallbackType.keySent:
// Key sent to dock
break;
case DockCallbackType.dockUnlocked:
// Dock unlocked
break;
case DockCallbackType.dockChainRemoved:
// Chain removed - bike ready to take, start rental timer
break;
case DockCallbackType.error:
// Handle error
break;
default:
break;
}
});
Callback Sequence for Renting
The expected callback sequence:
onNFCScanCompleted- NFC tag readonBleConnecting- BLE connection startedonBleConnected- BLE connectedonKeySent- Unlock key sent to dockonDockUnlocked- Dock unlockedonDockChainRemoved- Chain removed (bike ready to take, start rental timer)
Returning a Bike
Step 1: Request Lock Key
Request a rentBikeLock key. The SDK uses the existing session from the previous unlock operation:
- Android
- iOS
- Flutter
lifecycleScope.launch {
val unlockKey = sdk.requestUnlockKey(
type = MOSADockUnlockKeyType.RentBikeLock,
areaId = "area-123" // areaId from session
)
if (unlockKey != null) {
returnBike(unlockKey)
}
}
Task {
do {
let unlockKey = try await sdk.requestUnlockKey(
type: .rentBikeLock,
areaId: "area-123", // areaId from session
userId: "user-123"
)
await returnBike(unlockKey)
} catch {
showError("Failed to request lock key: \(error)")
}
}
try {
final unlockKey = await mosaDockSdk.requestUnlockKey(
type: MosaDockUnlockKeyType.rentBikeLock,
areaId: 'area-123', // areaId from session
userId: 'user-123',
);
await returnBike(unlockKey);
} catch (e) {
showError('Failed to request lock key: $e');
}
Step 2: Execute Lock Operation
Call unlockSmartLock() with the lock key and sharing location type:
- Android
- iOS
- Flutter
private fun returnBike(unlockKey: MOSADockKey) {
lifecycleScope.launch {
sdk.unlockSmartLock(
unlockKey = unlockKey,
gfaLocationType = MOSADockLocationType.SHARING,
callback = object : MOSADockSDKCallback {
override fun onNFCScanCompleted(mosaTag: MosaTag) {
// NFC scan completed
}
override fun onBleConnecting() {
// BLE connecting
}
override fun onBleConnected() {
// BLE connected
}
override fun onKeySent() {
// Key sent to dock
}
override fun onDockUnlocked() {
// Dock unlocked
}
override fun onDockChainInserted() {
// Chain inserted - bike locked, stop rental timer
}
override fun onDockSecured() {
// Dock secured - rental complete
}
override fun onError(error: MosaError) {
// Handle error
}
// Implement other callbacks...
}
)
}
}
func returnBike(_ unlockKey: UnlockKey) async {
do {
try await sdk.unlockSmartLock(
with: unlockKey,
location: .sharing,
delegate: self
)
} catch {
showError("Return operation failed: \(error)")
}
}
// Delegate callbacks
func onNFCScanCompleted(_ tag: MosaTag) {
// NFC scan completed
}
func onBleConnecting() {
// BLE connecting
}
func onBleConnected() {
// BLE connected
}
func onKeySent() {
// Key sent to dock
}
func onDockUnlocked() {
// Dock unlocked
}
func onDockChainInserted() {
// Chain inserted - bike locked, stop rental timer
}
func onDockSecured() {
// Dock secured - rental complete
}
func onError(_ error: MosaError) {
// Handle error
}
Future<void> returnBike(MosaDockKey unlockKey) async {
try {
await mosaDockSdk.handleClientUnlock(
unlockKey: unlockKey,
locationType: MosaDockLocationType.sharing.value,
);
} catch (e) {
showError('Return operation failed: $e');
}
}
// Callback handling
mosaDockSdk.dockCallbackEvents.listen((event) {
switch (event.type) {
case DockCallbackType.nfcScanCompleted:
// NFC scan completed
break;
case DockCallbackType.bleConnecting:
// BLE connecting
break;
case DockCallbackType.bleConnected:
// BLE connected
break;
case DockCallbackType.keySent:
// Key sent to dock
break;
case DockCallbackType.dockUnlocked:
// Dock unlocked
break;
case DockCallbackType.dockChainInserted:
// Chain inserted - bike locked, stop rental timer
break;
case DockCallbackType.dockSecured:
// Dock secured - rental complete
break;
case DockCallbackType.error:
// Handle error
break;
default:
break;
}
});
Callback Sequence for Returning
The expected callback sequence:
onNFCScanCompleted- NFC tag readonBleConnecting- BLE connection startedonBleConnected- BLE connectedonKeySent- Lock key sent to dockonDockUnlocked- Dock unlockedonDockChainInserted- Chain inserted (bike locked, stop rental timer)onDockSecured- Dock secured (rental complete)
Complete Example
Here's a complete example combining both operations:
- Android
- iOS
- Flutter
class BikeSharingActivity : AppCompatActivity(), MOSADockSDKCallback {
private lateinit var sdk: MOSADockSDK
private var rentalStartTime: Long? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sdk = MOSADockSDK.builder()
.setActivity(this)
.setApiKey("your-api-key")
.setUserId("user-123")
.build()
}
fun rentBike() {
lifecycleScope.launch {
val unlockKey = sdk.requestUnlockKey(
type = MOSADockUnlockKeyType.RentBikeUnlock,
areaId = "area-123"
)
if (unlockKey != null) {
sdk.unlockSmartLock(
unlockKey = unlockKey,
gfaLocationType = MOSADockLocationType.SHARING,
callback = this@BikeSharingActivity
)
}
}
}
fun returnBike() {
lifecycleScope.launch {
val unlockKey = sdk.requestUnlockKey(
type = MOSADockUnlockKeyType.RentBikeLock,
areaId = "area-123"
)
if (unlockKey != null) {
sdk.unlockSmartLock(
unlockKey = unlockKey,
gfaLocationType = MOSADockLocationType.SHARING,
callback = this@BikeSharingActivity
)
}
}
}
override fun onDockChainRemoved() {
// Start rental timer
rentalStartTime = System.currentTimeMillis()
startRentalTimer()
}
override fun onDockSecured() {
// Stop rental timer and calculate rental duration
rentalStartTime?.let { startTime ->
val duration = System.currentTimeMillis() - startTime
endRental(duration)
}
}
// Implement other callbacks...
}
class BikeSharingViewController: UIViewController, MosaDockDelegate {
var sdk: MosaDockSDK!
var rentalStartTime: Date?
override func viewDidLoad() {
super.viewDidLoad()
MosaDockSDK.initialize(
with: self,
apiKey: "your-api-key",
userId: "user-123"
)
sdk = MosaDockSDK.shared
sdk.setDelegate(self)
}
func rentBike() {
Task {
do {
let unlockKey = try await sdk.requestUnlockKey(
type: .rentBikeUnlock,
areaId: "area-123",
userId: "user-123"
)
try await sdk.unlockSmartLock(
with: unlockKey,
location: .sharing,
delegate: self
)
} catch {
showError("Failed: \(error)")
}
}
}
func returnBike() {
Task {
do {
let unlockKey = try await sdk.requestUnlockKey(
type: .rentBikeLock,
areaId: "area-123",
userId: "user-123"
)
try await sdk.unlockSmartLock(
with: unlockKey,
location: .sharing,
delegate: self
)
} catch {
showError("Failed: \(error)")
}
}
}
func onDockChainRemoved() {
// Start rental timer
rentalStartTime = Date()
startRentalTimer()
}
func onDockSecured() {
// Stop rental timer and calculate rental duration
if let startTime = rentalStartTime {
let duration = Date().timeIntervalSince(startTime)
endRental(duration)
}
}
// Implement other delegate methods...
}
class BikeSharingScreen extends StatefulWidget {
@override
_BikeSharingScreenState createState() => _BikeSharingScreenState();
}
class _BikeSharingScreenState extends State<BikeSharingScreen> {
final mosaDockSdk = MosaDockSdk();
DateTime? rentalStartTime;
@override
void initState() {
super.initState();
_setupCallbacks();
mosaDockSdk.initialize(
apiKey: 'your-api-key',
userId: 'user-123',
);
}
void _setupCallbacks() {
mosaDockSdk.dockCallbackEvents.listen((event) {
switch (event.type) {
case DockCallbackType.dockChainRemoved:
// Start rental timer
rentalStartTime = DateTime.now();
startRentalTimer();
break;
case DockCallbackType.dockSecured:
// Stop rental timer and calculate rental duration
if (rentalStartTime != null) {
final duration = DateTime.now().difference(rentalStartTime!);
endRental(duration);
}
break;
default:
break;
}
});
}
Future<void> rentBike() async {
try {
final unlockKey = await mosaDockSdk.requestUnlockKey(
type: MosaDockUnlockKeyType.rentBikeUnlock,
areaId: 'area-123',
userId: 'user-123',
);
await mosaDockSdk.handleClientUnlock(
unlockKey: unlockKey,
locationType: MosaDockLocationType.sharing.value,
);
} catch (e) {
showError('Failed: $e');
}
}
Future<void> returnBike() async {
try {
final unlockKey = await mosaDockSdk.requestUnlockKey(
type: MosaDockUnlockKeyType.rentBikeLock,
areaId: 'area-123',
userId: 'user-123',
);
await mosaDockSdk.handleClientUnlock(
unlockKey: unlockKey,
locationType: MosaDockLocationType.sharing.value,
);
} catch (e) {
showError('Failed: $e');
}
}
}
Required Parameters
Rent Operation
- Key Type:
rentBikeUnlock - Area ID: ID of the sharing area
- Location Type:
SHARING/.sharing/sharing
Return Operation
- Key Type:
rentBikeLock - Area ID: ID of the sharing area (from session)
- Location Type:
SHARING/.sharing/sharing
Rental Timer
For bike sharing, you should track rental duration:
- Start Timer: When
onDockChainRemovedis called (bike taken) - Stop Timer: When
onDockSecuredis called (bike returned) - Display Duration: Show rental duration to user
- Calculate Fee: Use duration to calculate rental fee (if applicable)
Next Steps
- Bike Parking Guide - Guide for bike parking operations
- SDK Callbacks - Understanding callback lifecycle
- API Reference - Complete UnlockKey documentation