Articles

Build-konfigurációk kezelése Xcode-ban

Források

Töltse le ingyenesen a
The Missing Manual
for Swift Development

The Guide I Wish I Have When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Egy vadonatúj Xcode projekt két build konfigurációt definiál, Debug és Release. A legtöbb projekt különböző okokból egy vagy több további build-konfigurációt definiál. Ez nem újdonság, és jó gyakorlat a build-konfigurációk használata, hogy a buildet a telepítendő környezet speciális igényeihez igazítsuk.

Ebben az epizódban megmutatom, hogyan lehet biztonságosan kezelni a build-konfigurációra jellemző adatokat, például az API-kulcsokat, hitelesítő adatokat és más érzékeny adatokat. Többféle stratégia létezik a build-konfigurációk kezelésére, de vannak fontos különbségek, amelyeket figyelembe kell venned, különösen a biztonság szempontjából.

Konfiguráció hozzáadása

Tüzeld fel az Xcode-ot, és hozz létre egy új projektet, kiválasztva a Single View App sablont az iOS > Alkalmazás szakaszból.

Setting Up the Project

Nevezd el a projektet Konfigurációknak, állítsd a nyelvet Swiftre, és ellenőrizd, hogy az alján lévő jelölőnégyzetek nincsenek-e bejelölve. Mondja meg az Xcode-nak, hogy hol szeretné tárolni a projektet, és kattintson a Létrehozás gombra.

Setting Up the Project

Nyissa meg a bal oldali Projektnavigátort, és kattintson a projektre a tetején. Válassza ki a projektet a Projekt részben a projekt részleteinek megjelenítéséhez. Egy vadonatúj Xcode projekt két build-konfigurációt határoz meg, a Debug és a Release konfigurációt.

Build Configurations in Xcode

A Debug konfigurációt általában a fejlesztés során használja, míg a Release konfigurációt az App Store vagy a TestFlight buildek létrehozásához. Ez valószínűleg nem újdonság az Ön számára.

Nagyon sok alkalmazás kommunikál egy backenddel, és általános gyakorlat, hogy van egy staging és egy production környezet. A staging környezetet a fejlesztés során használják. Kattintson duplán a Debug konfigurációra, és nevezze át Stagingre.

Build Configurations in Xcode

Adjunk hozzá egy harmadik konfigurációt a termelési környezethez. Kattintsunk a táblázat alján lévő + gombra, válasszuk a Duplicate “Staging” Configuration lehetőséget, és nevezzük el a konfigurációt Production.

Duplicating a Build Configuration

Duplicating a Build Configuration

Hozzunk létre egy sémát mindegyik konfigurációhoz, hogy megkönnyítsük a környezetek közötti gyors váltást. Jelöljük ki a sémát a tetején, és válasszuk a Sémák kezelése… menüpontot. Válasszuk ki a Konfigurációk nevű sémát, és kattintsunk rá még egyszer. Nevezze át Stagingre.

Update Schemes

A séma kiválasztása után kattintson az alsó fogaskerék ikonra, és válassza a Duplikálás lehetőséget. Nevezze el a sémát Termelésnek. Válassza a bal oldalon a Futtatás lehetőséget, és állítsa a Build Configuration-t Production-re.

Update Schemes

Ez minden. Most már van egy build konfigurációnk a staging és a production számára. A sémák segítségével gyorsan és egyszerűen válthatunk a build-konfigurációk között.

A felhasználó által meghatározott build-beállítások

Amint korábban említettem, több megoldás is létezik az adott build-konfigurációra jellemző adatok kezelésére. Ebben az epizódban egy olyan megoldást mutatok be, amelyet minden olyan projektben használok, amely némi összetettséggel bír. Mielőtt kiteregetném az általam elképzelt megoldást, szeretnék megmutatni egy másik, a fejlesztők által gyakran használt megoldást.

Válassza ki a projektet a bal oldali Projektnavigátorban. Válassza ki a Konfigurációk célpontot a Célok szakaszból, majd kattintson a tetején lévő Építési beállítások fülre.

Target Build Settings

Az Építési beállítások fülön a Konfigurációk célpont építési beállításai láthatók. Lehetőség van arra, hogy ezt a listát az Ön által meghatározott építési beállításokkal bővítse. Kattintson a tetején lévő + gombra, és válassza a Felhasználó által definiált beállítás hozzáadása lehetőséget.

Adding a User-Defined Setting

Nevezze el a felhasználó által definiált beállítást BASE_URL-nek. Az alap-URL meghatározása gyakori a backenddel interakcióba lépő alkalmazásoknál.

Adding a User-Defined Setting

Mi az előnye a felhasználó által definiált beállítás meghatározásának? A felhasználó által definiált beállítás lehetővé teszi, hogy a BASE_URL értékét minden egyes építési konfigurációhoz megadjuk. A felhasználó által definiált beállítás bal oldalán található háromszögre kattintva megjelenik a build-konfigurációk listája.

