piątek, 19 maja 2017

Nowa praca, nowe początki

Witam ponownie po dłuższej przerwie!
Piszę do was będąc na pierwszej delegacji we Francji i mając już ponad dwa tygodnie w nowej firmie za sobą. Siedzę w hotelu w małym miasteczku - Obernai gdzie mieści się zarazem główna siedziba mojej nowej firmy, którą jest (fanfary):


Firma produkcyjna - o korporacyjnym charakterze, rodzinnym kapitale i posiadająca kilka interesujących marek. Dla mnie najbardziej nieznaną sprawą była (i wciąż jest) korporacyjność. Przeskok organizacyjny pomiędzy małą polska firmą, a międzynarodową korporacją jest ogromny i ciężko mi jeszcze określić co dokładnie jest plusem, a co minusem - na pewno plusem jest jasno określona hierarchia, zakres obowiązków i pełny wgląd do wszelkich regulaminów, systemu benefitów itp. Największym minusem jest chyba polityka bezpieczeństwa IT - na komputerach nie mamy dostępu do konta admina, a cały ruch sieciowy leci przez proxy we Francji i bez aktywnego VPNa nie można się połączyć z internetem poza firmą. Pewnie wrócę do tematu plusów i minusów jeszcze w przyszłości aczkolwiek jestem jak na razie zadowolony.

Moja praca polega na pisaniu aplikacji mobilnej o nazwie 123Schema z wykorzystaniem Xamarin.Native przy użyciu frameworka MVVMCross, o którym jeszcze później wspomnę. Pracuję w 4-ro osobowym zespole składającym się ze mnie, kolegi juniora w PL, architekta/team leadera oraz z seniora we Francji. Do zarządzania cyklem życia aplikacji używamy TFSa, w którym również siedzi kod z wykorzystaniem GIT-a. Jest to dla mnie nowość i po tych dwóch tygodniach jestem BARDZO zadowolony z używania takiego rozwiązania. Backlog, tablica Kanbana, daily meetingi, zarządzanie taskami, branchami, pull requestami - wszystko na prawdę współgra i tworzy spójną całość, w której natychmiast się odnalazłem i czekam jeszcze tylko na integrację z SonarQubem i rozpoczęcie korzystania z Continous Integration. Muszę również powiedzieć szczerze, że architekt jest OK - bardzo rygorystycznie przestrzega wszelkich standardów i bardzo szczegółowo pisze wymagania. Ma sporą wiedzę o wzorcach projektowych i dobrych praktykach  - chociaż  raz chciał zastosować anty-pattern IRepository<T> -  ale jak zawsze mnie wysłuchał i ogólnie jest otwarty na pomysły i dyskusje. 

MVVMCross powoli poznaje i odkrywając jego funkcjonalność dodawaną w kolejnych betach przekonuje się że to dobre narzędzie ALE .... mimo wszystko cały czas obstawiam, że Xamarin.Forms byłby lepszy :) Niestety decyzje o wyborze stosu technologicznego zapadły na długo zanim projekt się zaczął i ja pojawiłem się w firmie więc biorę co jest i traktuje to jako zawodowe wyzwanie - zawsze to przyjemnie nauczyć się czegoś nowego.



Czekają mnie jeszcze co najmniej dwie dwutygodniowe podróże tutaj, by na miejscu sprawnie się konsultować, integrować i poznawać ludzi z IT - z jednej strony bardzo fajnie, a z drugiej niestety takie wyjazdy są bardzo uciążliwe ze względów rodzinnych i m.in pszczółkowych - przez 3 miesiące okresu próbnego - połowę czasu spędzę we Francji. 

Na zakończenie mogę powiedzieć, że ogólnie sprawy zaczynają się stabilizować - mam nadzieję w najbliższych tygodniach wrócić do DSP i kontynuować pisanie mojej konkursowej aplikacji. Jednocześnie pozdrawiam kolegów z inSolutions i życzę wszystkiego dobrego! Do kolejnego przeczytania :)


niedziela, 23 kwietnia 2017

Update na blogu

Witam wszystkich po przerwie.
Niestety jak widać od pewnego czasu na blogu jest trochę pusto i cicho - postaram się pokrótce wytłumaczyć dlaczego do tego doszło i co dalej z blogiem.
Powód pierwszy, najważniejszy - zmieniam pracę! Tak - 6 lat i 2 miesiące to mój staż w obecnej firmie i czas ten dobiegł końca. Już za tydzień trafię do nowego zespołu i zaczynam nowy życiowy rozdział - razem z naszym przyjacielem - Xamarinem. No i teraz do sedna - dlaczego nie piszę postów? Otóż projekty w nowej firmie będę robił w podejściu natywnym i cały swój wolny czas staram się poświęcić na podszkoleniu z platformowych API. Trochę to nie pasuje do Xamarin.Forms w ramach którego piszę swój projekt na DSP więc chwilowo muszę projekt wstrzymać, aczkolwiek dobitnie podkreślam że go nie anuluje!
Gorący i stresujący okres przez który przechodzę , zwalnianie się, wykańczanie projektów, które zostawiam i niepewność co zastane w nowej pracy to zły czas na prowadzenie bloga - dodatkowo przez pierwsze trzy miesiące czekają mnie służbowe wyloty do francji na wdrożeniowe szkolenia.
 Zachęcam jednak do zaglądania co jakiś czas bo na pewno będę relacjonował początki w nowej pracy, a jak tylko sytuacja się ustabilizuje to wracam do "Mojej Pasieki".
No ale jeszcze jest powód drugi - powód dla którego projekt powstał i powód, który również zajmuje sporo mojej uwagi. Pszczółki, bo o nich mowa maja się świetnie - przezimowały wspaniale, a ich wiosenny rozwój troche mnie przerasta. Przy okazji ostatniej lepszej pogody chciałem im pouzupełniać brakujące ramki - dwie rodziny zimowały na dwóch korpusach wielkopolskich z ramkami ułożonymi w komin po 7-8 ramek na korpus, jedna na 1,5 korpusu, a ostatni odkład zimował na 9 ramkach w jednym korpusie. Widok jaki zastałem po dwóch tygodniach:


Jak widać ramki z węzą chciałem im dać zbyt późno - same sobie pouzupełniały wg uznania, a część już pozalewały świeżym nakropem - najprawdopodobniej z klonów, mleczy i drzew owocowych. Praca na pasiece wre, sezon oficjalnie rozpoczęty - w garażu tworzą się korpusy, dennice i daszki, a w domu unosi się zapach wosku z wtapianej w nowe ramki węzy. 
Wybaczcie mi więc tą ciszę - życie wymusza skupienie uwagi na innych sprawach. Obiecuję o was nie zapomnieć i wracać z nowymi postami jak tylko często się da.

niedziela, 9 kwietnia 2017

Jak dodać projekt platformy do już istniejącej solucji.

Cześć,
dzisiaj małe tips&tricks. Na początek przepraszam za brak postów ostatnio na blogu - powody opiszę w najbliższej przyszłości - zapewniam, że są racjonalne :)

O co chodzi w temacie posta? Problemem jest dodanie nowego projektu dla konkretnej platformy w juz istniejącej solucji Xamarin.Forms. Niestety żadne IDE Xamarinowe nie posiada takiej funkcjonalności, zarówno Xamarin Studio na OS-x i Windowsie ani Visual Studio.

Xamarin Studio na OSX

Rozpoczynając projekt mojej konkursowej aplikacji popełniłem błąd i zacząłem pisać ją w Xamarin Studio na Windowsie - tam niestety platforma iOS nie jest wspierana. Miałem więc solucję z projektami Forms, Droid i Test - co zrobić, żeby dodać tam projekt na iOSa?

Rozwiązanie jest proste choć nieoczywiste. Trzeba utworzyć nową solucję o takiej samej nazwie jak obecna (ten sam package name) i zaznaczyć brakującą platformę. Fizycznie kopiujemy projekt iOS do naszej pierwotnej solucji, a następnie już w IDE dodajemy istniejący projekt, wyklikujemy dodanie referencji i gotowe.
Mnie udała się ta sztuka za pierwszym razem, a do nadrobienia miałem dodanie inicjalizacji paru nugetowych pakietów w AppDelegate oaz implementację dwóch interfejsów z DependcencyService.
Analogicznie powinno to działać w przypadku braku projektu Androidowego.

Teraz moja solucja wygląda następująco:

widok na OSX

Widok na Windowsie

niedziela, 26 marca 2017

Jak narysować coś w Xamarin.Forms

