03 — Mobilā izstrāde
iOS, Android, vairāku platformu un backendi aiz tiem
Mobilās lietotnes, kas piegādātas reāliem lietotājiem — un palika piegādātas — vairāk nekā vienpadsmit gadus.
Natīvais iOS un Android to pašu valodās, React Native un Flutter tur, kur tie der, un backendi, keši un mākoņi, kas padara tālruni par vairāk nekā plānu apvalku. Vienpadsmit gadu trase ir tas, kas pārējo padara ticamu.
Esmu pavadījis vairāk nekā vienpadsmit gadus, būvējot un piegādājot mobilās lietotnes reāliem lietotājiem — un tieši šis viens fakts padara katru citu apgalvojumu šajā lapā lasīšanas vērtu.
Ikviens var uzskaitīt frameworkus. Apliecinājums, kas svarīgs, ir ilgmūžība: lietotnes, ko reāli cilvēki instalēja, paturēja un lietoja iOS, Android un Windows platformās vairāk nekā desmitgadi. Vienpadsmit gadu trase nav rindiņa CV — tā ir pierādījums, ka zemāk esošie lēmumi tika pieņemti zem spiediena no programmatūras, kurai bija jāturpina darboties pēc palaišanas.
Es strādāju natīvi vispirms. iOS tas nozīmē Swift un Objective-C ar SwiftUI, Core Data, Core Animation un SiriKit; Android tas nozīmē Kotlin un Java pret Android SDK daudzās OS versijās. Tieši šī natīvā tekošā prasme padara vairāku platformu darbu — React Native un Flutter augstā līmenī, Xamarin darba līmenī — par apzinātu izvēli, nevis veidu, kā izvairīties no platformas.
Un mobilā lietotne nekad nav tikai klients. Aiz tās sēž backendi Node, Spring Boot, Django, Flask, Rails un Go, eksponējot REST un GraphQL, balstīti uz SQLite ierīcē un Redis serverī, izvietoti uz Firebase, AWS un Google Cloud. Lapa, kas seko, ir visa šī virsma, izstāstīta tā, kā es to patiešām būvēju.
gadi, piegādājot mobilās lietotnes reāliem lietotājiem — iOS, Android un Windows platformās
natīvās platformas, uzturētas to pašu valodās — Swift/Objective-C un Kotlin/Java
vairāku platformu steki, ko ekspluatēju augstā līmenī — React Native un Flutter
backend frameworki, ko savienoju ar mobilajiem klientiem, no Node līdz Go
Divas natīvās platformas, divi vairāku platformu steki, viena Windows virsma.
Pirmais lēmums jebkurā mobilajā projektā ir, uz kā būvēt, un tā reti ir viena un tā pati atbilde divreiz. Zemāk esošā matrica ir tas, kā es par to spriežu: natīvais iOS un Android katrs savā valodā, divi vairāku platformu steki, ko ekspluatēju augstā līmenī, un Windows virsma, kas notur godīgu koplietoto loģiku. Diagramma kartē valodas un frameworkus uz platformām, nevis izliekas, ka viens rīks aptver visu.
Valodas un frameworki, kartēti uz platformām
Matrica, nevis monolīts — katra platforma valodā, ko tā atalgo.
iOS saņem Swift un Objective-C; Android saņem Kotlin un Java; Windows saņem savu klientu. React Native un Flutter aptver veikalus, kad UI necīnās ar platformu, un kolonna pa labi parāda, kur katrs rīks patiešām nokļūst.
Matricas lasīšana ir darbs: tā es izlemju, vai funkcija ir natīva, vairāku platformu vai plāns klients virs koplietota backenda, pirms uzrakstīta kaut viena UI rindiņa.
- iOS — Swift, Objective-C, SwiftUI, Core Data
- Android — Kotlin, Java, Android SDK
- Vairāku platformu — React Native, Flutter (augsts līmenis)
- Windows — natīvs darbvirsmas klients
Ko es patiešām rakstu katrā platformā.
Platformas nav savstarpēji aizstājamas, un rīki to atspoguļo. Katra cilne ir platforma, kurā esmu piegādājis — valodas, frameworki un spriedums par to, kad katrs nopelna savu vietu.
Natīvais iOS Swift valodā, ar Objective-C tur, kur tas vēl dzīvo
iOS es rakstu Swift un lasu Objective-C, jo reālas koda bāzes, kas piegādātas gadiem, reti ir tīrs Swift. Es būvēju saskarnes SwiftUI jaunajiem ekrāniem un saglabāju UIKit tur, kur no tā atkarīga esoša plūsma, nevis pārrakstu strādājošu kodu kāda framework dēļ.
Noturība ir Core Data, kad lietotnei pieder reāls objektu grafs, un kustība ir Core Animation, kad pārejai jājūtas natīvai, nevis tuvinātai. SiriKit apstrādā balss nodomus tur, kur tie pelna savu vietu — ne katrā ekrānā, tikai tur, kur runātais saīsne patiešām ir ātrāka par pieskaršanos.
- Swift vispirms, Objective-C lasīts un uzturēts
- SwiftUI jauniem ekrāniem, UIKit saglabāts tur, kur tas darbojas
- Core Data objektu grafam, Core Animation kustībai
- SiriKit nodomi tur, kur balss patiešām ir ātrāka
Android Kotlin un Java valodās, daudzās OS versijās
Android es strādāju Kotlin un Java pret Android SDK, un esmu piegādājis pret plašu OS versiju klāstu — praktiskais darbs ir atbalstīt ierīces, ko lietotāji patiešām nēsā, nevis tikai jaunāko laidienu. Tas nozīmē godīgus minimālā SDK lēmumus un fragmentācijas testēšanu, nevis pieņēmumu, ka tās nav.
Tie paši raksti pārceļas: skaidrs datu slānis, dzīves cikla apzinīga UI un to lietu skaidra apstrāde, ko Android liek apstrādāt — procesa nāve, konfigurācijas izmaiņas un fona ierobežojumi, kas saspringst ar katru laidienu.
- Kotlin un Java pret Android SDK
- Piegādāts daudzās Android OS versijās
- Dzīves cikla apzinīga UI, skaidra procesa nāves apstrāde
- Minimālā SDK lēmumi, balstīti uz reālām ierīcēm
Windows klienti līdzās mobilajam darbam
Es būvēju arī Windows, kas saglabā virsmu godīgu: funkcija, kurai jānokļūst iOS, Android un Windows, nevar paļauties uz vienas platformas ērtībām. Koplietotās daļas tiek iebīdītas backendā un līgumos, un katrai platformai specifiskās daļas paliek plānas.
Šī disciplīna ir iemesls, kāpēc zemāk esošie vairāku platformu steki ir izvēle, nevis noklusējums — esmu uzturējis natīvo un darbvirsmas pusi, tāpēc zinu, ko katra abstrakcija man patiešām ietaupa.
- Windows klienti piegādāti līdzās mobilajam
- Koplietotā loģika iebīdīta backendā un līgumos
- Katras platformas kods saglabāts plāns un skaidrs
React Native un Flutter augstā līmenī, Xamarin darba līmenī
React Native un Flutter es ekspluatēju augstā līmenī. Es ķeros pie tiem, kad produktam vajadzīga viena komanda, kas ātri piegādā divus veikalus, un UI necīnās ar platformu — un nokāpju līdz natīvajiem moduļiem brīdī, kad ekrānam vajadzīgs Core Animation vai sensors, ko tiltiņš tīri neeksponē.
Xamarin man ir darba līmenī, kas svarīgi .NET orientētās organizācijās, kur pārējais steks jau ir C#. Jēga nav uzticībā framework; tā ir steka pielāgošanā komandai un produktam.
- React Native — augsts līmenis, ar natīvajiem moduļiem, kur vajadzīgs
- Flutter — augsts līmenis, viena koda bāze diviem veikaliem
- Xamarin — darba līmenis, .NET orientētām komandām
- Nokāpt līdz natīvajam, kad tiltiņš traucē
Swift tā, kā tas lasās uz reāla ekrāna.
Koda panelis ir godīgāks par funkciju sarakstu — tā ir iOS darba forma, nevis tā apraksts.
iOS es paļaujos uz SwiftUI jaunajām virsmām un Core Data objektu grafam aiz tām. Zemāk esošais fragments ir tāda veida, ko rakstu katru dienu: mazs, tipizēts modelis, fetch, ko skats var novērot, un izkārtojums, kas paliek deklaratīvs. Tas ir apzināti parasts — natīvās tekošās prasmes jēga ir tāda, ka rutīnas kods ir tīrs, nevis viltīgs.
import SwiftUI
import CoreData
struct ContactList: View {
@FetchRequest(
sortDescriptors: [SortDescriptor(\.name)]
) private var contacts: FetchedResults<Contact>
var body: some View {
List(contacts) { contact in
VStack(alignment: .leading) {
Text(contact.name)
.font(.headline)
Text(contact.phone)
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
.animation(.easeInOut, value: contacts.count)
}
} iOS frameworku kopa
SwiftUI virspusē, Core Data apakšā, SiriKit tur, kur balss ir ātrāka.
Frameworki saliekas kopā: SwiftUI renderē, Core Data noturē, Core Animation apstrādā kustību, ko SwiftUI neapstrādā, un SiriKit eksponē vienu vai divus nodomus, kur runātais saīsne patiešām pārspēj pieskaršanos. Objective-C paliek attēlā, jo ilgdzīvojošas lietotnes to vēl satur, un tā lasīšana ir daļa no to uzturēšanas.
Nekas no tā netiek pievienots pats par sevi. Katrs framework ir lietotnē, jo konkrēta problēma — noturība, kustība, balss — to pieprasīja.
- SwiftUI jauniem ekrāniem, UIKit saglabāts tur, kur tas darbojas
- Core Data objektu grafam
- Core Animation natīvi jūtamai kustībai
- SiriKit nodomi tikai tur, kur balss ir ātrāka
Kotlin pret Android SDK, ar visu fragmentāciju.
Android atalgo skaidrību. Zemāk esošais fragments ir Kotlin ViewModel, kas eksponē UI stāvokli kā flow — dzīves cikla apzinīgs, izdzīvo konfigurācijas izmaiņas un dara savu ielādi ārpus galvenā pavediena. Esot piegādājis daudzās OS versijās, es rakstu Android kodu, kas pieņem procesa nāvi un saspringtus fona ierobežojumus, nevis cer no tiem izvairīties.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
class ContactViewModel(
private val repo: ContactRepository
) : ViewModel() {
private val _state =
MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
init {
viewModelScope.launch {
runCatching { repo.loadContacts() }
.onSuccess { _state.value = UiState.Ready(it) }
.onFailure { _state.value = UiState.Error }
}
}
} Daudzas OS versijas, reālas ierīces
Atbalstīt tālruņus, ko lietotāji nēsā, nevis to no atklāšanas runas.
Android fragmentācija nav problēma, par ko sūdzēties — tā ir vide. Darbs ir izvēlēties godīgu minimālo SDK, testēt versiju un ekrānu izmēru klāstu, ko darbina reālā lietotāju bāze, un apstrādāt fona un atļauju noteikumus, kas saspringst ar katru laidienu.
Java paliek rīkkopā līdzās Kotlin tā paša iemesla dēļ, kāpēc Objective-C uz iOS: piegādātais kods dzīvo ilgi, un tā uzturēšana nozīmē to lasīt tekoši.
- Kotlin un Java pret Android SDK
- Godīgas minimālā SDK izvēles
- Testēts versijās, ko lietotāji darbina
- Skaidra fona un atļauju apstrāde
Lietotnes forma, uzzīmēta no UI līdz datu krātuvei.
Mobilā lietotne ir skaidru slāņu kaudze — un disciplīna ir tos saglabāt skaidrus, lai katru varētu testēt un nomainīt atsevišķi.
Lai kāda būtu platforma, arhitektūra, pie kuras ķeros, garā ir tā pati: UI slānis, kas tikai renderē stāvokli, domēna slānis, kas tur loģiku, datu slānis, kas sarunājas ar tīklu un ierīces krātuvi, un sinhronizācijas robeža pa vidu. Diagramma parāda slāņus un vienīgo virzienu, uz kuru norāda atkarības.
Bultu turēšana, norādot vienā virzienā, ir tas, kas padara lietotni uzturamu gadu vēlāk — UI nesniedzas tīklā, un tīkls nezina par UI. Tā pati forma turas, vai klients ir Swift, Kotlin, React Native vai Flutter.
UI → domēns → dati → krātuve
Četri slāņi, atkarības norādot vienā virzienā.
UI novēro stāvokli un izstaro nodomus; domēna slānis izlemj; datu slānis izgūst un noturē; SQLite tur patiesību ierīcē, un tīkls nes pārējo. Katra robeža ir vieta, kur varu ielikt testu vai nomainīt implementāciju, augšējiem slāņiem to nepamanot.
Tā nav arhitektūra pati par sevi. Tā ir struktūra, kas ļāva vienpadsmit gadu lietotnēm turpināt pieņemt funkcijas, nesabrūkot zem tām.
- UI renderē stāvokli, nekad nesniedzas tīklā
- Domēna slānis tur loģiku un noteikumus
- Datu slānim pieder tīkls un ierīces noturība
- SQLite kā patiesības avots ierīcē
Katras mobilās lietotnes servera puse.
Mobilais klients ir tik labs, cik backends, ar kuru tas runā — tāpēc es būvēju arī šo backendu, jebkurā framework, kas der komandai.
Aiz lietotnēm esmu piegādājis Node/Express, Spring Boot, Django, Flask, Ruby on Rails un Go ar Gin un Gorm. Framework tiek izvēlēts komandai un latentuma budžetam, nevis pēc ieraduma: Node tur, kur komanda tekoši pārvalda JavaScript, Spring tur, kur tā ir JVM māja, Django vai Flask Python smagiem backendiem, Rails konvenciju virzītam CRUD, un Go tur, kur budžets uz pieprasījumu ir saspringts.
Katrs eksponē līgumu — REST vai GraphQL —, pret kuru tiek būvēts mobilais klients, noturē caur ORM kā Sequelize un Mongoose, un kešo karsto ceļu Redis. Zemāk esošais process ir tas pats neatkarīgi no tā, kurš framework sēž pa vidu.
No līguma līdz sinhronizētai ierīcei
- 01 Definēt līgumu REST vai GraphQL shēma vispirms saskaņota — klients un serveris tiek būvēti pret vienu līgumu, nevis viens pret otru.
- 02 Izvēlēties framework Node/Express, Spring Boot, Django/Flask, Rails vai Go (Gin/Gorm) — izvēlēts, lai derētu komandai un latentuma budžetam.
- 03 Modelēt datus ORM — Sequelize vai Mongoose — virs relāciju vai dokumentu krātuves, kuru domēns patiešām vajag.
- 04 Kešot karsto ceļu Redis dārgo lasījumu priekšā, ar skaidru invalidāciju, nevis cerīgiem TTL.
- 05 Sinhronizēt ierīci SQLite ierīcē, sinhronizācijas protokols pār līgumu, konfliktu risināšana definēta pirms tā vajadzīga.
- 06 Piegādāt pīpeli Parakstīti būvējumi, automatizēti testi, piegāde veikaliem — CI, kas pārvērš commit būvējumā, ko lietotājs var instalēt.
func GetContact(c *gin.Context) {
id := c.Param("id")
if cached, err := rdb.Get(ctx, id).Result();
err == nil {
c.Data(200, "application/json",
[]byte(cached))
return
}
var contact Contact
if err := db.First(&contact, id).Error;
err != nil {
c.JSON(404, gin.H{"error": "not found"})
return
}
c.JSON(200, contact)
} Go apstrādātājs, tā, kā tas tiek piegādāts
REST un GraphQL, apkalpoti ātri un tipizēti.
Fragments ir Gin apstrādātājs, kas lasa caur Gorm ar Redis kešu priekšā — mazā, atkārtojamā galapunkta forma, uz kuru mobilais klients var paļauties: tipizēta atbilde, paredzama kļūda un kešs, kas noņem slodzi no datubāzes karstajā ceļā.
Vai framework ir Go, Node vai Spring, līgums, ko redz tālrunis, ir tas pats. Tā ir jēga to vispirms saskaņot.
- Go ar Gin un Gorm uz saspringta budžeta ceļiem
- Redis, kas kešo dārgos lasījumus
- Tipizētas atbildes, paredzamas kļūdu formas
- Tas pats līgums neatkarīgi no framework
Pilns mobilais steks — no klienta līdz mākonim
- iOS valodas
- Swift · Objective-C
- iOS frameworki
- SwiftUI · UIKit · Core Data · Core Animation · SiriKit
- Android valodas
- Kotlin · Java
- Android mērķis
- Android SDK, daudzas OS versijas
- Vairāku platformu
- React Native · Flutter (augsts līmenis) · Xamarin (darba līmenis)
- Darbvirsma
- Windows klienti
- Backend risinājumi
- Node/Express · Spring Boot · Django/Flask · Rails · Go (Gin/Gorm)
- API
- REST · GraphQL
- Ierīcē / dati
- SQLite · Redis · Sequelize · Mongoose
- Mobilais mākonis
- Firebase · AWS · Google Cloud
- Frontend amats
- HTML5 · CSS3 · SASS/SCSS · JS ES6+ · TypeScript
Kad tīkls atteic — un uz tālruņa tas atteiks.
Tālrunis pārvietojas pa tuneļiem, liftiem un mirušajām zonām. Lietotne, kas pieņem savienojamību, ir lietotne, kas iestrēgst pie griezēja reāla lietotāja priekšā.
Es būvēju mobilās lietotnes bezsaiste vispirms: ierīce nekavējoties raksta SQLite un ievieto izmaiņu rindā uz sinhronizāciju, lai lietotājs nekad negaida tīklu, lai redzētu savu paša darbību stājamies spēkā. Kad savienojamība atgriežas, rinda izsmeļas pār REST vai GraphQL līgumu, un konflikti tiek atrisināti pēc politikas, kas izlemta katrai entītijai, pirms tā vispār vajadzīga.
Diagramma izseko šo ciklu — lokāla rakstīšana, rinda, sinhronizācija, saskaņošana — un kļūmes režīms ir daļa, kas svarīga: lietotne degradējas uz tikai lasīšanu, nevis klusi pazaudē rakstījumu. Tā ir atšķirība starp lietotni, kurai lietotāji uzticas, un to, ko viņi pamet.
Lokāla rakstīšana → rinda → sinhronizācija → saskaņošana
Ierīce ir patiesības avots, līdz serveris piekrīt.
Katrs rakstījums vispirms nokļūst SQLite un tiek atspoguļots sinhronizācijas rindā. Fona sinhronizācija izsmeļ rindu pret serveri, serveris saskaņo, izmantojot Redis balstītus lasījumus un ORM kartēto krātuvi, un atrisinātais stāvoklis plūst atpakaļ uz ierīci. Lietotājs redz tūlītēju lokālu atgriezenisko saiti un galīgu konsekvenci, nevis griezēju.
Konfliktu politikas projektēšana iepriekš — last-write-wins, sapludināšana vai manuāla — ir tas, kas notur ciklu godīgu, kad divas ierīces rediģē to pašu ierakstu.
- Lokāla rakstīšana SQLite pirms jebkura tīkla izsaukuma
- Izmaiņa ievietota rindā, izsmelta, kad savienojamība atgriežas
- Konfliktu politika definēta katrai entītijai iepriekš
- Degradēties uz tikai lasīšanu, nekad nepazaudēt rakstījumu
Bezsaiste vispirms — līgums ar tīklu
- Krātuve ierīcē
- SQLite — patiesības avots, kamēr bezsaistē
- Rakstīšanas ceļš
- Lokāla rakstīšana vispirms, rindā uz sinhronizāciju
- Sinhronizācijas transports
- REST vai GraphQL pār saskaņoto līgumu
- Konfliktu politika
- Definēta katrai entītijai pirms tā vajadzīga
- Servera kešs
- Redis dārgo lasījumu priekšā
- Servera krātuve
- Relāciju vai dokumentu, caur Sequelize vai Mongoose
- Kļūmes režīms
- Degradēties uz tikai lasīšanu, nekad klusi nepazaudēt rakstījumu
Lietotājam nekad nevajadzētu gaidīt tīklu, lai redzētu savu paša darbību. Rakstīt lokāli, sinhronizēt fonā un nekad nepazaudēt rakstījumu — tā ir visa bezsaiste-vispirms disciplīna.
Firebase, AWS un Google Cloud — izvēlēti pēc atbilstības.
Mākonis nav viens lēmums. Mazai komandai vajadzīgs backends uz vakar; produktam, kas mērogojas, vajadzīga kontrole pār savu skaitļošanu. Katra cilne ir platforma, kurā izvietoju mobilos backendus, un spriedums par to, kad katra ir pareizā atbilde.
Firebase, kad mazai komandai vajadzīgs backend uz vakar
Firebase pelna savu vietu, kad mobilam produktam vajadzīga autentifikācija, sinhronizēta datu krātuve un push, vispirms neuzceļot infrastruktūru. Es izmantoju Auth pieteikšanās, Firestore strukturētiem, vaicājamiem datiem, un Realtime Database tur, kur piekļuves raksts patiešām ir dzīvs koks, nevis dokumenti.
Disciplīna ir zināt, kur Firebase pārstāj būt pareizā atbilde — drošības noteikumi, kuriem jākodē reāla autorizācija, un lasīšanas izmaksas, kas aug ar naivu datu modeli. Es projektēju dokumentus ap vaicājumiem, nevis otrādi.
- Auth pieteikšanās, Firestore strukturētiem datiem
- Realtime Database tur, kur dzīvs koks der piekļuves rakstam
- Drošības noteikumi uzskatīti par reālu autorizāciju
- Dokumenti projektēti ap vaicājumiem
AWS backendiem, kas pāraug BaaS
Kad produktam vajadzīga kontrole pār savu skaitļošanu, es to darbinu uz AWS — Lambda notikumvirzītām un pīķveida slodzēm, EC2 tur, kur nepieciešams ilgdzīvojošs process vai specifiska izpildvide, un S3 medijiem un statiskajām slodzēm, ko mobilais klients lejupielādē.
Mobilā maina ierobežojumus: aukstos startus jūt uz tālruņa, augšupielādes atkārtojumiem jāizdzīvo nestabils mobilo sakaru savienojums, un ierīce nevar turēt savienojumu atvērtu tā, kā to var serveris. Arhitektūra ņem vērā tīklu, kurā ierīce patiešām atrodas.
- Lambda notikumvirzītām un pīķveida slodzēm
- EC2 ilgdzīvojošiem procesiem un specifiskām izpildvidēm
- S3 medijiem un lejupielādējamām slodzēm
- Projektēts nestabiliem mobilajiem sakariem, nevis datu centra LAN
Google Cloud tur, kur platforma der slodzei
Google Cloud es izmantoju App Engine pārvaldītiem pakalpojumiem, kas mērogojas, man neuzraugot instances, un Compute Engine tur, kur slodzei vajadzīga pilna VM. Tas dabiski savienojas ar Firebase, kad daļa produkta ir pārvaldīta datu krātuve un daļa — pielāgota skaitļošana.
Izvēle starp mākoņiem reti ir ideoloģiska. Tas ir par to, kuri pārvaldītie pakalpojumi noņem visvairāk operatīvā darba šai komandai, vienlaikus saglabājot budžetā latentumu, ko redz mobilais klients.
- App Engine pārvaldītiem, automātiski mērogojamiem pakalpojumiem
- Compute Engine tur, kur nepieciešama pilna VM
- Savienojas ar Firebase jauktiem pārvaldītiem/pielāgotiem backendiem
- Izvēlēts pēc operatīvās atbilstības, nevis uzticības
Kur backends patiešām darbojas
Pārvaldīts tur, kur tas ietaupa darbu, kontrolēts tur, kur tam jābūt.
Firebase Auth un Firestore uzceļ sinhronizētu backendu bez infrastruktūras; Lambda un EC2 dod serverless un ilgdzīvojošu skaitļošanu uz AWS; App Engine un Compute Engine dara to pašu uz Google Cloud; S3 tur medijus, ko tālrunis lejupielādē. Diagramma kartē klientu uz šīm opcijām.
Mobilais ierobežojums iet cauri tam visam: aukstos startus jūt uz tālruņa, un augšupielādēm jāizdzīvo mobilo sakaru savienojums. Mākonis tiek izvēlēts tā, lai latentums, ko redz lietotājs, paliek budžetā.
- Firebase — Auth, Firestore, Realtime Database
- AWS — Lambda, EC2, S3
- Google Cloud — App Engine, Compute Engine
- Izvēlēts pēc operatīvās atbilstības, latentums budžetā
React Native un Flutter — izvēle, nevis noklusējums.
Vairāku platformu steki ļauj vienai komandai piegādāt abus veikalus no vienas koda bāzes, un es ekspluatēju React Native un Flutter augstā līmenī. Bet iemesls, kāpēc es tiem uzticos, ir tas, ka esmu piegādājis natīvi vispirms: es precīzi zinu, ko tiltiņš man ietaupa un kur tas traucē. Kad ekrānam vajadzīgs Core Animation, sensors, ko tiltiņš tīri neeksponē, vai platformas uzvedība, ko abstrakcija aizsedz, es nokāpju līdz natīvajam modulim bez vilcināšanās.
Xamarin atrodas darba līmenī rīkkopā, kas ir pareizā atbilde .NET orientētās organizācijās, kur pārējais steks jau ir C#. Lēmums starp tiem nekad nav par uzticību — tas ir par steka pielāgošanu komandai un produktam, ar natīvo vienmēr pieejamu zemāk.
Viena koda bāze, divi veikali, natīvā glābšanas lūka
Koplietota biznesa loģika virspusē, natīvie moduļi tur, kur tiltiņš apstājas.
Vairāku platformu lietotnē biznesa loģika un lielākā daļa UI dzīvo vienā koplietotā koda bāzē — React Native vai Flutter —, kas renderē gan iOS, gan Android. Daļas, ko abstrakcija nevar apkalpot, izkrīt līdz natīvajiem moduļiem Swift un Kotlin, lai lietotne nekad netirgo spēju pret pārnesamību.
Šī glābšanas lūka ir viss iemesls, kāpēc natīvā tekošā prasme ir svarīga pat vairāku platformu projektā. Koplietotais slānis aptver kopīgo zemi; natīvie moduļi aptver malas.
- Koplietota loģika un UI vienā koda bāzē
- Renderē gan iOS, gan Android
- Natīvie moduļi Swift / Kotlin malās
- Spēja nekad netiek tirgota pret pārnesamību
Pīpelis, kas pārvērš commit instalācijā.
Mobilais CI ir grūtāks par tīmekļa CI — ir divas rīkkopas, koda parakstīšana un divi veikala izskates procesi starp commit un lietotāju.
Mobilais laidiens nav izvietošana; tā ir būve, paraksts un iesniegšana. Pīpelis, ko darbinu, vispirms lint un tipu pārbauda, tad būvē iOS archive ar Xcode un to paraksta, būvē parakstīto Android APK vai AAB ar Gradle un nosūta versionētus artefaktus uz TestFlight un iekšējo kanālu pirms veikala. Noteikums ir vienkāršs: nav zaļš pīpelis, nav laidiena.
Zemāk esošā diagramma parāda šo izvēršanos — viens commit, divas rīkkopas, divi veikali — jo cena par kļūdu koda parakstīšanā vai versijas paaugstināšanā ir noraidīta iesniegšana un zaudēta diena, nevis ātra atkārtota izvietošana.
Commit → pārbaudes → divas būves → divi veikali
Viens commit izvēršas divās parakstītās būvēs.
Pīpelis palaiž statiskās pārbaudes vienreiz, tad sadalās: Xcode ceļš, kas būvē, paraksta un arhivē iOS, un Gradle ceļš, kas saliek un paraksta Android. Katrs ražo versionētu artefaktu, kas iet uz sava veikala testa kanālu pirms laidiena.
Parakstītās būves uzskatīšana par vienīgo, kas tiek piegādāts — nekad lokāls archive no kāda mašīnas — ir tas, kas notur laidienus reproducējamus visā vienpadsmit gadu lietotņu garumā.
- Lint, tipu pārbaude un vienības testi kā vārti
- Xcode paraksta un arhivē iOS
- Gradle saliek parakstītu APK/AAB Android
- Versionēti artefakti uz testa kanāliem, tad veikals
Mobilais CI — stadija pa stadijai
- Trigeris
- Commit laidiena zarā
- Statiskās pārbaudes
- Lint, tipu pārbaude (TypeScript), vienības testi
- iOS būve
- Xcode būve, koda parakstīšana, archive
- Android būve
- Gradle assemble, parakstīts APK/AAB
- Artefakts
- Versionēts būvējums katrai platformai
- Piegāde
- TestFlight / iekšējais kanāls, tad veikals
- Vārti
- Nav zaļš pīpelis, nav laidiena
Tīmekļa virsmas, kas sēž līdzās lietotnei.
Mobilais produkts reti ir tikai lietotne. Ir uzņemšanas lapas, mārketinga virsmas, iegultie tīmekļa skati un koplietota loģika, kas visa grib to pašu rūpību kā natīvais kods. Es tos būvēju tīrā HTML5 un CSS3, organizēju stilus ar SASS/SCSS, lai dizaina sistēma paliktu sistēma, un rakstu loģiku mūsdienu JavaScript — ES6+ un TypeScript visur, kur projekts pieļauj tipu sistēmu.
TypeScript jo īpaši atmaksājas mobilajā: tipizēts līgums noķer neatbilstību starp klientu un serveri, pirms lietotājs to vispār redz, un tie paši tipi var plūst no GraphQL shēmas līdz React Native klientam.
HTML5 un CSS3
Tīmekļa virsmas, kas sēž līdzās mobilajai lietotnei — uzņemšanas lapas, mārketings, iegultie tīmekļa skati — būvētas tīrā HTML5 un CSS3, nevis refleksīvi iemestas framework.
SASS/SCSS
Stila lapas, organizētas ar SASS/SCSS, lai dizaina sistēma paliktu sistēma: mainīgie, partials un mixins, nevis kopētas deklarācijas.
JavaScript ES6+
Mūsdienu JavaScript koplietotajai loģikai un tīmekļa slānim — moduļi, async/await un valodas iespējas, kas padara koda bāzi lasāmu gadus vēlāk.
TypeScript
TypeScript visur, kur projekts to pieļauj, jo tipizēts līgums noķer neatbilstību starp klientu un serveri, pirms lietotājs to vispār redz.
REST API
REST vienkāršajiem, resursveida galapunktiem, ar versionēšanu un paredzamām kļūdu formām, uz kurām mobilais klients var paļauties.
GraphQL API
GraphQL tur, kur mobilam ekrānam vajadzīgi tieši tā dati vienā turp-atpakaļ braucienā — tīkla brauciens, ko tālrunis maksā, ir tas, ko vērts ietaupīt.
Vienpadsmit gadi, viens platformas lēmums vienlaikus.
Pamats, natīvais, vairāku platformu, backend, mākonis — tas pats inženieris, sekojot darbam, kamēr produkti pieauga aiz to pirmajiem tūkstoš lietotājiem.
Lasīts secībā, mobilais darbs ir viena nepārtraukta trase, nevis frameworku saraksts. Tas sākas ar vienpadsmit gadiem piegādes reāliem lietotājiem, padziļinās natīvajā iOS un Android, paplašinās caur React Native un Flutter tur, kur tie der, sniedzas atpakaļ backendos, kas padara klientu par vairāk nekā apvalku, un nonāk pie mākoņiem, kas to mērogo.
Tas, kas iet cauri visam, ir tas pats atteikums, kas caurvij pārējo manu darbu: lietotne un sistēma aiz tās ir viena problēma, nevis divas, sadalītas starp diviem cilvēkiem.
- Pamats Vienpadsmit gadi piegādes reāliem lietotājiem Trase, kas noenkuro visu pārējo: vairāk nekā vienpadsmit gadi, būvējot un piegādājot mobilās lietotnes, ko reāli cilvēki instalēja un lietoja, iOS, Android un Windows platformās.
- Natīvais iOS un Android to pašu valodās Swift un Objective-C uz iOS ar SwiftUI, Core Data, Core Animation un SiriKit; Kotlin un Java uz Android daudzās OS versijās. Natīvā tekošā prasme, kas padara vairāku platformu izvēles informētas.
- Vairāku platformu React Native un Flutter augstā līmenī Viena komanda, kas piegādā divus veikalus, kur tas der, nokāpjot līdz natīvajiem moduļiem, kur ne — plus Xamarin darba līmenī .NET orientētām komandām.
- Backend Katras lietotnes servera puse Node/Express, Spring Boot, Django/Flask, Rails un Go (Gin/Gorm), eksponējot REST un GraphQL, balstīti uz SQLite, Redis un ORM — backendi, kas padara mobilo klientu par vairāk nekā plānu apvalku.
- Mākonis Firebase, AWS un Google Cloud Pārvaldītas datu krātuves, serverless skaitļošana un glabātuve, izvēlētas pēc operatīvās atbilstības — infrastruktūra, kas mērogo lietotni aiz tās pirmajiem tūkstoš lietotājiem.
Principi zem platformām.
Platformas un frameworki mainās ar produktu; principi ne. Šie ir noteikumi, ko piemēroju, vai lietotne ir natīvs Swift, natīvs Kotlin, React Native vai Flutter — daļa, kas vienpadsmit gadu mobilo darbu pārvērta lietotnēs, kas turpināja darboties, nevis palaišanu portfelī.
Natīvā tekošā prasme nopelna abstrakciju
Tā kā esmu piegādājis Swift un Kotlin tieši, React Native vai Flutter izvēle ir mērīts kompromiss, nevis veids, kā izvairīties no platformas apgūšanas. Es zinu, ko tiltiņš man ietaupa un ko tas slēpj.
Līgums nāk pirmais
Klients un serveris tiek būvēti pret vienu saskaņotu REST vai GraphQL līgumu, ideālā gadījumā tipizētu no sākuma līdz beigām. Integrācija tiek projektēta, pirms kāda no pusēm uzrakstīta, nevis apspriesta pēc tam, kad abas salūst.
Pieņem, ka tīkls atteiks
Tālrunis pārvietojas pa mirušajām zonām. Lietotne vispirms raksta lokāli SQLite, ievieto rindā uz sinhronizāciju un degradējas uz tikai lasīšanu, nevis pazaudē rakstījumu vai iestrēgst pie griezēja.
Respektē platformas noteikumus
Fona ierobežojumi, procesa nāve, veikala izskate, koda parakstīšana — tie nav šķēršļi, ko apiet. Strādāt ar tiem ir tas, kas notur lietotni instalētu, nevis atinstalētu.
Testē fragmentāciju
Daudzas Android OS versijas un iOS ierīču klāsts nozīmē testēt to, ko lietotāji patiešām nēsā, nevis apgalvot, ka jaunākais laidiens aptver visus.
Vienpadsmit gadi ir apliecinājums
Katrs apgalvojums šeit ir balstīts uz lietotnēm, kas tika piegādātas un palika piegādātas. Ilgmūžība ir pierādījums — nevis framework uz slaida, bet programmatūra, kas izdzīvoja saskari ar reāliem lietotājiem.
Vienpadsmit gadi lietotņu, kas tika piegādātas un palika piegādātas, ir apliecinājums. Viss pārējais šajā lapā ir patiess tāpēc, ka šī viena lieta ir.
Open to the right work
Ja jūsu produkts ir mobilā lietotne un backends, mākonis un bezsaistes uzvedība, kas to padara uzticamu, tā ir visa problēma, ko es vēlos.
If you are holding a problem that doesn't fit inside one field, that is the conversation I want.