Adding a User-Defined Setting

A Production és Release értékét https://cocoacasts.com-re, a Staging értékét pedig https://staging.cocoacasts.com-re állítsuk.

Adding a User-Defined Setting

Amint a neve is mutatja, a build-beállítás a build-folyamat során érhető el. A kódja nem férhet hozzá közvetlenül az Ön által definiált build-beállításokhoz. Ez egy gyakori tévhit. Van azonban megoldás. Nyissa meg az Info.plist fájlt, és adjon hozzá egy új kulcs/érték párt. Állítsa a kulcsot BASE_URL-re, az értéket pedig $(BASE_URL).

Updating Info.plist

Mit ér egy kulcs/érték pár hozzáadása a projekt Info.plist-jéhez? A kulcs/érték páros értéke a készítéskor frissül. Az Xcode megvizsgálja az aktuális építési konfiguráció építési beállításait, és beállítja a BASE_URL kulcs értékét a projekt Info.plistjében.

Kipróbáljuk ki. Nyissuk meg az AppDelegate.swift fájlt, és navigáljunk a application(_:didFinishLaunchingWithOptions:) metódushoz. Hozzá kell férnünk az Info.plist fájl tartalmához. Ez a Bundle osztály infoDictionary tulajdonságán keresztül lehetséges. Az a köteg, ami minket érdekel, a main bundle. Ahogy a neve is mutatja, a infoDictionary tulajdonság egy kulcs/érték párokat tartalmazó szótár, és mi a BASE_URL kulcs értékéhez férünk hozzá.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: ?) -> Bool { print(Bundle.main.infoDictionary?) return true}

Válasszuk ki a Production sémát a tetején, és futtassuk az alkalmazást a szimulátorban. Ellenőrizzük a kimenetet a konzolon.

