Ismerje meg az iOS bevált gyakorlatait egy egyszerű receptek alkalmazás létrehozásával

Forrás: ChefStep

Tartalomjegyzék

  • Elkezdeni
  • Xcode és Swift verzió
  • Minimális támogatott iOS-verzió
  • Az Xcode projekt megszervezése
  • Receptek alkalmazás felépítése
  • Kódmegállapodások
  • Dokumentáció
  • A kódrészek jelölése
  • Forrásvezérlés
  • Dependencies
  • Bejutás a projektbe
  • API
  • Indító képernyő
  • App ikonra
  • Világítási kód a SwiftLint segítségével
  • Típusbiztos erőforrás
  • Mutasd meg a kódot
  • A modell megtervezése
  • Jobb navigáció a FlowController segítségével
  • Automatikus elrendezés
  • Építészet
  • Hatalmas látásvezérlő
  • Hozzáférés-szabályozás
  • Lusta tulajdonságok
  • Kódrészletek
  • Hálózat
  • Hogyan kell tesztelni a hálózati kódot
  • Gyorsítótár bevezetése az offline támogatáshoz
  • A Cache tesztelése
  • Távoli képek betöltése
  • A képek betöltése kényelmesebbé válik az UIImageView számára
  • Általános adatforrás az UITableView és UICollectionView számára
  • Vezérlő és nézet
  • A felelősségek kezelése a gyermek nézővezérlővel
  • Összetétel és függőség-befecskendezés
  • App szállítási biztonság
  • Egyéni gördíthető nézet
  • Keresési funkciók hozzáadása
  • A prezentációs környezet megértése
  • A keresési műveletek feloldása
  • A debouncing tesztelése fordított elvárással
  • A felhasználói felület tesztelése az UITestekkel
  • Fő menetvédő
  • Előadások és kérdések mérése
  • Prototípuskészítés játszótérrel
  • Hová menj innen?

Az iOS fejlesztését akkor kezdtem el, amikor az iOS 7 bejelentésre került. És egy kicsit megtanultam munkamódszereken keresztül, kollégáim és az iOS közösség tanácsaival.

Ebben a cikkben szeretnék megosztani sok jó gyakorlatot egy egyszerű receptek alkalmazásának példájával. A forráskód a GitHub Recipes webhelyen található.

Az alkalmazás egy hagyományos mester részlet alkalmazás, amely bemutatja a receptek listáját és a részletes információkat.

A probléma megoldásának ezrei vannak, és a probléma kezelésének módja a személyes ízléstől is függ. Remélhetőleg ebben a cikkben megtanul valami hasznosat - sokat tanultam, amikor ezt a projektet készítettem.

Néhány kulcsszóhoz csatoltam linkeket, ahol úgy éreztem, hogy a további olvasás hasznos lehet. Tehát feltétlenül nézd meg őket. Bármilyen visszajelzés örvendetes.

Tehát kezdjük el ...

Itt található egy magas szintű áttekintés arról, hogy mit fog építeni.

Elkezdeni

Döntsük el az általunk használt eszköz- és projektbeállításokat.

Xcode és Swift verzió

A WWDC 2018-on az Apple bemutatta az Xcode 10-et a Swift 4.2-tel. Az írás idején azonban az Xcode 10 még mindig béta 5 volt. Tehát tartsuk be a stabil Xcode 9-et és a Swift 4.1-et. Az Xcode 4.2 rendelkezik néhány jó tulajdonsággal - játszhat vele ezen a fantasztikus játéktéren. Ez nem vezet be hatalmas változásokat a Swift 4.1 verziójához képest, így szükség esetén könnyedén frissíthetjük alkalmazásunkat a közeljövőben.

A Swift verziót a Projekt beállításban kell beállítania, a célbeállítás helyett. Ez azt jelenti, hogy a projekt minden céljának ugyanaz a Swift verziója van (4.1).

Minimális támogatott iOS-verzió

2018 nyarától kezdve az iOS 12 nyilvános béta 5-ben van, és nem célozhatjuk az iOS 12-et Xcode 10 nélkül. Ebben a bejegyzésben az Xcode 9-et használjuk, és az alap SDK iOS 11. Az igénytől és a felhasználói alaptól függően egyes alkalmazások támogatnia kell a régi iOS verziókat. Bár az iOS-felhasználók általában gyorsabban fogadnak el új iOS-verziókat, mint azok, akik az Android-ot használják, vannak olyanok, amelyek a régi verziókkal maradnak. Az Apple tanácsai szerint támogatnunk kell a legfrissebb két verziót, az iOS 10 és az iOS 11-et. Az App Store 2018. május 31-i mérése szerint a felhasználók csak 5% -a használja az iOS 9-et és azt megelőzőt.

Az új iOS-verziók célzása azt jelenti, hogy kihasználhatjuk az új SDK-k előnyeit, amelyeket az Apple mérnökei évente tovább fejlesztnek. Az Apple fejlesztői webhelyén javult a változásnapló-nézet. Most könnyebb látni, hogy mi lett hozzáadva vagy módosítva.

Ideális esetben annak meghatározásához, hogy mikor kell lehagyni a régi iOS-verziók támogatását, elemzésre van szükségünk arról, hogy a felhasználók hogyan használják az alkalmazásunkat.

Az Xcode projekt megszervezése

Az új projekt létrehozásakor válassza az „Include Unit Tests” és az „Inc UI Tests” elemet, mivel ez javasolt módszer a tesztek korai írására. Az XCTest keretrendszerének legutóbbi változásai, különösen az UI tesztekben, megkönnyítik a tesztelést, és elég stabilak.

Mielőtt új fájlokat adna a projekthez, tartson szünetet, és gondoljon át alkalmazásának felépítésére. Hogyan akarjuk rendezni a fájlokat? Van néhány lehetőségünk. Fájlokat funkció / modul vagy szerep / típus szerint lehet rendezni. Mindegyiknek megvan a maga előnye és hátránya, és az alábbiakban tárgyalom ezeket.

Szerep / típus szerint:

  • Előnyök: Kevésbé gondolkodik azon, hogy hol helyezze el a fájlokat. A szkriptek vagy szűrők alkalmazása is könnyebb.
  • Hátrányok: Nehéz összevetni, ha szeretnénk-e több fájlt találni ugyanahhoz a szolgáltatáshoz. Időbe telik a fájlok átszervezése is, ha a jövőben újrafelhasználható elemekké akarjuk őket tenni.

Funkció / modul szerint

  • Előnyök: Mindent modulárissá tesz és ösztönzi a kompozíciót.
  • Hátrányok: rendetlen lehet, ha sok különféle fájlt összecsomagolnak.

Maradjon modulárisan

Személy szerint megpróbálom a kódomat a lehetőségek / elemek szerint rendezni, amennyire csak lehetséges. Ez megkönnyíti a kapcsolódó kijavítandó kód azonosítását, valamint a jövőben könnyebb új funkciók hozzáadását. Válaszol a „Mit csinál ez az alkalmazás?” Kérdésre, a „Mi ez a fájl?” Helyett. Itt egy jó cikk erről.

Egy jó hüvelykujjszabály az, hogy következetes maradjon, függetlenül attól, hogy melyik felépítést választja.

Receptek alkalmazás felépítése

Az alábbiakban bemutatjuk az alkalmazás szerkezetét, amelyet a recept alkalmazásunk használ:

Forrás

Forráskód fájlokat tartalmaz, komponensekre bontva:

  • Jellemzők: a főbb jellemzők az alkalmazásban
  • Kezdőlap: a kezdőképernyő, a receptek listája és a nyílt keresés
  • Lista: megjeleníti a receptek listáját, ideértve a recept újratöltését és az üres nézet megjelenítését, ha recept nem létezik
  • Keresés: a keresés és a debounálás kezelése
  • Részlet: Részletes információkat jelenít meg

Könyvtár

Az alkalmazás fő alkotóelemeit tartalmazza:

  • Flow: tartalmazza a FlowControllert az áramlások kezelésére
  • Adapter: általános adatforrás az UICollectionView-hoz
  • Bővítés: kényelmes kiterjesztések a közös műveletekhez
  • Modell: A modell az alkalmazásban, a JSON elemzésével

Forrás

Plist, erőforrás és Storyboard fájlokat tartalmaz.

Kódmegállapodások

Egyetértek a raywenderlich / swift-style-guide és a github / swift-style-útmutató legtöbb stíluskalauzával. Ezek egyszerűek és ésszerűek a Swift projektekben történő felhasználáshoz. Ezenkívül olvassa el az Apple Swift csapata által készített hivatalos API-tervezési irányelveket arról, hogyan lehet jobb Swift-kódot írni.

Bármelyik stílusú útmutatót is választja, a kód tisztaságának kell lennie a legfontosabb célnak.

A behúzás és a tabulátor-háború érzékeny téma, de ismét az ízléstől függ. Négy szóköz behúzást használok Android projektekben, valamint két teret az iOS-ben és a React-ban. Ebben a Receptek alkalmazásban következetes és könnyen indokolható behúzást követek, amiről itt és itt írtam.

Dokumentáció

A jó kódnak világosan meg kell magyaráznia magát, így nem kell megjegyzéseket írni. Ha a kódrészlet nehezen érthető, akkor érdemes szünetet tartani, és megismételni azt néhány módszerrel leíró nevekkel, így a kódrészlet egyértelműbben megérthető. Ugyanakkor azt találom, hogy a dokumentációs osztályok és módszerek szintén jók a munkatársak és a jövő önmaga számára. A Swift API tervezési irányelveinek megfelelően

Írjon dokumentációs megjegyzést minden nyilatkozathoz. A dokumentáció írásával nyert információk nagymértékben befolyásolhatják a formatervezést, ezért ne tedd le.

Nagyon könnyű megjegyzés sablont készíteni /// az Xcode-ban a Cmd + Alt + / billentyűkkel. Ha azt tervezi, hogy újból átalakítja a kódot egy keretrendszerbe, hogy azt a jövőben megoszthassa másokkal, az olyan eszközök, mint a jazzy, dokumentumokat generálhatnak, így mások is követhetik azokat.

A kódrészek jelölése

A MARK használata hasznos lehet a kódrészek elkülönítésében. Ezenkívül szépen csoportosítja a funkciókat a navigációs sávban. Használhat kiterjesztési csoportokat, kapcsolódó tulajdonságokat és módszereket is.

Egy egyszerű UIViewControllerhez a következő jeleket definiálhatjuk:

// JEL:: Init
// MARK: - Az életciklus megtekintése
// MARK: - Beállítás
// MARK: - Akció
// JEL:: Adatok

Forrásvezérlés

A Git jelenleg népszerű forrásvezérlő rendszer. Használhatjuk a .gitignore sablon fájlt a gitignore.io/api/swift fájlból. Előnyök és hátrányok vannak a függőségi fájlok (CocoaPods és Carthage) ellenőrzésében is. A projekttől függ, de hajlamosak vagyok nem elkövetni függőségeket (node_modules, Carthage, Pods) a forrásvezérlésben, hogy ne zavarják a kódbázist. Ezenkívül megkönnyíti a Pull kérések felülvizsgálatát.

Függetlenül attól, hogy benézi a Pods könyvtárba, a Podfile-t és a Podfile.lock-ot mindig verzió-ellenőrzés alatt kell tartani.

Mind az iTerm2, mind a parancsok végrehajtásához, valamint a Source Tree segítségével, az ágak és a szakaszok megtekintéséhez.

Dependencies

Használtam harmadik féltől származó kereteket, sokat készítettem és hozzájárultam a nyílt forráskódhoz. A keret használata egy lendületet ad az elején, de a jövőben is sokat korlátozhat. Vannak olyan triviális változások, amelyeket nagyon nehéz megoldani. Ugyanez történik az SDK-k használatakor. Előnyben részesítem az aktív nyílt forráskódú keretek kiválasztását. Olvassa el figyelmesen a forráskódot, és ellenőrizze a kereteket, és konzultáljon a csapatával, ha azt tervezi használni. Egy kis óvatosság nem árt.

Ebben az alkalmazásban a lehető legkevesebb függőséget próbálom használni. Csak annyit, hogy bemutassuk, hogyan kell kezelni a függőségeket. Egyes tapasztalt fejlesztők inkább a Carthage-t, a függőségi kezelőt részesítik előnyben, mivel ez teljes ellenőrzést biztosít Önnek. Azért választom a CocoaPod-ot, mert könnyen használható, és eddig nagyszerűen működött.

A projekt gyökerében található a 4.1 értékű .swift verzió nevű fájl, amely elmondja a CocoaPods-nak, hogy a projekt Swift 4.1-et használ. Ez egyszerűnek tűnik, de nekem sok időbe telt, hogy kitaláljam.

Bejutás a projektbe

Készítsünk néhány indítóképet és ikont, hogy szép megjelenést kapjon a projekt.

API

Az iOS hálózatépítésének egyszerű módja a nyilvános ingyenes API-szolgáltatások révén. Itt az food2fork-ot használom. Regisztrálhat fiókot a http://food2fork.com/about/api oldalon. Sok más fantasztikus API is van ebben a public-api lerakatban.

Jó, ha hitelesítő adatait biztonságos helyen tartja. Az 1Password felhasználásával állíthatom elő és tárolhatom a jelszavamat.

Mielőtt elkezdenénk a kódolást, játsszunk az API-kkal, hogy megtudjuk, milyen típusú kérésekre és válaszokra válaszolnak. Az Insomnia eszközt használom az API válaszok tesztelésére és elemzésére. Nyílt forráskódú, ingyenes és nagyszerűen működik.

Indító képernyő

