4. Nov 2021
iOSXcode project templates Vol. 4: Ako vyriešiť licencie knižníc tretích strán
Stále ti zaberá tvorba nového projektu veľa času? Pripravil som pre teba ďalší diel venovaný Xcode project templates, ktoré ti ušetria čas. Prečítaj si návod, ako na to a možno zmeníš spôsob vytvárania tvojich projektov. Dnes vám ukážem, ako integrovať do projektu obrazovku so všetkými knižnicami tretích strán pomocou knižnice LicensePlist
Predtým, ako sa vrhneme na poslednú časť zo série Xcode project templates
si nezabudni prečítať predchádzajúce články. V tých nájdeš návody krok po kroku, ako spustiť a kde nájsť šablóny, ako automatizovať konfiguráciu projektu či konfiguráciu swiftlintu a swiftgenu.
Teraz späť k dnešnej téme. LicensePlist je nástroj, ktorý vygeneruje plist všetkých knižníc tretích strán vrátané SPM, Carthage a Cocoapod knižníc.
Na konci tohto článku by sme po vytvorení projektu a úspešnom spustení
na simulátore mali vidieť v nastaveniach aplikácie tlačidlo licencie kde uvidíme všetky použite knižnice tretích strán a to bez žiadneho setupu. Akonáhle úspešne vytvoríš template budúce integrácie už máš za sebou.
Najskôr si ale trocha priblížime kontext. Prečo vôbec uvádzať knižnice
v aplikácii? Dôvod je jednoduchý. Ak ste niekedy prezerali github nejakej knižnice určite ste si všimli súbor LICENSE.
Tu sú uvedené podmienky používania knižnice a aj keď je knižnica opensource, stále je možné, že vyžaduje deklaráciu použitia v appkách ktoré sú verejne dostupné. Nie každá knižnica to vyžaduje, ale je šanca, že skôr či neskôr narazíte na knižnicu, ktorá to vyžaduje. Je to vlastne spôsob, ako si nesposobiť právne problémy.
Teraz poďme na integráciu. Prvé čo treba urobiť je pridať si LicencePlist do pod dependencies v našom pod template.
platform :ios, '___VARIABLE_VERSION___
target '___PACKAGENAME___' do
use_frameworks!
pod 'SwiftGen'
pod 'SwiftLint'
pod 'LicensePlist'
end
Zároveň tým Template na pody nadobúda zmysel, pretože si uľahčujeme pridávanie viacerých knižnic, dohľadávanie názvu atď...
Ako ďalší krok vytvoríme nový sub-template, kde ako jediný súbor budeme mať settings.bundle a pri targete určíme podobne, ako v minulom článku target, kde budú 2 build scripty. Tieto musia odkazovať na Licene-plist script a druhý bude output kopírovať a vkladať do settings.bundle súboru. Následne po sebe uprace a output vymaže.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key> <!--States the template type-->
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<!--States if the template is a standalone template or a part
of template used in a diffrent template. In this case it will
be hidden from the template wizzard-->
<key>Concrete</key>
<false/>
<key>Identifier</key> <!--Template ID in the "template space"-->
<string>com.goodrequest.License</string>
<key>Definitions</key> <!--Template variables-->
<dict>
<key>Application/Settings.bundle</key>
<dict>
<key>Path</key>
<string>Settings.bundle</string>
<key>Group</key>
<array>
<string>Application</string>
</array>
</dict>
</dict>
<key>Nodes</key>
<array>
<string>Application/Settings.bundle</string>
</array>
<key>Targets</key>
<array>
<dict>
<key>TargetIdentifier</key>
<string>com.apple.dt.cocoaTouchApplicationTarget</string>
<key>BuildPhases</key>
<array>
<dict>
<key>Name</key>
<string>📁 Run licence generator</string>
<key>SortOrder</key>
<string>1</string>
<key>Class</key>
<string>ShellScript</string>
<key>ShellPath</key>
<string>/bin/sh</string>
<key>ShellScript</key>
<string>PLIST_PATH="${PODS_ROOT}/LicensePlist/license-plist"
if test -f "${PLIST_PATH}"; then
"${PLIST_PATH}" --package-path "${PROJECT_NAME}.xcworkspace/xcshareddata/swiftpm/Package.swift" --suppress-opening-directory
else
echo "warning: LicencePlist not installed, download from https://github.com/mono0926/LicensePlist 😭😩🤦♂️"
fi</string>
</dict>
<dict>
<key>Name</key>
<string>📁 Copy licence data into settings bundle</string>
<key>SortOrder</key>
<string>1</string>
<key>Class</key>
<string>ShellScript</string>
<key>ShellPath</key>
<string>/bin/sh</string>
<key>ShellScript</key>
<string>cp -R "${SRCROOT}/com.mono0926.LicensePlist.Output/" "${SRCROOT}/${PROJECT_NAME}/Application/Settings.bundle/"
rm -rf "${SRCROOT}/com.mono0926.LicensePlist.Output/"</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>
Settings.bundle vytvoríte jednoducho cez xcode a zvolením settings šablóny.
Vytvoríte tým niečo takéto, kde sa o content postará script, ktorý všetky informácie z knižníc v projekte vloží dnu. Takto vytvorený súbor skopírujte
a vložte do zložky našej sub-šablóny.
Následne pridáme túto šablónu do našej hlavnej projektovej šablóny.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key>
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<key>Identifier</key>
<string>com.goodrequest.singleViewApplication</string>
<key>Ancestors</key>
<array>
<string>com.apple.dt.unit.applicationBase</string>
<string>com.apple.dt.unit.iosBase</string>
<string>com.goodrequest.CocoaPods</string> <!--ID of the Pod template-->
<string>com.goodrequest.License</string>
</array>
<key>Concrete</key>
<true/>
<key>Definitions</key>
<dict>
<key>Application/AppDelegate.swift</key>
<dict>
<key>Path</key>
<string>Application/AppDelegate.swift</string>
<key>Group</key>
<array>
<string>Application</string>
</array>
</dict>
<key>Models/Response/SampleResponse.swift</key>
<dict>
<key>Path</key>
<string>Models/Response/SampleResponse.swift</string>
<key>Group</key>
<array>
<string>Models</string>
<string>Response</string>
</array>
</dict>
<key>Models/Request/SampleRequest.swift</key>
<dict>
<key>Path</key>
<string>Models/Request/SampleRequest.swift</string>
<key>Group</key>
<array>
<string>Models</string>
<string>Request</string>
</array>
</dict>
<key>Managers/Dependency/DependencyContainer.swift</key>
<dict>
<key>Path</key>
<string>Managers/Dependency/DependencyContainer.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Dependency</string>
</array>
</dict>
<key>Managers/Request/RequestManager.swift</key>
<dict>
<key>Path</key>
<string>Managers/Request/RequestManager.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Request</string>
</array>
</dict>
<key>Managers/Request/RequestManagerType.swift</key>
<dict>
<key>Path</key>
<string>Managers/Request/RequestManagerType.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Request</string>
</array>
</dict>
<key>Managers/Request/Endpoint.swift</key>
<dict>
<key>Path</key>
<string>Managers/Request/Endpoint.swift</string>
<key>Group</key>
<array>
<string>Managers</string>
<string>Request</string>
</array>
</dict>
<key>Coordinators/Coordinator.swift</key>
<dict>
<key>Path</key>
<string>Coordinators/Coordinator.swift</string>
<key>Group</key>
<string>Coordinators</string>
</dict>
<key>Coordinators/AppCoordinator.swift</key>
<dict>
<key>Path</key>
<string>Coordinators/AppCoordinator.swift</string>
<key>Group</key>
<string>Coordinators</string>
</dict>
<key>Screens/SampleController/SampleViewController.swift</key>
<dict>
<key>Path</key>
<string>Screens/SampleController/SampleViewController.swift</string>
<key>Group</key>
<array>
<string>Screens</string>
<string>SampleController</string>
</array>
</dict>
<key>Views/SampleView/SampleView.swift</key>
<dict>
<key>Path</key>
<string>Views/SampleView/SampleView.swift</string>
<key>Group</key>
<array>
<string>Views</string>
<string>SampleView</string>
</array>
</dict>
<key>Resources/Assets.xcassets</key>
<dict>
<key>Path</key>
<string>Resources/Assets.xcassets</string>
<key>Group</key>
<array>
<string>Resources</string>
</array>
</dict>
<key>Resources/Colors.xcassets</key>
<dict>
<key>Path</key>
<string>Resources/Colors.xcassets</string>
<key>Group</key>
<array>
<string>Resources</string>
</array>
</dict>
<key>Resources/SwiftGen/swiftgen.yml</key>
<dict>
<key>Path</key>
<string>Resources/SwiftGen/swiftgen.yml</string>
<key>Group</key>
<array>
<string>Resources</string>
<string>SwiftGen</string>
</array>
</dict>
<key>Resources/SwiftGen/Colors.swift</key>
<dict>
<key>Path</key>
<string>Resources/SwiftGen/Colors.swift</string>
<key>Group</key>
<array>
<string>Resources</string>
<string>SwiftGen</string>
</array>
</dict>
<key>Resources/SwiftGen/Assets.swift</key>
<dict>
<key>Path</key>
<string>Resources/SwiftGen/Assets.swift</string>
<key>Group</key>
<array>
<string>Resources</string>
<string>SwiftGen</string>
</array>
</dict>
<key>Resources/SwiftGen/Localizable.swift</key>
<dict>
<key>Path</key>
<string>Resources/SwiftGen/Localizable.swift</string>
<key>Group</key>
<array>
<string>Resources</string>
<string>SwiftGen</string>
</array>
</dict>
<key>Helpers/TypeAliases.swift</key>
<dict>
<key>Path</key>
<string>Helpers/TypeAliases.swift</string>
<key>Group</key>
<string>Helpers</string>
</dict>
<key>Resources/.swiftlint.yml</key>
<dict>
<key>Path</key>
<string>Resources/.swiftlint.yml</string>
<key>Group</key>
<string>Resources</string>
</dict>
</dict>
<key>Nodes</key>
<array>
<string>Helpers/TypeAliases.swift</string>
<string>Application/AppDelegate.swift</string>
<string>Models/Response/SampleResponse.swift</string>
<string>Models/Request/SampleRequest.swift</string>
<string>Managers/Dependency/DependencyContainer.swift</string>
<string>Managers/Request/RequestManager.swift</string>
<string>Managers/Request/RequestManagerType.swift</string>
<string>Managers/Request/Endpoint.swift</string>
<string>Coordinators/Coordinator.swift</string>
<string>Coordinators/AppCoordinator.swift</string>
<string>Screens/SampleController/SampleViewController.swift</string>
<string>Views/SampleView/SampleView.swift</string>
<string>Resources/Colors.xcassets</string>
<string>Resources/Assets.xcassets</string>
<string>Resources/SwiftGen/swiftgen.yml</string>
<string>Resources/Colors.xcassets</string>
<string>Resources/Assets.xcassets</string>
<string>Resources/Localizable.strings</string>
<string>Resources/SwiftGen/Assets.swift</string>
<string>Resources/SwiftGen/Colors.swift</string>
<string>Resources/SwiftGen/Localizable.swift</string>
<string>Resources/.swiftlint.yml</string>
</array>
<key>Targets</key>
<array>
<dict>
<key>TargetIdentifier</key>
<string>com.apple.dt.cocoaTouchApplicationTarget</string>
<key>BuildPhases</key>
<array>
<dict>
<key>Name</key>
<string>🛠 Generate enums with SwiftGen</string>
<key>SortOrder</key>
<string>1</string>
<key>Class</key>
<string>ShellScript</string>
<key>ShellPath</key>
<string>/bin/sh</string>
<key>ShellScript</key>
<string>SWIFT_GEN="${PODS_ROOT}/SwiftGen/bin/swiftgen"
SWIFT_GEN_CONFIG="${PROJECT_DIR}/${PROJECT_NAME}/Resources/SwiftGen/swiftgen.yml"
if test -f "${SWIFT_GEN}"; then
if test -f "${SWIFT_GEN_CONFIG}"; then
"${SWIFT_GEN}" config run --verbose --config "${SWIFT_GEN_CONFIG}"
else
echo "warning: Swifgen configuration file missing make sure the path "$SWIFT_GEN_CONFIG" contains the file 😭😩🤦♂️"
fi
else
echo "warning: SwiftGen not installed, download from https://github.com/SwiftGen/SwiftGen 😭😩🤦♂️"
fi</string>
</dict>
<dict>
<key>Name</key>
<string>🧹 Validate codestyle with SwiftLint</string>
<key>SortOrder</key>
<string>1</string>
<key>Class</key>
<string>ShellScript</string>
<key>ShellPath</key>
<string>/bin/sh</string>
<key>ShellScript</key>
<string>SWIFT_LINT="${PODS_ROOT}/SwiftLint/swiftlint"
SWIFT_LINT_CONFIG="${SRCROOT}/${PROJECT_NAME}/Resources/.swiftlint.yml"
if test -f "${SWIFT_LINT}"; then
if test -f "${SWIFT_LINT_CONFIG}"; then
"${SWIFT_LINT}" --config "${SWIFT_LINT_CONFIG}"
else
echo "warning: Swiflint configuration file missing make your the submodule is fetched 😭😩🤦♂️"
fi
else
echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint 😭😩🤦♂️"
fi</string>
</dict>
</array>
</dict>
</array>
<key>Project</key>
<dict>
<key>SharedSettings</key>
<dict>
<key>SWIFT_VERSION</key>
<string>5.0</string>
</dict>
</dict>
</dict>
</plist>
Teraz už iba vygenerujte projekt a pozrite sa, ako sa vám settings bundle naplní informáciami o knižniciach.
Zároveň pri nainštalovaní aplikácie na simulátor je pekne vidno že licencie
sú pridané.
To je pre dnešný návod všetko. Zároveň treba dodať že všetky materiály nájdete na príslušnej branch nášho repozitára so šablónami.
Chceš sa stať súčasťou inovatívneho tímu? Pridaj sa k nám do iOS 🍎