Hej,
dzisiaj kolejny problem jaki musiałem rozwiązać w formsach. Chciałem narysować ul. W zależności od jego typu, elementów i danych wprowadzonych przez użytkownika rysunek miał się różnić tak aby oddawać jego rzeczywisty stan więc wstawienie gotowego rastra odpadało. Schemat ula musi być więc wygenerowany w kodzie - niestety ograniczenia formsów znowu okazały się zbyt duże. Tak na prawdę musimy poprzestać na samym prostokącie, który możemy zrealizować np poprzez kontrolkę BoxView lub każdą inną, która ma właściwość BackgroundColor. W Adobe AIR sprawa byłaby prosta - większość kontrolek wystawia nam obiekt klasy Graphics, który pozwala na rysowanie wektorowe przeróżnych kształtów. Po, krótkim researchu odnalazłem jej odpowiednik dla Xamarina - po pierwsze NGraphics, które pozwala na obsługe wektorowej grafiki na każdej z platform korzystając z natywnych konrolek, a po drugie - NControl, która to biblioteka opakowuje pierwszą w kontrolkę, dziedziczącą po Xamarin.Forms.View. Sprawa jest o tyle przyjemna, że mając naszą biblioteke rysującą w postaci View - możemy nie naruszając architektury MVVM rysować i bindować do widoku nasz rysunek np poprzez:
<ContentView Content="{Binding BeeHiveDrawing}" />
całkowicie w naszym modelu widoku:
public Xamarin.Forms.View BeeHiveDrawing
{
    get
    {
        return _beeHiveDrawing;
    }
    set
    {
        _beeHiveDrawing = value;
        OnPropertyChanged(nameof(BeeHiveDrawing));
    }
}
Ok, przejdźmy więc dalej. Sama inicjalizacja naszej kontrolki jest standardowa  i opisana pod linkiem podanym powyżej. Jej przykładowe użycie wygląda następująco:
var nc = new NControlView((NGraphics.ICanvas canvas, NGraphics.Rect rect) =>
{
    canvas.DrawPath(new PathOp[] 
    { 
        new MoveTo(0, 0),
        new LineTo(rect.Width, rect.Height) 
    }, 
    new Pen("#ff0000", 3));
});

W konstruktorze NControlView podajemy Action<ICanvas, Rect> gdzie otrzymujemy nasz element - płachtę, po której rysujemy oraz jej finalne wymiary w postaci obiektu NGraphics.Rect. Mamy więc od czego zacząć - w następnym poście zaprezentuje jak poradziłem sobie z zadaniem, opisanym na początku - rysowaniem ula.

środa, 22 marca 2017

Mapy i geolokalizacja w Xamarin.Forms

Hej,
w moim projekcie, zaraz na początkowym etapie miałem potrzebę użycia map. Dokładnie potrzebowałem poprosić użytkownika o wskazanie lokalizacji pasieki - w przyszłości mam zamiar wykorzystać te dane do pokazywania użytkownikowi z jednego z dostępnych API  pogody na pasiece i w pewnych przypadkach pokazywać mu notyfikacje (moja główna wartość biznesowa). Na start trafiłem na przygotowany przez ekipę Xamarina bibliotekę Xamarin.Forms.Maps, której użycie i konfiguracja opisana jest tutaj.
Niestety szybko okazało się, że funkcjonalność oferowana przez ten pakiet jest uboga i ogranicza się głównie do kontrolki mapy, na której możemy pokazać nieinteraktywnego pina. Szybko trafiłem na bibliotekę, która bazując na w/w bardzo rozszerza jej funkcjonalność, a którą bardzo polecam: TK.CustomMap - z jej pomocą możemy dodawać customowe, przeciągalne piny, rysować na mapie kształty, wyznaczać trasy itp. Instalacja z nugeta jest bezproblemowa i po konfiguracji opisanej np na githubie możemy zacząć dodawać mapy.

Na potrzeby testów i ewentualnej zmiany używanej biblioteki dodałem sobie interfejs:
public interface IMap
{
    void showMap();
    Task showMapForLocation(Action<string> onUserSelectPoint, double currentLat = 0, double currentLng = 0);
}

Pierwsza metoda służy mi do pokazania mapy - zapewne będę ją rozbudowywał o dodatkową funkcjonalność, jednak do zabawy z mapami po prostu pokazuje okienko z mapą. W samej aplikacji korzystam na razie tylko z drugiej funkcji i na jej przykładzie omówię moje użycie biblioteki - pełna implementacja jest wrzucona na githubie.
Na początek tworzę obiekt mojej mapy z początkową lokalizacją gdzieś w Polsce (52,20):
var map = new TK.CustomMap.TKCustomMap(MapSpan.FromCenterAndRadius(new Position(52, 20), Distance.FromKilometers(100)));

Utworzony obiekt dodaję do wcześniej przygotowanego widoku, a następnie tworzę obiekt pina,  dodaje go do mapy, a po kliknięciu w mapę, przesuwam pina w te miejsce.
var pin = new TK.CustomMap.TKCustomMapPin { Position = map.MapCenter, IsDraggable = true, ShowCallout = true, Title = "Lokalizacja pasieki" };
map.CustomPins = new List<TK.CustomMap.TKCustomMapPin> { pin };
map.MapClicked += (object sender, TK.CustomMap.TKGenericEventArgs<Position> e) =>
{
    pin.Position = e.Value;
};