Az első benyomás fontos, csakúgy, mint a Indító képernyő. Az előnyben részesített módszer a LaunchScreen.storyboard használata statikus Launch kép helyett.

Ha indítóképet szeretne hozzáadni az eszközkatalógushoz, nyissa meg a LaunchScreen.storyboard alkalmazást, adja hozzá az UIImageView fájlt, és rögzítse azt az UIView széléhez. Nem szabad rögzíteni a képet a biztonságos területhez, mivel azt akarjuk, hogy a kép teljes képernyős legyen. Ezenkívül törölje az automatikus elrendezés korlátainak szélső jelölését. Állítsa az UIImageView contentMode formátumát Aspect Fill-ként, hogy az a megfelelő oldalarányú legyen.

Konfigurálja az elrendezést a LaunchScreen alkalmazásban.

App ikonra

A bevált gyakorlat az, hogy minden támogatott eszköz számára biztosítja az összes szükséges alkalmazásikonot, valamint olyan helyeken, mint például az Értesítés, a Beállítások és a Springboard. Győződjön meg arról, hogy minden képnek nincs átlátszó képpontja, különben fekete háttér lesz. Ez a tipp az Emberi felület irányelvei - App Icon-ből származik.

Tartsa a háttér egyszerű, és kerülje az átláthatóságot. Győződjön meg arról, hogy az ikon átlátszatlan, és ne borítsa el a hátteret. Adjon neki egy egyszerű hátteret, hogy ez ne tegye túlzott mértékben a többi alkalmazás ikonját a közelben. Nem kell a teljes ikont kitölteni tartalommal.

Olyan négyzet alakú képeket kell megterveznünk, amelyek mérete nagyobb, mint 1024 x 1024, hogy mindegyik képes legyen kisebb méretűre méretezni. Ezt megteheti kézzel, szkripttel, vagy felhasználhatja ezt a kis IconGenerator alkalmazást, amelyet készítettem.

Az IconGenerator alkalmazás generálhat ikonokat iOS-hoz iPhone, iPad, macOS és watchOS alkalmazásokban. Az eredmény az AppIcon.appiconset, amelyet közvetlenül az eszközkatalógusba húzhatunk. Az Eszközkatalógus a modern Xcode projektek felé vezető út.

Világítási kód a SwiftLint segítségével

Függetlenül attól, hogy milyen platformon dolgozunk ki, jó, ha van egy bélelt a következetes egyezmények érvényesítésére. A Swift projektek legnépszerűbb eszköze a SwiftLint, amelyet a Realm fantasztikus emberei készítettek.

A telepítéshez adjon hozzá a 'SwiftLint', '~> 0,25' pod a Podfile-hez. Az is jó gyakorlat, hogy meghatározzuk a függőségek verzióját, így a pod telepítése véletlenül nem frissül egy főbb verzióra, amely megszakíthatja az alkalmazását. Ezután adjon hozzá egy .swiftlint.yml-t a kívánt konfigurációhoz. A mintakonfiguráció itt található.

Végül adjunk hozzá egy új parancsfájl-kifejezést a swintlint futtatásához az összeállítás után.

Típusbiztos erőforrás

Az R.swift eszközt használom az erőforrások biztonságos kezelésére. Típusbiztonsági osztályokat generálhat a betűkészlet, a lokalizálható karakterláncok és a színek eléréséhez. Amikor megváltoztatjuk az erőforrásfájlneveket, implicit összeomlás helyett fordítási hibákat kapunk. Ez megakadályozza, hogy az aktívan használt erőforrásokkal következtetjünk bennünket.

imageView.image = R.image.notFound ()

Mutasd meg a kódot

Merüljünk bele a kódba, kezdve a modellel, az áramlásvezérlőkkel és a szolgáltatási osztályokkal.

A modell megtervezése

Unalmasnak tűnhet, de az ügyfelek csak egy szebb módja az API-válasz megjelenítésének. A modell talán a legalapvetőbb dolog, és sokat használjuk az alkalmazásban. Olyan fontos szerepet játszik, de lehetnek nyilvánvaló hibák a hibásan formált modellekkel kapcsolatban, és feltételezések lehetnek arról, hogy a modellt hogyan kell értelmezni, amelyeket figyelembe kell venni.

Tesztelnünk kell az alkalmazás minden modelljét. Ideális esetben a modellek automatikus tesztelésére van szükség az API-válaszok alapján, ha a modell megváltozott a háttérképtől.

A Swift 4.0-tól kezdve adaptálhatjuk modellünket Codable-hez, hogy könnyen sorba tudjuk állítani a JSON-on és onnan. Modellünknek változatlannak kell lennie:

struct recept: Covable {
  hadd kiadó: String
  had url: URL
  let sourceUrl: Karakterlánc
  let id: Karakterlánc
  hadd cím: String
  let imageUrl: Karakterlánc
  hadd socialRank: Dupla
  hadd PublisherUrl: URL
enum CodingKeys: String, CodingKey {
    eset kiadó
    eset url = "f2f_url"
    case sourceUrl = "source_url"
    eset id = "recept_id"
    eset címe
    case imageUrl = "image_url"
    case socialRank = "social_rank"
    case publisherUrl = "publisher_url"
  }
}

Használhatunk néhány tesztkeretet, ha szereti a képzelet szintaxisát vagy az RSpec stílust. Néhány harmadik féltől származó tesztrendszernek problémái lehetnek. Úgy találom, hogy az XCTest elég jó.

import XCTest
@testable import receptek
osztály RecipesTests: XCTestCase {
  func testParsing () dob {
    hadd json: [karakterlánc: bármilyen] = [
      "kiadó": "Két borsó és azok hüvelye",
      "f2f_url": "http://food2fork.com/view/975e33",
      "cím": "Nem süthető csokoládé mogyoróvaj perecet tartalmazó sütik",
      "source_url": "http://www.twopeasandtheirpod.com/no-bake-chocolate-peanut-butter-pretzel-cookies/",
      "recept_id": "975e33",
      "image_url": "http://static.food2fork.com/NoBakeChocolatePeanutButterPretzelCookies44147.jpg",
      "social_rank": 99.99999999999974,
      "Publisher_url": "http://www.twopeasandtheirpod.com"
    ]
hadd adatok = próbáld ki a JSONSerialization.data-t (withJSONObject: json, options: [])
    let dekóder = JSONDecoder ()
    hagyja, hogy recept = próbáld meg a decoder.decode-ot (Recept: önálló, -tól: adatok)
XCTAssertEqual (recept.title, "Nem süthető csokoládé mogyoróvaj pretzel sütik")
    XCTAssertEqual (recept.id, "975e33")
    XCTAssertEqual (recept.url, URL (karakterlánc: "http://food2fork.com/view/975e33")!)
  }
}

Jobb navigáció a FlowController segítségével

Korábban a Compass-ot használtam irányítómotorként a projektekben, de az idő múlásával rájöttem, hogy az egyszerű útválasztási kód írása is működik.

A FlowController-et számos UIViewController-rel összekapcsolt elem közös kezelésére használják. Érdemes elolvasni a FlowControllert és a Koordinátort más használati esetekben és a jobb megértés érdekében.

Van egy AppFlowController, amely kezeli a rootViewController megváltoztatását. Most elindítja a RecipeFlowController programot.

ablak = UIWindow (keret: UIScreen.main.bounds)
window? .rootViewController = appFlowController
ablak? .makeKeyAndVisible ()
appFlowController.start ()

A RecipeFlowController kezeli (valójában az) az UINavigationController alkalmazást, amely kezeli a HomeViewController, a RecipesDetailViewController, a SafariViewController tolóelemeit.

végső osztály RecipeFlowController: UINavigationController {
  /// Indítsa el az áramlást
  func start () {
    let service = RecipesService (hálózatépítés: NetworkService ())
    hadvezérlő = HomeViewController (recipesService: service)
    viewControllers = [vezérlő]
    kontroller.select = {[gyenge ön] recept a
      önálló? .startDetail (recept: recept)
    }
  }
privát func startDetail (recept: recept) {}
  privát func startWeb (URL: URL) {}
}

Az UIViewController delegálással vagy bezárással értesítheti a FlowController-t a változásokról vagy a folyamat következő képernyőiről. A delegált számára szükség lehet ellenőrizni, hogy vannak-e ugyanazon osztály két példánya. Az egyszerűség kedvéért itt használjuk a bezárást.

Automatikus elrendezés

Az automatikus elrendezés az iOS 5 óta működik, ez évről évre javul. Bár néhány embernek továbbra is problémája van vele, elsősorban a megtévesztő megszorítások és a teljesítmény miatt, de személy szerint úgy gondolom, hogy az Auto Layout elég jó.

Megpróbálom az Auto Layout-ot a lehető legnagyobb mértékben felhasználni adaptív felhasználói felület létrehozására. Olyan könyvtárakat használhatunk, mint az Anchors, hogy deklaratív és gyors automatikus elrendezést végezzünk. Ebben az alkalmazásban azonban csak az NSLayoutAnchort használjuk, mivel az iOS 9-ből származik. Az alábbi kódot a Constraint ihlette. Ne feledje, hogy az automatikus elrendezés a legegyszerűbb formájában magában foglalja a translatesAutoresizingMaskIntoConstraints váltását és az aktív korlátozások aktiválását.

kiterjesztés NSLayoutConstraint {
  statikus funkciók aktiválása (_ korlátozások: [NSLayoutConstraint]) {
    constraints.forEach {
      ($ 0.firstItem as? UIView) ?. translatesAutoresizingMaskIntoConstraints = false
      $ 0.isActive = igaz
    }
  }
}

Valójában sok más elrendezési motor is elérhető a GitHub-on. Ha meg szeretné tudni, melyik használható, nézze meg a LayoutFrameworkBenchmark oldalt.

Építészet

Az építészet talán a legmegbeszéltebb és megvitatottabb téma. Én rajongó vagyok az építészet felfedezéséhez, itt megnézhet több hozzászólást és keretet a különböző architektúrákról.

Számomra minden architektúra és minta meghatározza az egyes objektumok szerepeit és azok összekapcsolásának módját. Ne felejtse el az építkezés megválasztására vonatkozó alábbi alapelveket:

  • beilleszteni, hogy mi változik
  • előnyben részesíti a kompozíciót az öröklés felett
  • program interfész, nem pedig a megvalósítás

Miután sokféle architektúrával játszottam, az Rx-mel és anélkül, rájöttem, hogy az egyszerű MVC elég jó. Ebben az egyszerű projektben csak egy UIViewController található, amelynek logikája be van ágyazva a segítő szolgáltatási osztályokba,

Hatalmas látásvezérlő

Lehet, hogy hallotta, hogy az emberek tréfálnak arról, hogy milyen masszív az UIViewController, de a valóságban nincs hatalmas nézet-vezérlő. Csak az, hogy rossz kódot írunk. Vannak azonban módjai annak csökkentésére.

Az általam használt receptek alkalmazásban,

  • Szolgáltatás, amely injekciót ad a nézetvezérlőbe egyetlen feladat elvégzéséhez
  • Általános nézet a nézet áthelyezéséhez és a deklaráció ellenőrzéséhez a Nézet rétegbe
  • Gyerek nézet-vezérlő a gyermek nézet-vezérlők összeállításához további szolgáltatások felépítéséhez

Itt egy nagyon jó cikk, 8 tipp a nagy vezérlők lecsökkentéséhez.

Hozzáférés-szabályozás

A SWIFT dokumentációja megemlíti, hogy „a hozzáférés-vezérlés korlátozza a kód részeinek a hozzáférését más forrásfájlok és modulok kódjai alapján. Ez a szolgáltatás lehetővé teszi a kód végrehajtási részleteinek elrejtését, és meghatározhatja azt a preferált interfészt, amelyen keresztül a kód elérhető és használható. ”

Alapértelmezés szerint mindennek privátnak és véglegesnek kell lennie. Ez is segíti a fordítót. Ha köztulajdonban van, akkor a projekt egészén keresztül kell keresnünk, mielőtt bármit megtennénk. Ha az ingatlant csak egy osztályon belül használják, akkor magántulajdonba hozatala azt jelenti, hogy nem kell törődnünk, ha máshol elbomlik.

Ha lehetséges, a tulajdonságokat nyilvánítsa véglegesnek.

végső osztály HomeViewController: UIViewController {}

Nyilvánítsa a tulajdonságokat magántulajdonban vagy legalább magántulajdonban (készlet).

végső osztály RecipeDetailView: UIView {
  private let scrollableView = ScrollableView ()
  privát (beállított) lusta var imageView: UIImageView = self.makeImageView ()
}

Lusta tulajdonságok

Azoknál a tulajdonságoknál, amelyekhez később lehet hozzáférni, kijelenthetjük őket, hogy lusta és a záráshoz használhatják a gyors építkezést.

végső osztály RecipeCell: UICollectionViewCell {
  privát (beállított) lusta var containerView: UIView = {
    nézet = UIView ()
    view.clipsToBounds = true
    view.layer.cornerRadius = 5
    view.backgroundColor = Color.main.withAlphaComponent (0.4)
visszatérési nézet
  } ()
}

A make függvényeket akkor is használhatjuk, ha azt tervezzük, hogy ugyanazt a funkciót több tulajdonsághoz újra felhasználjuk.

végső osztály RecipeDetailView: UIView {
  privát (beállított) lusta var imageView: UIImageView = self.makeImageView ()
privát funkció makeImageView () -> UIImageView {
    hadd imageView = UIImageView ()
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true
    visszatérés imageView
  }
}

Ez megegyezik a Strive for Fluent Usage tanácsaival.

Kezdje a gyári módszerek nevét a „make” -vel, például x.makeIterator ().

Kódrészletek

Néhány kódszintaxist nehéz megjegyezni. Fontolja meg a kódrészletek használatát a kód automatikus generálásához. Ezt az Xcode támogatja, és ezt az Apple mérnökei részesítik előnyben a demózás során.

ha # elérhető (iOS 11, *) {
  viewController.navigationItem.searchController = searchController
  viewController.navigationItem.hidesSearchBarWhenScrolling = false
} más {
  viewController.navigationItem.titleView = searchController.searchBar
}

Készítettem egy repót néhány hasznos Swift-részlettel, amelyet sokan élveznek.

Hálózat

A Swift hálózatba szervezése valamilyen megoldott probléma. Vannak unalmas és hibára hajlamos feladatok, például a HTTP válaszok elemzése, a kérési sorok kezelése, a paraméter-lekérdezések kezelése. Láttam hibákat a PATCH kérésekkel, a kisbetűs HTTP módszerekkel kapcsolatban ... Csak használhatjuk az Alamofire-t. Itt nem kell időt pazarolni.

Ehhez az alkalmazáshoz, mivel egyszerű és elkerüli a felesleges függőségeket. Csak közvetlenül használhatjuk az URLSession-t. Az erőforrás általában URL-t, elérési utat, paramétereket és a HTTP-módszert tartalmaz.

struct Resource {
  had url: URL
  let út: Húr?
  hadd httpMethod: String
  hagyjuk paraméterek: [String: String]
}

Egy egyszerű hálózati szolgáltatás csak elemezheti az erőforrást az URLRequesthez, és utasítja az URLSession végrehajtását

végső osztály NetworkService: Hálózatépítés {
  @discardableResult func fetch (erőforrás: erőforrás, befejezés: @escaping (adatok?) -> érvénytelen) -> URLSessionTask? {
    őr let request = makeRequest (erőforrás: erőforrás) else {
      befejezése (nulla)
      visszatérés nulla
    }
Hagyja, hogy a task = session.dataTask (a következővel: request, completeHandler: {data, _, error in
      őr hadd adatok = adatok, hiba == nincs más {
        befejezése (nulla)
        Visszatérés
      }
befejezése (adatok)
    })
task.resume ()
    visszatérési feladat
  }
}

Használjon függőségi injekciót. Engedélyezze a hívó félnek az URLSessionConfiguration megadását. Itt a Swift alapértelmezett paramétert használjuk a leggyakoribb lehetőség biztosításához.

init (konfiguráció: URLSessionConfiguration = URLSessionConfiguration.default) {
  self.session = URLSession (konfiguráció: konfiguráció)
}

Az iQ 8-ból származó URLQueryItem-et is használom. Ez paraméterek elemzését teszi elegánsnak és kevésbé unalmasnak a lekérdezésére.

Hogyan kell tesztelni a hálózati kódot

Az URLProtocol és az URLCache felhasználásával csonkot adhatunk a hálózati válaszokhoz, vagy használhatunk olyan kereteket, mint a Mockingjay, amelyek elrejtik az URLSessionConfiguration-t.

Én magam inkább a protokollt használom a teszteléshez. A protokoll használatával a teszt ál-kérést hozhat létre csonka válasz biztosítására.

protokoll hálózatépítés {
  @discardableResult func fetch (erőforrás: erőforrás, befejezés: @escaping (adatok?) -> érvénytelen) -> URLSessionTask?
}
végső osztály MockNetworkService: Hálózatépítés {
  let data: Adatok
  init (fájlnév: karakterlánc) {
    let bundle = Bundle (a következőhöz: MockNetworkService.self)
    hadd url = bundle.url (forResource: fileName, withExtension: "json")!
    self.data = próbáld! Adatok (tartalom: URL)
  }
func fetch (erőforrás: Forrás, befejezés: @escaping (Adatok?) -> Érvénytelen) -> URLSessionTask? {
    befejezése (adatok)
    visszatérés nulla
  }
}

Gyorsítótár bevezetése az offline támogatáshoz

Sokszor hozzászóltam és használtam a Cache nevű könyvtárat. Egy jó gyorsítótár-könyvtárhoz szükségünk van a memóriára és a lemez-gyorsítótárra, a gyors hozzáféréshez szükséges memóriára, a tartóssághoz szükséges lemezre. Amikor mentünk, mentünk mind a memóriába, mind a lemezre. Betöltéskor, ha a memória-gyorsítótár nem sikerül, betölti a lemezről, majd újra frissíti a memóriát. Sok speciális téma van a gyorsítótárról, például a megtisztítás, lejárati idő, hozzáférési gyakoriság. Olvassa el velük itt.

Ebben az egyszerű alkalmazásban elegendő egy otthoni gyorsítótár szolgáltatási osztály, és jó módszer a gyorsítótárazás működésének megtanulására. A Swift-ben mindent átalakíthatunk adatmá, így az adatokat csak gyorsítótárba menthetjük. A Swift 4 Codable az objektumot Data-ba sorosíthatja.

Az alábbi kód megmutatja, hogyan kell a FileManager-et használni a lemez gyorsítótárához.

/// Adatok mentése és betöltése a memóriába és a lemez gyorsítótárába
végső osztály CacheService {
/// Az adatok memóriába történő beolvasása vagy betöltése
  private let memory = NSCache  ()
/// A gyorsítótárban lévő fájlokat (mp3 fájlok és képfájlok) tartalmazó elérési út URL
  private let diskPath: URL
/// A fájl vagy könyvtár ellenőrzéséhez létezik egy megadott elérési út
  private let fileManager: FileManager
/// Győződjön meg arról, hogy minden műveletet sorosan hajtanak végre
  private let serialQueue = DispatchQueue (címke: "Receptek")
init (fileManager: FileManager = FileManager.default) {
    self.fileManager = fileManager
    csináld {
      hadd documentDirectory = próbáld ki a fileManager.url fájlt (
        a következőhöz: .documentDirectory,
        itt: .userDomainMask,
        megfelelőFor: nulla,
        létrehozás: igaz
      )
      diskPath = documentDirectory.apppingPathComponent ("Receptek")
      próbálja meg a createDirectoryIfNeeded ()
    } elkapni {
      fatális hiba()
    }
  }
func save (adatok: adatok, kulcs: karakterlánc, befejezés: (() -> érvénytelen)? = nulla) {
    had kulcs = MD5 (kulcs)
serialQueue.async {
      self.memory.setObject (adatok NSData, forKey: kulcs mint NSString)
      csináld {
        próbálja ki az data.write-t (ide: self.filePath (kulcs: kulcs))
        befejezését? ()
      } elkapni {
        print (hiba)
      }
    }
  }
}

A hibás és nagyon hosszú fájlnevek elkerülése érdekében kivonhatjuk őket. Az MD5-t használom a SwiftHash-től, amely egyszerű használatot ad, hagyja, hogy a kulcs = MD5 (kulcs).

A Cache tesztelése

