iOS-prosjekt beste praksis og verktøy

Med en åpen kildekode for Xcode-prosjekter

Når jeg jobbet med greenfield iOS-prosjekter måtte jeg ofte starte et nytt prosjekt fra bunnen av. Mens jeg gjorde det, brukte jeg og teamet mitt alltid mye tid på grunnleggende prosjektoppsett som å integrere verktøy, sette opp prosjektstrukturen, skrive baseklasser, integrere eksterne biblioteker osv.

Jeg bestemte meg for at tidsbruken på oppstart av prosjektet kunne spares og prosessen hovedsakelig kunne automatiseres. Jeg skrev ned de vanlige beste praksisene og verktøyene vi brukte og utarbeidet en prosjektmal som jeg og teamet mitt kunne bruke når vi startet nye prosjekter. Denne malen skal spare prosjektoppsettetid og også gi et felles fundament som hvert teammedlem vil være vant til, slik at du ikke trenger å tenke og utforske prosjektstrukturen og grunnlaget. De vil alltid være de samme.

Hvert verktøy eller beste praksis inkludert i malen fortjener en artikkel på egen hånd, men jeg ville prøve å oppsummere hvert punkt og gi en kort forklaring på hvorfor jeg inkluderte dem.

Cocoapods

Jeg tror ikke denne trenger en introduksjon. Dette er biblioteket for å håndtere eksterne avhengigheter for iOS-prosjekter. Det har eksistert i lang tid og er robust og slått testet i tusenvis (om ikke millioner) prosjekter. Det er alternative avhengighetsledere der ute som Carthage, men jeg bestemte meg for å gå med Cocoapods fordi det har det bredeste spekteret av åpen kildekode-prosjekt som den støtter. Det er veldig enkelt å bruke Cocoapods, og det kommer med en søkeindeks som lar deg enkelt oppdage pakker du måtte trenge.

Malprosjektet kommer med en enkel Podfile som inkluderer Swiftlint og R.swift. Malen inneholder også en Gemfile for å administrere Cocoapods-versjonen som brukes til å løse avhengighetene. Dette er en ofte oversett forbedring som forhindrer problemer som oppstår når utviklere i teamet ditt installerer avhengigheter ved å bruke forskjellige versjoner av Cocoapods selv. Gemfile håndhever å bruke den samme Cocoapods-versjonen i hele teamet.

Swiftlint

Swiftlint er et veldig nyttig verktøy for å håndheve visse regler og kodestil for hver programmerer på teamet. Du kan tenke på det som et automatisert kodevurderingssystem som advarer programmereren om farlige ting som å tvinge ut pakker, tvinge utkast, tvinge forsøk osv., Men også håndhever en vanlig kodingsstil ved å sørge for at alle programmerere følger de samme "kodestilen" relaterte reglene som innrykk eller avstandsregler. Dette har enorme fordeler med ikke bare å spare tid på kodegjennomgang ved å gjøre de grunnleggende kontrollene, men også at det gjør at alle filene i prosjektet ser kjent ut, noe som øker deres lesbarhet og som et resultat deres forståelse av alle devs. Du kan finne en liste over alle reglene her. I malen blir Swiftlint installert via Cocoapods og inkludert i byggefasetrinnet, slik at det linser og advarer utvikleren ved hvert prosjektbygg.

R.swift

R.swift er et verktøy for å få sterkt skrevne, autofullførende ressurser som bilder, fontsegmenter og lokaliseringer. Det gjør dette ved å skanne prosjektet ditt og generere raske klasser som trengs for å få ressursene. Det største salgsargumentet for dette biblioteket er at mens du bruker ressurser, lager den koden:

  • Fullt skrevet - mindre avstøpning og gjett hva en metode vil returnere
  • Sett tid kontrollert - ikke flere uriktige strenger som gjør at appen din krasjer under kjøretid
  • Autofullført - trenger aldri å gjette det bildet / nib / storyboard-navnet igjen

Tenk på følgende kode ved å bruke den offisielle streng-API:

la ikon = UIImage (kalt: "tilpasset-ikon")

Hvis du stave feil navnet på bildet, får du null her. Hvis noe av teamet ditt endrer navnet på bildressursen, vil denne koden gå tilbake eller krasje hvis du tvinger opp pakken. Når du bruker R.swift blir dette:

la ikon = R.image.customIcon ()

Nå kan du være sikker på at ikonet virkelig eksisterer (kompilatoren vil advare deg hvis det ikke er takket være kompilering av tidskontroller), og du er sikker på at du ikke vil lage en skrivefeil i ikonnavnet, siden du bruker autofullfør.

R.swift er installert via Cocoapods og integrert i malen som en byggefase og vil generere Swift-innpakningsklassene på hvert bygg. Det betyr at hvis du legger til en fil / bilde / lokalisering / font / color / nib osv., Vil den være tilgjengelig for deg ved å bruke R.swift når du har satt sammen prosjektet.

Separat AppDelegate for tester

En ofte oversett god praksis er å ha en egen TestAppDelegate-klasse når du kjører tester. Hvorfor er det lurt? Vel, vanligvis gjør AppDelegate-klassen mye arbeid ved oppstart av appen. Det kan sette opp et vindu, bygge den grunnleggende brukergrensesnittet for appen, registrere deg for varsler, sette opp en database og til og med ringe API-anrop til noen backend-tjenester. Enhetstester skal ikke ha noen bivirkninger. Vil du virkelig ikke foreta tilfeldige api-samtaler og konfigurere hele UI-strukturen til appen din bare for å kjøre noen enhetstester?

