Historia pewnego buga – natywne krainy: tam i z powrotem

W Flutterowym świecie nierzadko zdarza nam się korzystać z funkcjonalności, których implementacja jest stricte zależna od platformy na której jest uruchamiana. Przykładów jest wiele, jak choćby bohater dzisiejszej opowieści – Niebieski Ząb, w szerokim świecie znany pod przydomkiem Bluetooth. Tego Pana, a raczej tej technologii nikomu przestawiać nie trzeba. Na ten moment ważnym podkreślenia jest tylko fakt, że, wykorzystaliśmy tę technologię w jednym z projektów do połączenia się fizycznym urządzeniem.

Tyle słowem wstępu. Jak mawia mój ulubiony szachowy Youtuber – “kawka, herbatka w dłoń” i ruszamy. Uwaga – historia zawiera elementy fantastyki i mimo licznych opisów bitew – żaden programista, ani jego klawiatura nie ucierpieli.

Dawno, dawno temu, a nadmienić należy, że w świecie technologii oznacza to około paru miesięcy, pewien znamienity rycerz zgłosił się do nas z prośbą o stworzenie aplikacji która z pomocą Niebieskiego Zęba byłaby w stanie konfigurować pewien sprzęt. Nieistotnym jest czy chodzi o część pancerza, czy może o zestaw ultranowoczesnych okularów. Jako że jesteśmy doświadczeni w obróbce damasceńskiego kodu Darta, zdecydowaliśmy się na wykorzystanie Fluttera.

Rozpaliliśmy magiczny piec projektu i poczęliśmy co rusz dorzucać do niego commity. Hartowanie aplikacji szło gładko i co chwila z dumą obserwowaliśmy jak nowe funkcjonalności świetnie działają.

Aż tu któregoś dnia, ni stąd, ni zowąd, apka śmiga sobie na Androidzie, a na iOS błąd. Jak to jest możliwe? Przecież Flutter wszędzie działa tak samo! Co to za czary. Żadne czary, tylko pradawna, trochę w świecie Fluttera zapomniana, natywna magia – taka co to w zależności od platformy inaczej działa.

Niejeden adept gildii Fluttera przeląkłby się słysząc słowa ni to zaklęcia, ni to klątwy jakiejś:

The characteristic 44A78CE3-E653-4827-8AAD-8A08268056BA of the service 0EF881BC-6EC4-11EA-BC55-0242AC130003 of the peripheral 57FD66C2-0FE4-78CA-D719-F13AC93CECF7 is not writable 

No ale nie my. Paramy się tą magią już wystarczająco długo, by bez strachu stawić czoła wyzwaniu i ubić smoka… czy tam robala, ku uciesze gawiedzi… czy tam klienta.

Zebrała się rada starszych i szybko obmyśliła plan. Wyślemy poselstwo do ziemi githubowej by tam, w rejonie znanym jako flutter_reactive_blue, zasięgnąć języka. Rozpoczynamy poszukiwania. Wybieramy z treści naszego zaklęcia najbardziej istotną, naszym zdaniem, część. W naszym przypadku było to: „is not writable”. Korzystając z dobrodziejstwa githuba szukamy rzeczonej frazy. I oto jest, w jednej z ksiąg znajdujemy wzmiankę o zaklęciu. Szybko ją otwieram i widzimy że napisana została w języku Swift. Tutaj troszeczkę cieszymy się, że to nie prastary język Objective-C, chociaż i z tym dalibyśmy sobie radę. Wracamy do do księgi. Na ostatniej stronie, na samym dole znajdujemy treść zaklęcia:

1. case .notWritable(let qualifiedCharacteristic):
2. return "The characteristic (qualifiedCharacteristic.id) of the service (qualifiedCharacteristic.serviceID) of the peripheral (qualifiedCharacteristic.peripheralID) is not writable"
3. }

Pozostaje tylko znaleźć miejsce jego, w sensie czaru, wywołania. Szybko odnajdujemy w tej samej księdze dwa wywołania:

1. guard characteristic.properties.contains(.write)
2. else { throw Failure.notWritable(qualifiedCharacteristic) }

1. guard characteristic.properties.contains(.writeWithoutResponse)
2. else { throw Failure.notWritable(qualifiedCharacteristic) }

Wiemy już czego szukać, czas użyć wehikułu kompilowania. Odpalamy Xcode’a. Odnajdujemy te dwa wywołania w kodzie i wstawiamy breakpointy. Dodajemy kosmyk z grzywy jednorożca i uruchamiamy kompilację. Lotem błyskawicy (takie skojarzenie – akurat burza za oknem 😉 ) przenosimy się w miejsce występowania SmokoRobala. Klikamy… i jest. Kod się zatrzymuje dokładnie tam gdzie tego oczekiwaliśmy.

Lotem innej błyskawicy przeskakujemy po kolejnych liniach kodu i oto jest. Drugie z wywołań, które wcześniej znaleźliśmy, okazuje się być trafionym. Widzimy, że Swiftowy strażnik wykrywa i udaremnia wszelkie zakusy ku nieprawemu użyciu peryferiów urządzenia.

1. guard characteristic.properties.contains(.writeWithoutResponse)
2. else { throw Failure.notWritable(qualifiedCharacteristic) }

No dobra, nie działa – wiemy gdzie, ale nie wiemy czemu. Widzimy, że jest jakaś charakterystyka, która nie posiada odpowiednich właściwości. Sprawdźmy zatem jakie właściwości owa charakterystyka posiada. Naszym oczom ukazuje się niewiele wyjaśniająca wartość.

Upewnijmy się czego oczekujemy poprzez sprawdzenie wartości writeWithoutResponse. Ogromnej księdze, którą zwiemy dokumentacją widzimy wzmiankę:

1. static var writeWithoutResponse: CBCharacteristicProperties { get }

Niewiele nam ona mówi. Lecz jednak, jeśli sięgniemy do tłumaczenia napisanego w Objective-C naszym oczom ukaże się coś dużo bardziej przydatnego.

1. CBCharacteristicPropertyWriteWithoutResponse = 0x04

Wiemy że charakterystyka którą używamy ma wartość 4. Sprawdźmy zatem jakaż to charakterystyka ma wartość 10. Niech nikogo nie zdziwi i nie załamie fakt, że nie ma takiej charakterystyki, której wartość wynosiłaby rzeczone 10. Już będąc młodym adaptem magii, w gildii magów, uczymy się o bitach i maskach bitowych. Korzystając z tej wiedzy możemy wartość 10 zapisać bitowo jako 00001010. Poszczególne bity, ustawione na 1 odpowiadają liczbom 2 i 8 – sumarycznie 10. Na brodę Merlina! mamy to i powiedzmy to głośno: Żadna z dostępnych charakterystyk urządzenia nie ma wartości 4, która odpowiada charakterystyce używanej przez nas. Zobaczmy zatem jakież to charakterystyki kryją się pod wartościami 2 i 8.

1. CBCharacteristicPropertyRead = 0x02
2CBCharacteristicPropertyWrite = 0x08

Wiedzeni doświadczeniem, wspomagani przez rezultat rzutu kostką K20, wybieramy do dalszych testów:

1. CBCharacteristicPropertyWrite = 0x08

Działa!

Urządzamy małą ucztę. Chciałoby się napisać, że tańcom nie było końca. Niestety, zwiadowcy donoszą, że na wschodnich rubieżach, ktoś począł atakować nasze zapytania do zamorskich serwerów. Wyprawiamy konie, czas ruszać.

This site is registered on wpml.org as a development site.