Mivel úgy tervezem, hogy a gyorsítótár-műveletek aszinkronak lesznek, a tesztvárakozást kell használnunk. Minden egyes teszt előtt ne felejtse el visszaállítani az állapotot, így az előző tesztállapot nem zavarja az aktuális tesztet. Az XCTestCase elvárásainak köszönhetően minden eddiginél könnyebbé válik az aszinkron kód tesztelése.

osztály CacheServiceTests: XCTestCase {
  let service = CacheService ()
felülbírálja a func setUp () {
    super.setUp ()
próbálja meg? service.clear ()
  }
func testClear () {
    hadd várakozás = self.expectation (leírás: #function)
    let string = "Hello világ"
    Hagyja, hogy az adatok = string.data (az .utf8 használatával)!
service.save (adatok: adatok, kulcs: "kulcs", kitöltés: {
      próbálja meg? self.service.clear ()
      self.service.load (kulcs: "kulcs", kitöltés: {
        XCTAssertNil ($ 0)
        expectation.fulfill ()
      })
    })
várj (mert: [elvárás], időtúllépés: 1)
  }
}

Távoli képek betöltése

Én is hozzájárulok az Imaginary-hez, így tudok egy kicsit arról, hogyan működik. A távoli képekhez letölteni és gyorsítótárazni kell, és a gyorsítótár kulcs általában a távoli kép URL-je.

Receptkészletünkben készítsünk egy egyszerű ImageService szolgáltatást a NetworkService és a CacheService alapján. Alapvetően egy kép csak egy hálózati erőforrás, amelyet letöltünk és tárolunk. Mi inkább a kompozíciót részesítjük előnyben, így a NetworkService és a CacheService szolgáltatást belefoglaljuk az ImageServicebe.

/// Ellenőrizze a helyi gyorsítótárat, és töltse le a távoli képet
végső osztály ImageService {
private let networkService: Hálózatépítés
  private let cacheService: CacheService
  privát var feladat: URLSessionTask?
init (networkService: hálózatépítés, cacheService: CacheService) {
    self.networkService = networkService
    self.cacheService = cacheService
  }
}

Általában UICollectionView és UITableView cellák vannak UIImageView-val. És mivel a cellákat újra felhasználják, minden új megkeresési feladatot meg kell szüntetnünk.

func fetch (URL: URL, kitöltés: @escaping (UIImage?) -> érvénytelen) {
  // Ha létezik, törölje a meglévő feladatot
  feladatot? .cancel ()
// Próbáljon betölteni a gyorsítótárból
  cacheService.load (kulcs: url.absoluteString, befejezés: {[gyenge saját] cachedData
    ha let data = cachedData, let image = UIImage (adatok: adatok) {
      DispatchQueue.main.async {
        befejezése (image)
      }
    } más {
      // Próbálja meg kérni a hálózatról
      hadd erőforrás = erőforrás (URL: url)
      self? .task = self? .networkService.fetch (erőforrás: erőforrás, befejezés: {networkData in
        ha let data = networkData, let image = UIImage (adatok: adatok) {
          // Mentés a gyorsítótárba
          self? .cacheService.save (adatok: adatok, kulcs: url.absoluteString)
          DispatchQueue.main.async {
            befejezése (image)
          }
        } más {
          print ("Hiba történt a kép betöltésekor a (URL) címen")
        }
      })
self? .task? .resume ()
    }
  })
}

A képek betöltése kényelmesebbé válik az UIImageView számára

Adjunk hozzá egy kiterjesztést az UIImageView elemhez, hogy a távoli képet az URL-ből állítsuk be. A társított objektummal tárolom ezt az ImageService szolgáltatást, és visszavonom a régi kéréseket. Használjuk a társított objektumot az ImageService UIImageView csatolásához. A lényeg az aktuális kérés visszavonása, amikor a kérés újra elindul. Ez akkor hasznos, ha a képnézeteket gördítõ listán jelenítik meg.

kiterjesztés UIImageView {
  func setImage (URL: URL, helyőrző: UIImage? = nulla) {
    if imageService == nulla {
      imageService = ImageService (networkService: NetworkService (), cacheService: CacheService ())
    }
self.image = helyőrző
    Az ön.imageService? .fetch (URL: url, befejezés: {[gyenge én] kép a
      én? .kép = kép
    })
  }
private var imageService: ImageService? {
    kap {
      return objc_getAssociatedObject (self, & AssociateKey.imageService) mint? ImageService
    }
    set {
      objc_setAssociatedObject (
        maga,
        & AssociateKey.imageService,
        newValue,
        objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
      )
    }
  }
}

Általános adatforrás az UITableView és UICollectionView számára

UITableView-t és UICollectionView-t szinte minden alkalmazásban használunk, és szinte ugyanazt a műveletet többször is végrehajtjuk.

  • Mutasd a frissítés vezérlését betöltés közben
  • újratöltési lista adatok esetén
  • hibát mutatni hiba esetén.

Az UITableView és az UICollection körül sok csomagoló található. Mindegyik újabb absztrakciós réteget ad hozzá, amely több energiát ad nekünk, de ugyanakkor korlátozásokat is alkalmaz.

Ebben az alkalmazásban az Adapter használatával általános adatforrást szereztem, hogy egy típusbiztonságos adatgyűjtést készítsek. Mert végül csak annyit kell tennünk, hogy leképezzük a modellt a cellákba.

Ezen ötlet alapján az Upstream-t is hasznosítom. UITableView és UICollectionView nehezen tekerhető, hiszen sokszor alkalmazásspecifikus, tehát elég egy vékony burkolólap, mint például az adapter.

végső osztályú adapter : NSObject,
UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
  var elemek: [T] = []
  var configure: ((T, cella) -> érvénytelen)?
  var válasszon: ((T) -> érvénytelen)?
  var cellHeight: CGFloat = 60
}

Vezérlő és nézet

Számos korlátozás és sok probléma miatt bemártottam a Storyboard-t. Ehelyett kódot használok nézetek készítéséhez és a korlátozások meghatározásához. Nem olyan nehéz követni. Az UIViewController kazánlemez-kódjának nagy része nézetek létrehozására és az elrendezés konfigurálására szolgál. Vigyük őket a nézetre. Erről bővebben itt olvashat.

/// A vezérlő és a nézet közötti szétválasztásra szolgál
osztály BaseController : UIViewController {
  legyen root = T ()
felülírja a func loadView () {
    nézet = gyökér
  }
}
végső osztály RecipeDetailViewController: BaseController  {}

A felelősségek kezelése a gyermek nézővezérlővel

A View vezérlő tároló hatékony koncepció. Minden nézetszabályozó különálló aggodalmakkal rendelkezik, és összeállíthatók, hogy fejlett funkciókat hozzanak létre. A RecipeListViewControllert használtam az UICollectionView kezelésére és a receptek listájának megjelenítésére.