W ten sposób, użytkownik może zarówno klikając w mapę jak i przeciągając pina po niej łatwo wskazać lokalizację. Teraz pozostaje nam temat drugi - czyli geolokalizacja. Na początek w celu ułatwienia użytkownikowi odnalezienia swojej pasieki staram się wycentrować mapę na jego bieżacej lokalizacji - użyłem do tego biblioteki Xam.Plugin.Geolocator - polecam
ją bo korzysta z async/await oraz sama pyta o przyznanie uprawnień w Androidzie 5.0+. Jej użycie sprowadza się tak naprawdę do paru linijek kodu:

if (Plugin.Geolocator.CrossGeolocator.Current.IsGeolocationAvailable && Plugin.Geolocator.CrossGeolocator.Current.IsGeolocationEnabled)
{
    userLocation = await Plugin.Geolocator.CrossGeolocator.Current.GetPositionAsync(10000);
    var userPosition = new Position(userLocation.Latitude, userLocation.Longitude);
    map.MoveToRegion(MapSpan.FromCenterAndRadius(userPosition, Distance.FromKilometers(3)));
    pin.Position = userPosition;
}

Jeżeli api geolokalizacji jest dostępne - pobieramy lokalizację z timeoutem 10s, zmieniamy strukturę lat/lng na tą z TK.CustomMaps i centrujemy mapę i pina. Na początek na moje potrzeby taka funkcjonalność jest wystarczająca chociaż będę musiał dodać jeszcze pole tekstowe do wyszukiwania adresu.  Dodanie obsługi map w aplikacji - korzystając z tutoriali i opisów z zamieszczonych tu linków nie nastręczyło mi większych trudności, a mam nadzieję że korzyść z tego płynąca będzie spora. Pozdrawiam i do następnego razu!


niedziela, 19 marca 2017

XAMARIN i SQlite

Hej,
dzisiaj trochę o bazie danych w naszej konkursowej aplikacji - cóż to by była za aplikacja bez bazy, prawda? Przeglądając wszystkie xamarinowe tutoriale wybór wydaje się oczywisty: SQLite. Z takiego też rozwiązania korzystam, aczkolwiek gwoli ścisłości sposobów na przechowywania danych w xamarinie jest sporo - możemy równie dobrze dane parsować do jsona i korzystając z PCL Storage trzymać je w plikach. Ale ponieważ SQLite jest natywnie wspierany pod wszystkimi mobilnymi platformami, oraz korzystałem z niego wcześniej pisząc pod Adobe AIR nie zastanawiałem się długo nad wyborem. W internecie jest więcej niż jedna biblioteka tego bazodanowego silnika, ale najbardziej wypromowana i polecana jest sqlite-net-pcl wywodząca się z .netowej implementacji o nazwie sqlite.net.

Sam proces tworzenia naszej bazy jest dobrze opisany w internecie np tu : link.
Korzystanie jest proste i przyjemne jednak mamy pewne ograniczenia wynikające ze stosowania ORMa oraz z trochę okrojonego silnika. Po tych kilku latach używania SQLite nazbierało mi się kilka uwag i upierdliwych problemów:

  • Pod każdy interesujący nas wynik zapytania musimy zrobić osobną encje - nie można po prostu zrobić joina dwóch tabelek i dostać wyniku w postaci List<object>. Bardzo brakuje mi możliwości zwrócenia wyniku w postaci Hashtable, ExpandoObject lub w inny podobny sposób, który nie angażowałby mnie tak bardzo w pisanie encji. Korzystając z bazy w Adobe Air, w taki właśnie sposób działało zwracanie wyników - dostawaliśmy tablicę AS3.0 obiektów które zachowują się tak jak ExpandoObject w C#. 
  • Brak kluczy zewnętrznych - aczkolwiek znalazłem bibliotekę, która taką funkcjonalność oferuje (link) ale pewnie daje to spory narzut bo sam silnik takiej funkcjonalności nie posiada. 
  • ALTER TABLE obsługuje tylko ADD COLUMN - żadnej zmiany na istniejącej kolumnie nie wykonamy - nawet jej nie usuniemy. Trzeba się bawić w przenoszenie danych do tabelki tymczasowej, drop, nowy create itd.
  • Plik bazy ma tendencje do puchnięcia - polecam co jakiś czas wykonać VACUUM.
  • Error: database disk image is malformed - kto nigdy tego błędu nie widział ten może się uznać za szczęśliwca. Niestety mając kilka tysięcy urządzeń na produkcji raz na jakiś czas zdarza się, że w środowisku wystąpi błąd który plik bazy nieodwracalnie uszkadza (link). Dostęp do którejś z tabel jest wtedy niemożliwy, a resztę zwykle staram się odzyskać i w locie przerzucić do nowo utworzonej bazy. Bardzo irytujący błąd, którego nie potrafię wyeliminować.