Optional(https://staging.cocoacasts.com)

Válasszuk felül a Staging sémát, és futtassuk az alkalmazást a szimulátorban. Ellenőrizze a kimenetet a konzolon. Ez nagyon szép. Ugye?

Optional(https://staging.cocoacasts.com)

Egy szó a biztonságról

A legtöbb fejlesztő nagyon kényelmesnek találja ezt a megoldást. És az is. Van azonban egy probléma. Az App Store-ból letöltött alkalmazásokból könnyű kinyerni az Info.plist fájlt. Mi történik, ha API-kulcsokat, hitelesítő adatokat vagy más érzékeny információkat tárol az Info.plist fájlban? Ez jelentős biztonsági kockázatot jelent, amit el lehet és el is kell kerülnünk.

Mutatnék egy egyszerű megoldást, amely ugyanazt a rugalmasságot, típusbiztonságot és jobb biztonságot kínálja. Hozzon létre egy új Swift fájlt, és nevezze el Configuration.swiftnek.

Creating a Swift File

Creating a Swift File

Definiáljon egy enumot Configuration névvel és egy String típusú nyers értékkel. Minden egyes build konfigurációhoz definiálunk egy esetet, staging, production és release.

import Foundationenum Configuration: String { // MARK: - Configurations case staging case production case release}

Mielőtt folytatnánk a Configuration enum megvalósítását, frissítenünk kell az Info.plist fájlt. Nyissuk meg az Info.plist fájlt, és adjunk hozzá egy kulcs/érték párt. A kulcs a Configuration, az érték pedig $(CONFIGURATION). A CONFIGURATION értéke automatikusan be van állítva számunkra. Nem kell aggódnunk miatta. Ahogy a neve is mutatja, az érték megegyezik annak a build-konfigurációnak a nevével, amellyel a build létrejön.

Update Info.plist

Nézzük meg újra a Configuration.swift fájlt. Szeretnénk könnyen hozzáférni a build konfigurációjához, a projekt Info.plist fájljában tárolt értékhez. Definiáljunk egy statikus, konstans current típusú, Configuration típusú tulajdonságot. Hozzáférünk az Info.plist fájlban a Configuration kulcshoz tárolt értékhez, és egy String példányra alakítjuk. Végzetes hibát dobunk, ha ez nem sikerül, mert ez soha nem történhet meg.

import Foundationenum Configuration: String { // MARK: - Configurations case staging case production case release // MARK: - Current Configuration static let current: Configuration = { guard let rawValue = Bundle.main.infoDictionary? as? String else { fatalError("No Configuration Found") } }()}

Az Info.plist fájlból származó értéket használjuk egy Configuration példány létrehozásához. Újabb végzetes hibát dobunk, ha az inicializálás sikertelen. Vegyük észre, hogy az Info.plist fájlban tárolt értéket kisbetűvel írjuk. A Configuration példányt a lezárás visszaadja. Ne felejtsünk el egy pár zárójelet csatolni a lezáráshoz.

import Foundationenum Configuration: String { // MARK: - Configurations case staging case production case release // MARK: - Current Configuration static let current: Configuration = { guard let rawValue = Bundle.main.infoDictionary? as? String else { fatalError("No Configuration Found") } guard let configuration = Configuration(rawValue: rawValue.lowercased()) else { fatalError("Invalid Configuration") } return configuration }()}

Kipróbáljuk ki. Nyissuk meg az AppDelegate.swift állományt, és írjuk ki az aktuális konfigurációt. Válasszuk ki a Staging sémát, futtassuk az alkalmazást, és vizsgáljuk meg a kimenetet a konzolon.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: ?) -> Bool { print(Configuration.current) return true}
staging

Konfiguráció bővítése

Valószínűleg tudod, hogy szeretem kihasználni az enumokat névterek létrehozására. Hadd mutassam meg, hogyan javíthatjuk a Configuration enum jelenlegi implementációját. A legegyszerűbb megoldás egy baseURL típusú, URL típusú statikus, számított tulajdonság definiálása. A tulajdonságot statikus, konstans tulajdonságként is definiálhatjuk. Ez egy személyes döntés. Egy switch utasítással adjuk vissza az alap URL-t minden egyes build-konfigurációhoz.

import Foundationenum Configuration: String { // MARK: - Configurations case staging case production case release // MARK: - Current Configuration static let current: Configuration = { guard let rawValue = Bundle.main.infoDictionary? as? String else { fatalError("No Configuration Found") } guard let configuration = Configuration(rawValue: rawValue.lowercased()) else { fatalError("Invalid Configuration") } return configuration }() // MARK: - Base URL static var baseURL: URL { switch current { case .staging: return URL(string: "https://staging.cocoacasts.com")! case .production, .release: return URL(string: "https://cocoacasts.com")! } }}

Még néhány részletre szeretnék rámutatni. Először is, nem használok default esetet. Kifejezetten megadom az egyes build-konfigurációkhoz visszaadott értéket. Ez megkönnyíti a problémák felismerését, valamint olvashatóbbá és intuitívabbá teszi a kódot. Másodszor, a felkiáltójelet arra használom, hogy kikényszerítsem a URL struktúra inicializálója által visszaadott érték kicsomagolását. Ez azon ritka esetek egyike, amikor a felkiáltójelet használom egy érték kicsomagolásának kikényszerítésére. Kényelmes, de ami még fontosabb, hogy az alap URL soha nem lehet egyenlő a nil.

Az AppDelegate.swift megnyitása és a baseURL számított tulajdonság értékének kiírása. A sémát a Stagingre állítva futtassuk az alkalmazást, és vizsgáljuk meg a kimenetet a konzolon.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: ?) -> Bool { print(Configuration.baseURL) return true}
https://staging.cocoacasts.com

Ez a minta rendelkezik néhány előnnyel az általunk megvalósított első megoldással szemben. Kihasználjuk a Swift típusbiztonságát, és már nem egy opcionális értékkel van dolgunk. Az Xcode automatikus kiegészítését is kihasználhatjuk. Ezek az előnyök finomak, de idővel értékelni fogjuk őket.

Tegyük fel a cseresznyét a tortára azzal, hogy névtereket adunk a keverékhez. Hozzunk létre egy új Swift fájlt, és nevezzük el Configuration+DarkSky.swift.

Creating a Swift File

Creating a Swift File

Készítsünk egy kiterjesztést a Configuration enum számára, és definiáljunk egy DarkSky nevű enumot a kiterjesztésben. A Dark Sky egy időjárási szolgáltatás, amelyet időről időre használok.

import Foundationextension Configuration { enum DarkSky { }}

A DarkSky enum egy apiKey típusú, String típusú statikus, állandó tulajdonságot definiál. Bekapcsoljuk az aktuális konfigurációt, és minden egyes építési konfigurációhoz más értéket adunk vissza. Ahogy korábban említettem, a tulajdonságot statikus, változó tulajdonságként is deklarálhatjuk. Ennek eldöntése rajtunk múlik.

import Foundationextension Configuration { enum DarkSky { static let apiKey: String = { switch Configuration.current { case .staging: return "123" case .production: return "456" case .release: return "789" } }() }}

Ez a megközelítés több előnnyel is jár. A Dark Sky API konfigurációja szépen névtérbe van helyezve. Ez azt is megkönnyíti, hogy a Dark Sky API konfigurációját egy külön fájlba helyezzük.

Nyissuk meg az AppDelegate.swift fájlt, és írjuk ki a Dark Sky időjárási szolgáltatás API-kulcsát. A sémát Stagingre állítva futtassa az alkalmazást, és vizsgálja meg a kimenetet a konzolon.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: ?) -> Bool { print(Configuration.DarkSky.apiKey) return true}
123

Mi következik?

Ez a minta kényelmet, típusbiztonságot és biztonságot ad a projektekhez. Könnyen elsajátítható, és ha egyszer már megvalósítottuk, egyszerűen bővíthető. Ezt a mintát több okból is szívesen használom. Erősen ajánlom, hogy próbálja ki.