végső osztály RecipeListViewController: UIViewController {
  privát (készlet) var gyűjteményView: UICollectionView!
  let adapter = Adapter  ()
  private let emptyView = EmptyView (szöveg: "Nem található recept!")
}

Van egy HomeViewController, amely beágyazja ezt a RecipeListViewController-t

/// Mutassa meg a receptek listáját
végső osztály HomeViewController: UIViewController {
/// Amikor egy recept kap, válassza ki
  var select: ((Recept) -> Érvénytelen)?
private var refreshControl = UIRefreshControl ()
  magánkézben lévő receptekSzolgáltatás: ReceptekSzolgáltatás
  magánkézben a searchComponent: SearchComponent
  privát let receptListViewController = RecipeListViewController ()
}

Összetétel és függőség-befecskendezés

Próbálom összetevőket építeni és kódot állítani, amikor csak tudok. Látjuk, hogy az ImageService használja a NetworkService és a CacheService szolgáltatást, a RecipeDetailViewController pedig a Recipe and RecipesService szolgáltatást használja.

Ideális esetben a tárgyak önmagukban nem hozhatnak létre függőségeket. A függőségeket kívülről kell létrehozni, és a gyökérről át kell adni. Alkalmazásunkban a gyökér AppDelegate és AppFlowController, tehát a függőségeknek innen kell kezdődniük.

App szállítási biztonság

Az iOS 9 óta minden alkalmazásnak át kell vennie az App Transport Security szolgáltatást

Az App Transport Security (ATS) betartja a bevált gyakorlatokat az alkalmazás és a háttere közötti biztonságos kapcsolatokban. Az ATS megakadályozza a véletlenszerű közzétételt, biztonságos alapértelmezett viselkedést biztosít, és könnyen alkalmazható; alapértelmezés szerint be van kapcsolva az iOS 9 és az OS X v10.11 esetén is. Az ATS-t a lehető leghamarabb el kell fogadnia, függetlenül attól, hogy új alkalmazást készít, vagy meglévőt frissít.

Alkalmazásunkban néhány képet HTTP kapcsolaton keresztül nyerünk. Ki kell zárnunk a biztonsági szabályból, de csak erre a domainre.

 NSAppTransportSecurity 

   NSExceptionDomains 
  
     food2fork.com 
    
       NSIncludesSubdomains 
      
       NSExceptionAllowsInsecureHTTPLoads 
      
    
  

Egyéni gördíthető nézet

A részlet képernyőhöz különféle cella típusokkal használhatjuk az UITableView és UICollectionView fájlokat. A nézeteknek statikusnak kell lenniük. Összerakhatjuk az UIStackView használatával. A nagyobb rugalmasság érdekében csak az UIScrollView alkalmazást használhatjuk.

/// Függőlegesen elrendezett nézet az UIScrollView automatikus elrendezésének használatával
ScrollableView záró osztály: UIView {
  private let scrollView = UIScrollView ()
  private let contentView = UIView ()
felülbírálni az init (keret: CGRect) {
    super.init (keret: keret)
scrollView.showsHorizontalScrollIndicator = hamis
    scrollView.alwaysBounceHorizontal = false
    addSubview (scrollView)
scrollView.addSubview (contentView)
NSLayoutConstraint.activate ([
      scrollView.topAnchor.constraint (equalTo: topAnchor),
      scrollView.bottomAnchor.constraint (equalTo: bottomAnchor),
      scrollView.leftAnchor.constraint (egyenlő: To: leftAnchor),
      scrollView.rightAnchor.constraint (egyenlő: To: jobbAnchor),
contentView.topAnchor.constraint (egyenlő: To: scrollView.topAnchor),
      contentView.bottomAnchor.constraint (egyenlő: To: scrollView.bottomAnchor),
      contentView.leftAnchor.constraint (egyenlő: To: leftAnchor),
      contentView.rightAnchor.constraint (equalTo: rightAnchor)
    ])
  }
}

Az UIScrollView oldalt rögzítjük. Rögzítjük a contentView bal és jobb oldali horgonyt az önmaga felé, miközben a contentView felső és alsó horgonyát az UIScrollView-hoz rögzítjük.

A ContentView belső nézetei felső és alsó korlátokkal rendelkeznek, tehát amikor kibővülnek, kibővítik a contentView-t is. Az UIScrollView ebből a tartalommegtekintésből az automatikus elrendezési információt használja a tartalomméret meghatározására. Így használható a ScrollableView a RecipeDetailView alkalmazásban.

scrollableView.setup (párok: [
  ScrollableView.Pair (nézet: imageView, beillesztés: UIEdgeInsets (felül: 8, bal: 0, alul: 0, jobb: 0)),
  ScrollableView.Pair (nézet: ingredientHeaderView, beillesztés: UIEdgeInsets (felső: 8, bal: 0, alsó: 0, jobb: 0)),
  ScrollableView.Pair (nézet: ingredientLabel, beillesztés: UIEdgeInsets (felül: 4, bal: 8, alul: 0, jobb: 0)),
  ScrollableView.Pair (nézet: infoHeaderView, beillesztés: UIEdgeInsets (felső: 4, bal: 0, alul: 0, jobb: 0)),
  ScrollableView.Pair (nézet: utasításgomb, beillesztés: UIEdgeInsets (felső: 8, bal: 20, alsó: 0, jobb: 20)),
  ScrollableView.Pair (nézet: eredeti gomb, beillesztés: UIEdgeInsets (felül: 8, bal: 20, alul: 0, jobb: 20)),
  ScrollableView.Pair (nézet: infoView, beillesztés: UIEdgeInsets (felül: 16, bal: 0, alul: 20, jobb: 0))
])

Keresési funkciók hozzáadása

Az iOS 8-tól kezdve az UISearchController használatával alapértelmezett keresési élményt kaphatunk a keresősáv és az eredményvezérlő segítségével. A keresési funkciókat beágyazzuk a SearchComponentbe, hogy ez csatlakoztatható legyen.

végső osztály SearchComponent: NSObject, UISearchResultsUpdates, UISearchBarDelegate {
  hagyja receptekSzolgáltatás: RecipesService
  hadd searchController: UISearchController
  had receptListViewController = RecipeListViewController ()
}

Az iOS 11-től kezdve van egy searchController nevű tulajdonság az UINavigationItem oldalon, amely megkönnyíti a keresősáv megjelenítését a navigációs sávon.

func add (a viewControllerhez: UIViewController) {
  ha # elérhető (iOS 11, *) {
    viewController.navigationItem.searchController = searchController
    viewController.navigationItem.hidesSearchBarWhenScrolling = false
  } más {
    viewController.navigationItem.titleView = searchController.searchBar
  }
viewController.definesPresentationContext = true
}

Ebben az alkalmazásban le kell tiltania a hidesNavigationBarDuringPresentation alkalmazást, mivel ez elég hibás. Remélhetőleg a jövőbeli iOS frissítésekkel megoldódik.

A prezentációs környezet megértése