Podsumowując - wybór silnika bazy danych był dla mnie prosty. Nie zawiodłem się na nim i polecam SQLite każdemu, kto potrzebuje skorzystać z relacyjnej bazy danych na urządzeniu mobilnym.



niedziela, 12 marca 2017

Widok, Model Widoku - proste połączenie

Hej,
w tym poście chciałbym zaprezentować prosty i skuteczny sposób na kojarzenie dwóch tytułowych elementów w jeden. Słowem wstępu - najpierw kilka informacji o działaniu widoków w Xamarin.Forms:
Widoki możemy tworzyć na dwa sposoby: używając XAMLa oraz całkowicie w kodzie c#. Osoby korzystające z Xamarina lub WPFa pewnie z XAMLem miały już do czynienia, a jak nie to krótko ujmując jest to język oparty na XMLu służący właśnie do tworzenia warstwy interfejsu użytkownika. Ma on tą przewagę nad tworzeniem widoków w kodzie, że po krótkiej praktyce, jestem w stanie z wizualizować sobie interfejs w głowie - przydatna umiejętność.
Pisząc XAMLowy widok w Xamarin.Forms jest on podzielony na dwa elementy - plik *.xaml, w którym jest xml z widokiem i plik tzw "codebehing" - *.xaml.cs który jest podpięty pod plik *.xaml i wygląda on mniej więcej tak:
public partial class WidokTest : ContentPage
{
    public WidokTest ()
    {
        InitializeComponent();
    }
}
Jak widać dla naszego testowego widoku mamy klasę, w której określamy bazowy widok po którym dziedziczymy - ContentPage lub ContentView najczęściej, a w konstruktorze wywołujemy inicjalizację komponentu.
Na pytanie skąd ten partial odpowiedź jest prosta - drugą częścią naszej klasy jest właśnie nasz plik xamlowy, który kompilator przerabia na jedną, wspólną klasę.

Dobrą praktyką jest aby w codebehind robić jak najmniej, a wszystkie operacje na widoku działy się poprzez bindingi. No ale w naszej klasie testowej brakuje co najmniej jednego elementu: ustalenia właściwości BindingContext, który powinien być instancją jednego z naszych modeli widoków.

Stworzyłem więc taką klasę bazową:

public abstract class ViewPage<T> : ContentPage where T : IViewModel
{
    readonly T _viewModel;
    public T ViewModel
    {
        get { return _viewModel; }
    }

    public ViewPage()
    {
        using (var scope = IoC.container.BeginLifetimeScope())
        {
            _viewModel = scope.Resolve<T>(new TypedParameter(this.GetType(), this));
        }
        BindingContext = _viewModel;
    }

    public ViewPage(object viewModelContext)
    {
        using (var scope = IoC.container.BeginLifetimeScope())
        {
            _viewModel = scope.Resolve<T>(new TypedParameter(this.GetType(), this), new NamedParameter("context", viewModelContext));
        }
        BindingContext = _viewModel;
    }
}
Stosując taką generyczną klasę bazową dla widoków mogę łatwo i szybko wyciągnąć z kontenera odpowiedni model i podpiąć go pod widok. Korzystając z niej, nasza testowa klasa widoku wyglądałaby następująco:

public partial class WidokTest : ViewPage<WidokTestModel>
{
    public WidokTest ()
    {
        InitializeComponent();
    }
}

public partial class WidokTest : ViewPage<WidokTestModel>
{
    public WidokTest (object kontekstWidoku) : base(kontekstWidoku)
    {
        InitializeComponent();
    }
}

Jak widać ilość kodu w codebehind wzrosła bardzo nieznacznie, a mamy załatwiony najważniejszy element. Niestety wszystko ma swoją cenę - trzeba przypomnieć sobie o tym nieszczęsnym partialu - nasz klasa dziedziczy teraz po nowym elemencie  i musimy analogicznie zmienić to w XAMLu:

<test:ViewPage xmlns:test="clr-namespace:MyApp.Test" x:TypeArguments="test:WidokTestModel"

Wystarczy dodać namespace w którym jest nasza klasa bazowa, zmienić nazwę głównego elementu oraz dodać generyczny parametr. W ten sposób wszystko nam się ładnie kompiluje, a my w prosty sposób spinany ze sobą widoki i modele widoków.