TestAppDelegate er også et flott sted å ha kode som du bare vil kjøre en gang under testpakken. Den kan inneholde kode som genererer mock, stubber nettverksforespørsler osv.

Malen inneholder en main.swift-fil som er hovedinngangspunktet for appen. Denne filen har metoder som sjekker hva som er miljøet appen kjører for øyeblikket, og hvis det er testmiljøet, påkaller TestAppDelegate.

Kompilatorprestasjonsprofileringsflagg

Swift er et flott språk, enklere å bruke og mye tryggere enn Objekt-C (IMO). Men da den ble introdusert for første gang, hadde den en stor ulempe - sammenstille tider. Tilbake i Swift 2 dager jobbet jeg på et prosjekt som hadde roughly 40k linjer med Swift-kode (et mellomstort prosjekt). Koden var veldig tung med generika og type inferanse, og det tok nesten 5 minutter å lage en ren konstruksjon. Når du foretok til og med en liten endring, ville prosjektet kompilere på nytt og det tok rundt 2 minutter å se endringen. Det var en av de verste utvikleropplevelsene jeg noensinne har hatt, og jeg sluttet nesten å bruke Swift på grunn av det.

Den eneste løsningen på det tidspunktet var å prøve å profilere kompileringstidene for prosjektet og prøve å endre koden din på en slik måte, som ville gjøre kompilatoren raskere. For å hjelpe med dette introduserer Apple noen uoffisielle kompilatorflagg som vil advare deg når du kompilerer en metodekropp eller løser typen uttrykk tok for lang tid. Jeg la flaggene til malprosjektet, så du vil bli advart fra starten om lange sammenstiltider for appen din.

I dag har byggetidene blitt dramatisk forbedret, og du trenger sjelden å finpusse koden bare for å forbedre byggetidene. Men likevel er det bedre å vite hvordan du prøver å løse problemet når prosjektet blir for stort.

Dev / Staging / Production konfigurasjoner

En annen god praksis (eller kanskje sier jeg nødvendighet) er å ha separate konfigurasjoner og miljøvariabler for utviklings-, iscenesettelses- og produksjonsmiljøer. Nesten hver app i dag må koble seg til en slags backend-tjeneste, og vanligvis er disse tjenestene distribuert i flere miljøer. Utviklingsmiljøet brukes til daglige distribusjoner og for devs å teste koden deres. Iscenesettelsesmiljøet brukes til stabile utgivelser for testere og klienter å teste. Vi vet alle hva produksjonsmiljøet er for.

En måte å støtte flere miljøer i et iOS-prosjekt på er å legge til konfigurasjonsnivåer på prosjektnivå.

Konfigurasjoner av prosjektnivå

Når du har definert konfigurasjonene, kan du opprette en Configuration.plist-fil som inneholder variablene for hvert miljø.

Configuration.plist

Når du kjører prosjektet, kan du spesifisere hvilken konfigurasjon som skal brukes. Du kan gjøre dette i byggeskemaet.

Du må deretter legge til en ekstra egenskap i prosjektet Info.plist-filen. Verdien av denne egenskapen vil bli løst dynamisk ved kjøretid til navnet på den nåværende konfigurasjonen.

Dette er alt forhåndsinnstilt for deg i malen.

Det eneste som gjenstår er å skrive en klasse som kan hente disse variablene under kjøretid, avhengig av konfigurasjonen som er valgt i byggeskemaet. Malen inneholder en ConfigurationManager-klasse som kan hente variablene for det gjeldende miljøet. Du kan sjekke implementeringen av den klassen på Github for å se hvordan den fungerer.

readme

Hvert prosjekt skal ha en grunnleggende Readme som i det minste har instruksjoner om hvordan du installerer avhengigheter og kjøre prosjektet. Den skal også inneholde beskrivelser av prosjektarkitekturen og modulene. Dessverre liker ikke utviklere å skrive dokumentasjon (en readme er en del av det), og jeg har sett prosjekt som ble utviklet i flere måneder, og de hadde til og med et grunnleggende Readme. For å fjerne byrden ved å skrive dette grunnleggende readme, inneholder malen en standard readme som dekker installasjon og prosjektstruktur. Når du setter opp et nytt prosjekt ved å bruke malen, vil du ha Readme automatisk inkludert.

Gitignore

I dag bruker de fleste prosjekter GIT som sitt versjonskontrollsystem. Når du bruker GIT, vil du vanligvis ikke ignorere noen filer eller mapper i prosjektet som build-mappen eller den avledede datamappen. For å spare deg for problemer med å søke etter en gitignore-fil som passer til iOS-prosjektet ditt, inneholder malen en standard gitignore levert av Github-bidragsytere.

Baseklasser for håndtering av dypkoblinger og varsler

Nesten hver app i dag må håndtere dypkoblinger og varsler. For å gjøre dette, må en utvikler skrive en viss mengde kjeleplate-kode i AppDelegate-klassen. Malen har det dekket og gir også baseklasser som gjør det lettere å jobbe med dypkoblinger og varsler.

Sammendrag

For å oppsummere, prøver malen å inkludere beste fremgangsmåter og integrerer nyttige tredjepartsverktøy. Dette skal spare deg og vårt team timer brukt på nytt prosjektoppsett og også gi et felles og sterkt grunnlag for resten av prosjektet. Måtte det tjene deg godt!

PS: Hvis du har problemer eller forespørsler om funksjoner for malen, kan du bare la meg være et problem på Github. Jeg vil prøve å løse det på fritiden.