A megjelenítési környezet megértése elengedhetetlen a nézetszabályozó bemutatásához. A keresés során a searchResultsController eszközt használjuk.

self.searchController = UISSearchController (searchResultsController: recipesListViewController)

A definesPresentationContext fájlt kell használni a forrás nézet-vezérlőn (a nézet-vezérlőn, amelybe hozzáadjuk a keresősávot). Ennek nélkül a searchResultsController teljes képernyőn megjelenik !!!

Amikor a currentContext vagy overCurrentContext stílust használ egy nézetszabályozó bemutatására, ez a tulajdonság szabályozza, hogy a nézetszabályozó hierarchiájában melyik meglévő nézetvezérlőt valójában lefedi az új tartalom. Amikor egy kontextus alapú prezentáció történik, az UIKit a jelenlegi nézetszabályozóval indul, és felmegy a nézetszabályozó hierarchiáján. Ha talál egy olyan nézetszabályzót, amelynek ezen tulajdonság értéke igaz, felkéri a nézetszabályzót, hogy mutassa be az új nézetszabályzót. Ha egyetlen nézetszabályozó sem határozza meg a bemutatási környezetet, akkor az UIKit felkéri az ablak gyökér nézet vezérlőjét a prezentáció kezelésére.
Ennek a tulajdonságnak az alapértelmezett értéke hamis. Néhány rendszer által biztosított nézetvezérlő, például az UINavigationController, az alapértelmezett értéket igazra változtatja.

A keresési műveletek feloldása

Nem szabad végrehajtani a keresési kérelmeket minden olyan kulcsszóra, amelyet a felhasználó beír a keresősávba. Ezért valamilyen fojtásra van szükség. A DispatchWorkItem segítségével bekapcsolhatjuk a műveletet, és elküldhetjük a sorba. Később visszavonhatjuk.

döntő osztálybeszélő {
  privát késés: TimeInterval
  privát var workItem: DispatchWorkItem?
init (késés: TimeInterval) {
    self.delay = késés
  }
/// Késleltetés után indítsa el a műveletet
  func ütemezése (akció: @escaping () -> érvénytelen) {
    workItem? .cancel ()
    workItem = DispatchWorkItem (blokk: akció)
    DispatchQueue.main.asyncAfter (határidő: .now () + késleltetés, végrehajtás: workItem!)
  }
}

A debouncing tesztelése fordított elvárással

A Debouncer teszteléséhez az XCTest elvárásait fordított módban használhatjuk. Tudjon meg többet erről az aszinkron Swift kód egységtesztje alatt.

Annak ellenőrzéséhez, hogy a tesztelés során nem fordul elő-e helyzet, hozzon létre egy elvárást, amely teljesül a váratlan helyzet bekövetkezésekor, és állítsa az invertált tulajdonságát igazra. A teszt azonnal sikertelen lesz, ha a fordított elvárás teljesül.
osztály DebouncerTests: XCTestCase {
  func testDebouncing () {
    hadd cancelExpectation = self.expectation (leírás: "Cancel")
    cancelExpectation.isInverted = igaz
hagyja, hogy completeExpectation = self.expectation (leírás: "teljes")
    let debouncer = Debouncer (késleltetés: 0,3)
debouncer.schedule {
      cancelExpectation.fulfill ()
    }
debouncer.schedule {
      completeExpectation.fulfill ()
    }
várakozás (mert: [CancelExpectation, completeExpectation], időtúllépés: 1)
  }
}

A felhasználói felület tesztelése az UITestekkel

A kis refaktorolás néha nagy hatással lehet. A letiltott gomb utólag használhatatlan képernyőket eredményezhet. A UITest segíti az alkalmazás integritásának és funkcionális aspektusainak biztosítását. A tesztnek deklaratívnak kell lennie. Használhatjuk a Robot mintát.

osztály ReceptekUITesztek: XCTestCase {
  var alkalmazás: XCUIApplication!
  felülbírálja a func setUp () {
    super.setUp ()
    folytatódik utánhiba = hamis
    app = XCUIAalkalmazás ()
  }
  func testScrolling () {
    app.launch ()
    hadd collectionView = app.collectionViews.element (kötöttBy: 0)
    collectionView.swipeUp ()
    collectionView.swipeUp ()
  }
  func testGoToDetail () {
    app.launch ()
    hadd collectionView = app.collectionViews.element (kötöttBy: 0)
    hadd firstCell = collectionView.cells.element (kötöttBy: 0)
    firstCell.tap ()
  }
}

Íme néhány cikk a teszteléssel kapcsolatban.

  • Az UITest futtatása Facebook-bejelentkezéshez iOS-ban
  • Tesztelés Swift-ben a megadott mikor mintával

Fő menetvédő

Az UI-hez való hozzáférés a háttér sorból potenciális problémákat okozhat. Korábban a MainThreadGuard-ot kellett használni, most, hogy az Xcode 9-nek van a Főszálat-ellenőrzője, csak engedélyeztem ezt az Xcode-ban.

A Főszálat-ellenőrző egy önálló eszköz a Swift és C nyelvekhez, amely észleli az AppKit, UIKit és más API-k érvénytelen használatát a háttérszálon. A felhasználói felület frissítése a fonalaktól eltérő szálakon gyakori hiba, amely elmulasztott felhasználói felület frissítéseket, látványhibákat, adatsérüléseket és összeomlásokat eredményezhet.

Előadások és kérdések mérése

Használhatjuk az eszközöket az alkalmazás alapos profilozásához. A gyors méréshez a Debug Navigator fülre léphetünk, és megnézhetjük a CPU, a memória és a hálózat használatát. Nézze meg ezt a hűvös cikket, hogy többet megtudjon az eszközökről.

Prototípuskészítés játszótérrel

A játszótér az ajánlott módja az alkalmazások prototípusának és építésének. A WWDC 2018-on az Apple bemutatta a Create ML-t, amely támogatja a Playground-t, hogy a modellt kiképzzék. Nézze meg ezt a hűvös cikket, ahol többet megtudhat a játszótér-vezérelt fejlesztésről Swift-ben.

Hová menj innen?

Köszönöm, hogy elérted ilyen messzire. Remélem, megtanultál valami hasznosat. A legjobb módszer valami megtanulására az, ha csak csinálod. Ha előfordul, hogy ugyanazt a kódot újra és újra írja, akkor készítse el alkotóelemként. Ha egy probléma nehézséget okoz, írjon róla. Ossza meg tapasztalatait a világgal, sokat fog tanulni.

Azt javaslom, hogy olvassa el az iOS fejlesztésének legjobb helyei című cikket, ahol többet megtudhat az iOS fejlesztéséről.

Ha bármilyen kérdése, észrevétele vagy észrevétele van, ne felejtse el felvenni ezeket a megjegyzésekbe. És ha hasznosnak találta ezt a cikket, ne felejtsd el tapsolni.

Ha tetszik ez a bejegyzés, érdemes meglátogatnia a többi cikkem és alkalmazásomat