4. Nov 2021iOS

Xcode 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

Andrej JaššoiOs Developer

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 🍎

Andrej JaššoiOs Developer