Das BeeWi Car war schon unter Xamarin ein spannendes Demo-Projekt. In diesem Artikel zeige ich, wie sich die Steuerung heute mit .NET MAUI umsetzen lässt – mit gemeinsamem UI, plattformspezifischer Bluetooth-Kommunikation für Android und iOS sowie praktischen Tipps für die Migration.
Das BeeWi Car ist ein kleines ferngesteuertes Auto von BeeWi, das sich per Bluetooth mit einer mobilen App steuern lässt. Wer unsere älteren Beiträge zu Xamarin.Android und Xamarin.iOS kennt, erkennt das Grundprinzip sofort wieder: Befehle wie Vorwärts, Rückwärts, Links und Rechts werden aus der App an das Fahrzeug gesendet. Der Unterschied ist heute vor allem die technische Basis. Statt zwei getrennte Projekte zu pflegen, lässt sich die Umsetzung mit .NET MAUI in einem modernen Single-Project-Ansatz realisieren.
Mit .NET MAUI können wir das gemeinsame UI und die zentrale Logik an einer Stelle pflegen, während die Bluetooth- bzw. Zubehöranbindung weiterhin plattformspezifisch umgesetzt wird. Genau das macht den Ansatz besonders interessant für Teams, die bestehende Apps modernisieren oder neue Cross-Plattform Apps entwickeln möchten. Wer sich generell mit solchen Projekten beschäftigt, findet auf unserer Seite zur Cross-Plattform App Entwicklung weitere Informationen. Ebenso ist .NET MAUI heute der richtige Weg für Teams, die ihre bestehende Xamarin-Lösung modernisieren möchten, etwa im Rahmen einer Xamarin-Migration auf .NET MAUI.
Wenn Sie den Beitrag gezielt durchgehen möchten, finden Sie hier eine schnelle Übersicht:
Das BeeWi Car ist ein schönes Praxisbeispiel, weil es sehr deutlich zeigt, wie plattformübergreifende Entwicklung in der Realität funktioniert. Das User Interface kann vollständig geteilt werden. Die eigentliche Kommunikation mit dem Gerät bleibt jedoch nativ.
Unter Android erfolgt die Verbindung klassisch über Bluetooth und einen RFCOMM-Socket. Unter iOS wird dagegen das External-Accessory-Modell verwendet, bei dem die App mit einem registrierten Zubehörprotokoll arbeitet. Diese Trennung gab es schon in Xamarin. In .NET MAUI wird sie lediglich sauberer organisiert.
Für genau solche Szenarien eignet sich die .NET MAUI App-Entwicklung besonders gut: gemeinsame Oberflächen, gemeinsame Geschäftslogik und dennoch voller Zugriff auf native Plattformfunktionen.
Die Architektur der App
Für die Umsetzung bietet sich eine klare Struktur an:
Gemeinsames UI in XAML
Gemeinsames ViewModel mit den Steuerbefehlen
Eine gemeinsame Schnittstelle IBeeWiCarService
Eine Android-Implementierung für Bluetooth
Eine iOS-Implementierung für External Accessory
So bleibt die Anwendung übersichtlich und wartbar. Gleichzeitig lässt sich die technische Logik leicht auf weitere Hardware-Szenarien übertragen. Das ist auch einer der Gründe, warum .NET MAUI nicht nur für kleine Demos, sondern ebenso für produktive Mobile App Entwicklung geeignet ist.
Das User-Interface in .NET MAUI
Um die möglichen Befehle Vorwärts, Rückwärts, Links und Rechts an das BeeWi Car zu senden, braucht die App entsprechende Eingabemöglichkeiten. In .NET MAUI lässt sich dieses Interface einmalig in XAML definieren.
Die Struktur ist bewusst einfach gehalten. Genau wie in den älteren Xamarin-Beispielen stehen vier Richtungsbuttons und eine Geräteauswahl im Mittelpunkt. Der Unterschied ist, dass das UI nun nicht mehr für jede Plattform separat gepflegt werden muss. Ebenfalls ein spannender Ansatz bietet Avalonia. Einen direkten Vergleich dazu findet ihr hier
Die gemeinsame Schnittstelle
Damit das ViewModel nicht wissen muss, ob es gerade auf Android oder iOS läuft, kapseln wir die Steuerung hinter einer Schnittstelle.
Auf dieser Basis kann das ViewModel später einfach Befehle senden, ohne Details der Plattformimplementierung kennen zu müssen.
Das ViewModel mit der gemeinsamen Logik
Im ViewModel liegt die eigentliche App-Logik. Hier werden Geräte geladen, Verbindungen aufgebaut und Befehle an den Service weitergeleitet.
public class MainViewModel
{
private readonly IBeeWiCarService _carService;
public ObservableCollection<string> Devices { get; } = new();
public string? SelectedDevice { get; set; }
public MainViewModel(IBeeWiCarService carService)
{
_carService = carService;
}
public async Task LoadDevicesAsync()
{
Devices.Clear();
var devices = await _carService.GetAvailableCarsAsync();
foreach (var device in devices)
Devices.Add(device);
}
public async Task ConnectAsync()
{
if (string.IsNullOrWhiteSpace(SelectedDevice))
return;
await _carService.ConnectAsync(SelectedDevice);
}
public Task ForwardPressedAsync() => _carService.SendCommandAsync(BeeWiCommand.ForwardGo);
public Task ForwardReleasedAsync() => _carService.SendCommandAsync(BeeWiCommand.ForwardStop);
public Task BackwardPressedAsync() => _carService.SendCommandAsync(BeeWiCommand.BackwardGo);
public Task BackwardReleasedAsync() => _carService.SendCommandAsync(BeeWiCommand.BackwardStop);
public Task LeftPressedAsync() => _carService.SendCommandAsync(BeeWiCommand.LeftGo);
public Task LeftReleasedAsync() => _carService.SendCommandAsync(BeeWiCommand.LeftStop);
public Task RightPressedAsync() => _carService.SendCommandAsync(BeeWiCommand.RightGo);
public Task RightReleasedAsync() => _carService.SendCommandAsync(BeeWiCommand.RightStop);
}
Damit ist der gemeinsame Teil der App praktisch schon fertig. Ab jetzt wird es plattformspezifisch.
Android: Bluetooth-Geräte auswählen und Socket öffnen
Unter Android ist das Vorgehen ähnlich wie im älteren Xamarin.Android-Beitrag. Zunächst werden bekannte Bluetooth-Geräte geladen. Danach wird das gewünschte Auto ausgewählt und über einen RFCOMM-Socket angesprochen.
using Android.Bluetooth;
using Java.Util;
public class BeeWiCarService : IBeeWiCarService
{
private BluetoothSocket? _socket;
private BluetoothDevice? _device;
private readonly BluetoothAdapter? _adapter = BluetoothAdapter.DefaultAdapter;
public Task<IReadOnlyList<string>> GetAvailableCarsAsync()
{
var result = new List<string>();
if (_adapter == null)
return Task.FromResult((IReadOnlyList<string>)result);
foreach (var device in _adapter.BondedDevices)
result.Add($"{device.Name}|{device.Address}");
return Task.FromResult((IReadOnlyList<string>)result);
}
public async Task<bool> ConnectAsync(string deviceId)
{
if (_adapter == null)
return false;
var address = deviceId.Split('|').Last();
_device = _adapter.BondedDevices.FirstOrDefault(d => d.Address == address);
if (_device == null)
return false;
_socket = _device.CreateRfcommSocketToServiceRecord(
UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));
if (!_socket.IsConnected)
await _socket.ConnectAsync();
return _socket.IsConnected;
}
public Task SendCommandAsync(BeeWiCommand command)
{
if (_socket?.IsConnected == true && _socket.OutputStream.CanWrite)
{
_socket.OutputStream.Write(new[] { (byte)command }, 0, 1);
_socket.OutputStream.Flush();
}
return Task.CompletedTask;
}
public Task DisconnectAsync()
{
_socket?.Close();
_socket = null;
_device = null;
return Task.CompletedTask;
}
}
Gerade hier zeigt sich, dass Cross-Plattform nicht bedeutet, auf nativen Code zu verzichten. Bei Bluetooth- oder Hardware-Szenarien bleibt die Plattformexpertise wichtig. Wer solche Integrationen gezielt für Android umsetzen möchte, findet auf unserer Seite zur Android App Entwicklung weitere Informationen.
iOS: Kommunikation über External Accessory
Auf iOS läuft die Kommunikation anders. Hier wird das BeeWi Car über das External-Accessory-Framework angesprochen. Entscheidend ist dabei das registrierte Protokoll com.beewi.controlleur.
using ExternalAccessory;
using Foundation;
public class BeeWiCarService : IBeeWiCarService
{
private const string Protocol = "com.beewi.controlleur";
private EASession? _session;
private EAAccessory? _accessory;
public Task<IReadOnlyList<string>> GetAvailableCarsAsync()
{
var devices = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories
.Where(a => a.ProtocolStrings.Contains(Protocol))
.Select(a => $"{a.Name}|{a.ConnectionID}")
.ToList();
return Task.FromResult((IReadOnlyList<string>)devices);
}
public Task<bool> ConnectAsync(string deviceId)
{
var connectionId = int.Parse(deviceId.Split('|').Last());
_accessory = EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories
.FirstOrDefault(a => a.ConnectionID == connectionId &&
a.ProtocolStrings.Contains(Protocol));
if (_accessory == null)
return Task.FromResult(false);
_session = new EASession(_accessory, Protocol);
_session?.InputStream?.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session?.InputStream?.Open();
_session?.OutputStream?.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session?.OutputStream?.Open();
return Task.FromResult(_session != null);
}
public Task SendCommandAsync(BeeWiCommand command)
{
if (_session?.OutputStream != null)
{
var buffer = new byte[] { (byte)command };
_session.OutputStream.Write(buffer, 1);
}
return Task.CompletedTask;
}
public Task DisconnectAsync()
{
_session?.InputStream?.Close();
_session?.OutputStream?.Close();
_session = null;
_accessory = null;
return Task.CompletedTask;
}
}
Zusätzlich muss in der Info.plist das unterstützte Protokoll eingetragen werden:
Auch hier gilt: Für produktive Hardware-Anwendungen ist plattformspezifisches Know-how unverzichtbar. Mehr dazu findet ihr auf unserer Seite zur iOS App Entwicklung.
Registrierung in MauiProgram.cs
Damit die App den jeweiligen nativen Service verwenden kann, wird die Implementierung in der DI-Konfiguration registriert.
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>();
builder.Services.AddSingleton<IBeeWiCarService, BeeWiCarService>();
builder.Services.AddSingleton<MainViewModel>();
builder.Services.AddSingleton<MainPage>();
return builder.Build();
}
}
Was sich gegenüber Xamarin geändert hat
Die grundlegende Logik ist erstaunlich ähnlich geblieben. Das BeeWi Car wird weiterhin über plattformspezifische Mechanismen angesprochen. Neu ist vor allem die Art, wie wir die Anwendung strukturieren. .NET MAUI erlaubt uns ein gemeinsames Projekt, ein gemeinsames UI und einen wesentlich klareren Aufbau.
Gerade für bestehende Xamarin-Apps ist das interessant. Vieles lässt sich konzeptionell übernehmen, aber in eine modernere Architektur überführen. Wer heute von Xamarin auf .NET MAUI wechseln möchte, sollte deshalb nicht nur an eine technische Portierung denken, sondern an eine strategische Modernisierung der Anwendung.
Fazit
Das BeeWi Car ist auch heute noch ein spannendes Beispiel dafür, wie sich mobile Apps mit externer Hardware verbinden lassen. Android und iOS verfolgen dabei unterschiedliche technische Ansätze, doch .NET MAUI schafft einen gemeinsamen Rahmen für UI, ViewModel und Service-Abstraktionen.
So entsteht eine App, die plattformübergreifend aufgebaut ist, ohne auf native Fähigkeiten zu verzichten. Genau das macht .NET MAUI für moderne Business-Apps, IoT-Szenarien und Hardware-Integrationen so interessant. Wer ein ähnliches Projekt plant oder eine bestehende App modernisieren möchte, findet auf unseren Seiten zur .NET MAUI App-Entwicklung, Cross-Plattform App Entwicklung und Mobile App Entwicklung passende Anknüpfungspunkte.
FAQ
Ein BeeWi Car ist ein kleines ferngesteuertes Spielzeugauto, das sich per Bluetooth mit einer mobilen App steuern lässt. Es eignet sich gut, um die Kommunikation zwischen App und externer Hardware praxisnah zu demonstrieren.
.NET MAUI eignet sich für dieses Beispiel besonders gut, weil die App ein gemeinsames UI und gemeinsame Logik nutzen kann, während die eigentliche Gerätekommunikation weiterhin plattformspezifisch umgesetzt wird.
Ja, das ist grundsätzlich gut möglich. Die fachliche Logik kann meist übernommen werden. Besonders profitieren Projekte davon, wenn die Migration gleichzeitig genutzt wird, um die Architektur zu modernisieren. Mehr dazu auf /xamarin/.
Nein. Android arbeitet in diesem Szenario klassisch mit Bluetooth und Socket-Kommunikation. iOS nutzt dagegen das External-Accessory-Framework und ein registriertes Zubehörprotokoll. Das gemeinsame UI kann geteilt werden, die Geräteanbindung bleibt jedoch nativ.
Ja. Gerade wenn dieselbe Anwendung auf mehreren Plattformen laufen soll und trotzdem nativer Zugriff auf Gerätefunktionen nötig ist, ist .NET MAUI ein sinnvoller Ansatz. Das betrifft nicht nur Spielzeugautos, sondern auch Scanner, Sensoren, Industrie-Hardware oder andere Bluetooth- und Zubehörlösungen.
Das hängt stark vom Projekt ab. Wenn große Teile der Anwendung auf mehreren Plattformen identisch sind, ist eine Cross-Plattform-Lösung oft wirtschaftlich sinnvoll. Wenn besonders viele plattformspezifische Funktionen oder Spezialintegrationen nötig sind, kann auch ein stärker nativer Ansatz sinnvoll sein. Einen Überblick geben die Seiten <a href=/cross-platform-app/>Cross-Plattform-App-Entwicklung</a>, <a href=/android-app-entwicklung/>Android-App-Entwicklung</a> und <a href=/ios-app-entwicklung/>iOS-App-Entwicklung</a>.
Sebastian Seidel
Als Mobile-Enthusiast und Geschäftsführer der Cayas Software GmbH ist es mir ein großes Anliegen, mein Team und unsere Kunden zu unterstützen, neue potenziale zu entdecken und gemeinsam zu wachsen. Hier schreibe ich vor allem zur Entwicklung von Android und iOS-Apps mit Xamarin und .NET MAUI.
In diesem Artikel lernst du, wie du Lottie-Animationen in .NET MAUI integrierst und sie mit Gesten, Scroll-Positionen und CarouselViews verknüpfst. Du erfährst, wie Animationen per Tap, durch Tippen & Halten, über Scroll-Interaktionen sowie beim Wechseln von CarouselView-Seiten gesteuert werden. Zusätzlich bekommst du komplette XAML- und C#-Beispiele, Best Practices und fertige Demo-Videos, um interaktive und moderne UI-Erlebnisse in deiner MAUI-App umzusetzen.
Mit dem nahenden Ende des Supports für Xamarin im Mai 2024 sind Entwickler damit beschäftigt, bestehende Xamarin.Forms-Projekte auf .NET MAUI als Nachfolger zu migrieren. Das tun wir natürlich auch. In diesem Artikel zeige ich 7 Schritte, die wir während des Übergangs immer machen mussten, um Ihnen den Umstieg auf .NET MAUI zu erleichtern.
Mobile apps thrive on an appealing and well-structured design that helps users achieve their goals. In this article, I'll show you how an old app design can be redesigned to make it look modern and clean. You'll learn more about the design process and design decisions made to provide users with a user-centric UI and UX that helps them achieve their goals.