Flavors pohoda ve Flutter, ale peklo v iOS

Občas je nutné vyrobit aplikaci, která má stejný codebase, ale na základě flavors má jiné bundleId, napojený firebase, ikonky a název – běžně to jsou klony pro různé klienty.

Ve Flutter se s flavors pracuje docela jednoduše, zkrátka při buildu přidáte flag –flavor a přidáte libovolný string. Jestliže se tím mění pouze nějaká logika uvnitř dart kódu, nemusíte dělat žádné další úpravy. Pro Android je potřeba definovat Flavors v /app/build.gradle, kde si následně nadefinujete jednotlivé proměnné, včetně jména aplikace. https://developer.android.com/build/build-variants, následně pak proměnné můžete používat v AndroidManifestu.

V app/build.gradle to pak vypadá asi takto:

    
android {    
.
.
.
.
    flavorDimensions "env"
    productFlavors {
        flavor1 {
            dimension "env"
            applicationId "cz.name.app"
            resValue "string", "app_name", "Nazev1"
            resValue "string", "launch_icon", "@mipmap/ic_launcher_flavor1"
        }
        flavor2 {
            dimension "env"
            applicationId "de.name.app"
            resValue "string", "app_name", "Nazev2"
            resValue "string", "launch_icon", "@mipmap/ic_launcher_flavor2""
        }
    }
}

A v AdroidManifest.xml třeba takto:

<application
.
.
.
        android:label="@string/app_name"
        android:name="${applicationName}"
        android:icon="@string/launch_icon">

Větší problém vidím ale u iOS, tyto definice se tam takto jednoduše nedají zapsat a musí si je člověk vyklikat v XCode. Pro začátek je nutné definovat různá Schema pro Runner. Následně pak v Runner->Info->Configurations udělat kopii pro Debug, Profile a Release. Tyto konfigurace pojmenujte jako Debug-Flavor, kde „Flavor“ je váš flavor, který jste si před tím sami nadefinovali a podle kterého jste pojmenovali Vaše nové Schema, pak postupujte stejně i pro Profile a Release.

Tím jste získali možnost buildovat aplikaco pro iOS s flavors. Jenže to nám zatím vůbec nic neřeší. Ale když se podíváte do různých nastavení, zjistíte, že pro každý flavor (resp. Configuration) si můžete navolit vlastní hodnoty. Doporučuji se v přednastavených hodnotách moc nehrabat. Ale můžeme si nadefinovat User-defined Setting. A to v Runner->Build Settings-> + -> User-Defined Setting. Určitě by se vám mohlo hodit mít možnost v rámci buildování aplikace zpřístupněný název vašeho flavor v rámci swift, ale třeba i pre-build scriptů. Proto je dobré si třeba nadefinovat „SchemeName“ a dále třeba „AplicationName“, obojí lze pak vkládat do info.plist, tudíž pak nemusíte mít více info.plist pro různé flavors.

<key>CFBundleDisplayName</key>
<string>$(AplicationName)</string>
<key>CFBundleName</key>
<string>$(SchemeName)</string>

Kde toto opravdu oceníte je, když potřebujete pro Firebase více různých nastavení. Pro začátek si stáhněte všechny GoogleService-Info.plist soubory (pro každý bundleId/flavor), tyto soubory si uložte do /ios/google/_FLAVOR_/, kde _FLAVOR_ je SchemeName z User-Defined setting. Teď, když se pokusíte aplikaci zbuildit, nejspíše se build nepodaří, protože se jednotlivé GoogleService-Info.plist soubory snaží nakopírovat do jednoho adresáře. XCode totiž „Bundle resources“, kam patří i tyto soubory, v rámci Build Phases kopíruje do build adresáře. Soubory mají stejné jméno a build kvůli tomu padá. Proto je nutné tyto soubory z Build Phases -> Copy Bundle Resources odstranit. Nyní to při buildu na této fázi nespadne. Ale spadne Vám to velice pravděpodobně v ios/Runner/AppDelegate.swift, kde máte kód pro konfiguraci FirebaseApp.

FirebaseApp.configure()

Tento kód hledá v buildu GoogleService-Info.plist, který tam teď není. Jako ověření a debugování si můžete vypisovat obsah tohoto souboru v AppDelegate.swift takto:

let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", )!
let data = NSData(contentsOfFile: filePath) as! Data

print(String(data: data, encoding: .utf8))

Jak tedy dostat zpět nastavení pro Google? Ano, v Build Phases spustíme script, který za nás vybere správný soubor a nakopíruje ho na správné místo. Využijeme u toho právě proměnnou SchemeName:

.
.
.
.
GOOGLESERVICE_INFO_PLIST=${PROJECT_DIR}/google/${SchemeName}/GoogleService-Info.plist


# Make sure the dev version of GoogleService-Info.plist exists
echo "Looking for ${SchemeName} in ${GOOGLESERVICE_INFO_PLIST}"
if [ ! -f $GOOGLESERVICE_INFO_PLIST ]
then
    echo "No ${SchemeName} GoogleService-Info.plist found. Please ensure it's in the proper directory."
    exit 1
fi


# Get a reference to the destination location for the GoogleService-Info.plist
PLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}"

cp "${GOOGLESERVICE_INFO_PLIST}" "${PLIST_DESTINATION}"

Tento kód vložíme na konec Run Script v Build Phases. Šlo by to udělat bez těch checků, zda soubor existuje a vypisování debug hodnot. Pokud build proběhne v pořádku, tyto informace se nikde nevypíší, tudíž nás nebudou otravovat. Ale pokud se něco pokazí, tak se vám tyto informace mohou hodit. Script si ověří, zda soubor existuje ve složce /ios/google/flavor/, a pak jej zkopíruje do build adresáře, aby byl dostupný pro další fázi běhu programu.

A to je vše. Takto máte nakonfigurované potřebné proměnné pro build s flavors na iOS.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *