Jak zwiększyć swoją produktywność z Bluetoothem na iOS’ie? 4 praktyczne porady
W naszym współczesnym życiu coraz większą rolę odgrywają urządzenia połączone w tzw. IoT, czyli internet of things. Mamy już nawet… czajniki, które posiadają system operacyjny i można sterować nimi z telefonu. Bluetooth jest jednym z najczęściej wybieranych protokołów do pracy z takimi urządzeniami. Oferuje on odpowiedni poziom bezpieczeństwa i w wielu przypadkach jest prostszy w użyciu, niż np. WiFi. W tym artykule podzielę się kilkoma praktycznymi poradami, które sam zdobyłem podczas pracy z tego typu projektami.
Spis treści
Leniwa inicjalizacja CBCentralManagera
Współczesne aplikacje iOS’owe mają bardzo intuicyjny flow, który prowadzi użytkownika przez wszystkie uprawnienia, które są potrzebne do poprawnego działania. W przypadku Core Bluetooth należy pamiętać o jego inicjalizacji w momencie, gdy będziemy gotowi pokazać użytkownikowi systemowy alert proszący o uprawnienia.
Na szczęście, dzięki Swiftowi mamy kilka gotowych narzędzi służących do tego, aby rozwiązać problem, nie poświęcając przy tym jakości kodu.
Pierwszym rozwiązaniem, jakie przychodzi programiście do głowy po usłyszeniu słowa “leniwy”, jest słowo klucz “lazy”. Dzięki temu nasza instancja CBCentralManagera zostanie stworzona przy pierwszym odwołaniu do niej, bez żadnej dodatkowej implementacji z naszej strony. Może to jednak przysporzyć nieco problemów, gdyż blok lazy nie może przyjmować żadnych argumentów, a większość aplikacji korzystających z CBCentralManagera potrzebuje przekazać mu delegate’a w inicjalizatorze.
Kolejnym pomysłem, jest skorzystanie z bloków. Zamiast przyjmować instancję CBCentralManagera do naszej klasy, możemy wstrzyknąć blok, który z delegata stworzy nam naszego CBCentralManagera. Typ bloku wyglądałby w następujący sposób:
typealias CreateCentralManagerBlock = (CBCentralManagerDelegate?) -> CBCentralManager
Dzięki temu możemy wywołać blok w dogodnym czasie i samemu wstrzyknąć odpowiedniego delegata. To rozwiązanie również łatwo przetestować w testach jednostkowych, dzięki czemu nasza aplikacja będzie działać w deterministyczny i pozbawiony regresji sposób.
Używaj debuggerów bluetoothowych
CoreBluetooth jako framework ma zarówno fanów, jak i antyfanów. Zresztą, jak każde narzędzie, ma swoje wady i zalety. Jedną z wad jest fakt, iż trzeba napisać trochę kodu, zanim będziemy mogli połączyć się z urządzeniem w dodatku kod ten trochę odbiega od współczesnych standardów typu functional reactive programming bądź structured concurrency niedawno wprowadzonego przez Apple’a.
Co, jeśli chcemy szybko sprawdzić, jak coś działa i na podstawie tego przyjąć odpowiednią strategię działania? Wtedy z pomocą przychodzą nam aplikacje, które ja osobiście nazywam “debuggerami bluetoothowymi”. Pozwalają one na skanowanie pobliskich urządzeń, analizowanie konfiguracji, wysyłanych pakietów rozgłoszeniowych, jak i zapis i odczyt charakterystyk. Dzięki temu możemy szybko zweryfikować, jak dane urządzenie działa i czy nasze rozumowanie jest poprawne.
Ja osobiście polecam nrfConnect od firmy Nordic Semiconductors. Jest to aplikacja na iOS’a, która zawiera wszystkie wymienione wyżej funkcje i jest dość intuicyjna w działaniu. Jeśli urządzenie docelowe korzysta z produktów firmy Nordic Semiconductors, za pomocą tej aplikacji możemy również szybko wykonać device firmware update.
Urządzenie nie jest gotowe? Można to przyspieszyć z Raspberry Pi!
Jako programiści często spotykamy się z takim problemem, że prace nad różnymi komponentami projektu muszą ruszyć od razu, aby zaoszczędzić czas i pieniądze. Tego typu podejście ma zwolenników i przeciwników, jednak ten artykuł nie będzie poruszał skuteczności tej metodologii. Nie ulega wątpliwości fakt, iż rzeczywistością wielu programistów (nie tylko) iOS’owych jest pisanie aplikacji, które łączy się z urządzeniem jeszcze nieistniejącym.
W naszym codziennym arsenale narzędzi znajduje się wiele emulatorów bądź symulatorów różnych rzeczy, jednak ciężko znaleźć symulator urządzenia Bluetooth’owego. Na szczęście mamy kilka rozwiązań, dzięki którym możemy przynajmniej zminimalizować czas oczekiwania, aż dostaniemy urządzenie do pracy. W większości wypadków jednak możemy sami napisać prosty program, który wystawi taki sam service GATT’owski (bądź inny), będzie miał takie same charakterystyki i taki sam sposób uwierzytelniania, jak urządzenie docelowe. Jeśli sam znajduję się w takiej sytuacji, najczęściej używam do tego Raspberry Pi.
Zdecydowana większość minikomputerów Raspberry Pi ma wbudowany chip Bluetooth, który jest podłączony pod magistralę UART. Domyślne dystrybucje Linuxa udostępniane przez producenta mają również zainstalowany open source’owy stos Bluetoothowy BlueZ, więc po uruchomieniu nie musimy nawet instalować dodatkowych rzeczy ani nic konfigurować. Komputery te są również stosunkowo tanie, można je bez problemu kupić w sklepach z elektroniką na terenie całego kraju.
Jest dużo różnych tzw. wrapperów wokół BlueZ do różnych języków programowania, za pomocą których w kilkadziesiąt linii kodu możemy napisać program, który będzie w stanie nawiązać dialog z naszą aplikacją na telefon. Swift jako open source’owy język działa również na Linuxie i istnieją otwarte rozwiązania, za pomocą których nie musimy pisać tego programu w innym, nieznanym nam języku. Teraz możemy kontynuować jej pisanie bez czekania, aż inny zespół skończy swoje zadanie. W moich poprzednich projektach dobrze sprawdzały się wrappery w node.js’ie – takie jak te: https://www.npmjs.com/package/bluez.
Rozwiązanie to jednak wymaga ścisłej synchronizacji pomiędzy zespołem mobilnym a zespołem implementującym firmware urządzenia. Zanim napiszemy choć jedną linijkę kodu, należy ustalić dokładny interfejs działania urządzenia, aby nie wracać później i zmieniać czegoś, co już działa.
Pisz testy jednostkowe, aby zweryfikować swoją logikę
Testy jednostkowe są bardzo ważnym narzędziem, za pomocą którego możemy szybko zweryfikować, czy nasza logika działa poprawnie. Niestety, w mojej karierze spotkałem się z wieloma projektami, które nie miały żadnych automatycznych testów. Najczęściej tłumaczono to brakiem czasu.
Aplikacje, które łączą się z innymi urządzeniami cechuje dość długi cykl edit, compile and run. Każdy taki cykl zajmuje cenny czas, a niestety czas pracy programistów jest drogi, więc przekłada się to na realne koszty w projekcie. Dlatego ważne jest, aby przed rozpoczęciem projektu zastanowić się nad odpowiednią architekturą, która pozwoli na odizolowanie dowolnego komponentu od jego środowiska, aby umożliwić pisanie testów.
Dzięki temu, zamiast uruchamiać aplikację od nowa za każdym razem, gdy mamy jakąś zmianę, możemy uruchomić test, który wykona się o wiele szybciej.
Swift został pozbawiony pewnych elementów dynamizmu na rzecz silnego typowania, więc nieco trudniej pisać testy w nim, niż w sędziwym Objective-C. Jednak Core Bluetooth jest napisany w Objective-C, więc w naszych testach możemy z powodzeniem nadal korzystać z jego dynamizmu. Istnieje wiele bibliotek, za pomocą których możemy w łatwy sposób stworzyć tzw mocki obiektów pochodzących z Core Bluetootha, przykładowo: https://github.com/birdrides/mockingbird.
Inną dobrą praktyką jest odizolowanie implementacji od interfejsu zgodnie z protocol based programming. Zamiast bezpośrednio wstrzykiwać implementacje do naszych komponentów, możemy odizolować metody, z których faktycznie korzystamy do protokołu i bazować tylko na nim. To podejście działa bardzo dobrze ze SwiftUI i Xcode’owym Preview. Działałoby to w następujący sposób:
protocol CentralManagerWrapper { func startScanning(services: [CBUUID]?) } extension CBCentralManager: CentralManagerWrapper { func startScanning(services: [CBUUID]?) { scanForPeripherals(withServices: services, options: nil) } }
Dzięki temu, nasza implementacja może bazować na protokole CentralManagerWrapper do którego można łatwo napisać mock’a w testach czy w preview SwiftUI.
Podsumowanie
Mam nadzieję, że powyższe porady pomogą Ci w produktywnej pracy z Bluetoothem na iOS’ie. W intent, gdzie zarządzam zespołem mobilnym, pracujemy z tego typu projektami i cały czas szukamy zdolnych programistów wszystkich technologii mobilnych. Jeśli chcesz pracować na co dzień ze SwiftUI’iem i CoreBluetoothem, daj mi koniecznie znać – michal.tuszynski@withintent.com.
Zdjęcie główne pochodzi z unsplash.com.