Slide 1

Slide 1 text

Pol Piella Abadia - πŸ“Mobile Devops Summit - 10/11/2022 Getting ahead of the game: Avoid Release day drama! @polpielladev

Slide 2

Slide 2 text

β€’ πŸ§‘πŸ’» Senior iOS Engineer @ BBC iPlayer β€’ πŸ“ From Barcelona β€’ πŸ‡¬πŸ‡§ Based in Manchester β€’ ❀ Swift and developer tooling β€’ πŸ‘• Random fact: I collect football shirts! Hi, I’m Pol πŸ‘‹

Slide 3

Slide 3 text

Let’s paint a picture πŸ₯— NutriFit

Slide 4

Slide 4 text

Let’s paint a picture Many new features since previous release Built with UIKit. New release includes first SwiftUI views CI is all set up using fastlane Modular ( SPM & Xcode Projects) codebase

Slide 5

Slide 5 text

What does the CI look like? πŸ§ͺ β€’ Triggered when a push to a PR occurs β€’ Runs Unit Tests β€’ Runs UI Tests β€’ Uses the Debug con fi guration of the App β€’ Performs code style checks PR pipeline πŸš€ β€’ Manually triggered β€’ Bumps version β€’ Archives the app β€’ Uploads to the App Store Release pipeline πŸ‘¨πŸ’» β€’ Triggered by every push to main β€’ Runs Unit Tests β€’ Runs UI Tests β€’ Uses the Debug con fi guration of the App Main pipeline

Slide 6

Slide 6 text

What does the CI look like? πŸš€ β€’ Manually triggered β€’ Bumps version β€’ Archives the app β€’ Uploads to the App Store Release pipeline

Slide 7

Slide 7 text

πŸš€ Let’s release!

Slide 8

Slide 8 text

πŸš€ Let’s release! Trigger pipeline τ€£ Bump version τ€£ Archive τ€€³ Upload to ASC τ€

Slide 9

Slide 9 text

An unusual archive error πŸ›‘ There is an error with the new SwiftUI View No SwiftUI symbols are found The solution is not obvious Need to investigate further

Slide 10

Slide 10 text

Let’s archive locally…

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

But… App target’s minimum deployment version is 15 Xcode doesn’t build for armv7 when targeting iOS > 11 TodayView is imported by the App Target

Slide 13

Slide 13 text

But… Source: https://developer.apple.com/forums/thread/690405

Slide 14

Slide 14 text

// swift-tools-version: 5.6 import PackageDescription let package = Package( name: "TodayFeature", targets: [ .target( name: "TodayFeature" ), .testTarget( name: "TodayFeatureTests", dependencies: ["TodayFeature"] ) ] ) It was the package all along…

Slide 15

Slide 15 text

// swift-tools-version: 5.6 import PackageDescription let package = Package( name: "TodayFeature", platforms: [ .iOS(.v15) ], targets: [ .target( name: "TodayFeature" ), .testTarget( name: "TodayFeatureTests", dependencies: ["TodayFeature"] ) ] ) It was the package all along…

Slide 16

Slide 16 text

🀞Let’s try again!

Slide 17

Slide 17 text

🀞Let’s try again! Trigger pipeline τ€£ Bump version τ€£ Upload to ASC τ€€³ Archive τ€£

Slide 18

Slide 18 text

Upload to ASC τ€€³ Binary contains duplicated bundles. This is related to a new framework called Schedule. It is depended on by multiple other frameworks. The error is due to the way Schedule is being imported.

Slide 19

Slide 19 text

πŸ“± Nutri fi t πŸ“¦ Settings.framework πŸ“¦ Streak.framework Upload to ASC τ€€³

Slide 20

Slide 20 text

Upload to ASC πŸ“¦ Settings.framework πŸ“¦ Streak.framework πŸ“± Nutri fi t τ€€³

Slide 21

Slide 21 text

🀷 One last time?

Slide 22

Slide 22 text

🀷 One last time? Trigger pipeline τ€£ Bump version τ€£ Upload to ASC τ€€³ Archive τ€£

Slide 23

Slide 23 text

What now?! 😠 It looks like the version of Xcode in the CI is not supported App was archived with Xcode 14 beta 2 A stable version of Xcode needs to be installed The beta version was in the CI for testing.

Slide 24

Slide 24 text

Source: https://developer.apple.com/download/applications/

Slide 25

Slide 25 text

Source: https://giphy.com/gifs/hulu-hulu-original-moone-boy-l0HlBO7eyXzSZkJri

Slide 26

Slide 26 text

😑 Surely now!?

Slide 27

Slide 27 text

😑 Surely now!? Trigger pipeline τ€£ Bump version τ€£ Archive τ€£ Upload to ASC τ€£

Slide 28

Slide 28 text

Let’s recap our release SwiftUI symbols could not be found on archive Invalid version of Xcode Same binary embedded multiple times Source: https://giphy.com/gifs/this-is- fi ne-QMHoU66sBXqqLqYvGO

Slide 29

Slide 29 text

πŸŒ… Could we have caught this early?

Slide 30

Slide 30 text

πŸŒ… Could we have caught this early? We should periodically check processes that don’t happen often. Scheduled workflows can be very helpful Triggered at specific times using cron expressions A good example are nightly CI runs Certain workflows are time consuming and can cause disruption Source: https://crontab.guru/#0_0_*_*_*

Slide 31

Slide 31 text

Adding a new CI pipeline πŸ§ͺ πŸš€ PR pipeline πŸ‘¨πŸ’» Main pipeline Release pipeline 🌝 Nightly pipeline β€’ Scheduled to run every night β€’ Can be achieved with most CI providers β€’ Archives the app β€’ Validates the binary with App Store Connect.

Slide 32

Slide 32 text

πŸ§‘πŸ’» Show me the code!

Slide 33

Slide 33 text

πŸ§‘πŸ’» Show me the code! τ€‡Ώ The code in the following slides uses fastlane but the same result can be achieved using other means, such as with a simple bash script.

Slide 34

Slide 34 text

lane :nightly do # Let's make some magic happen πŸͺ„ end πŸ§‘πŸ’» Show me the code!

Slide 35

Slide 35 text

lane :nightly do # Let's make some magic happen πŸͺ„ gym( project: "./NutriFit.xcodeproj", clean: true, derived_data_path: "./derived-data", output_directory: "./build", output_name: "NutriFit.ipa", scheme: "NutriFit", export_options: { provisioningProfiles: { "dev.polpiella.NutriFit" => "NutriFit App Store", } } ) end πŸ§‘πŸ’» Show me the code!

Slide 36

Slide 36 text

lane :nightly do # Let's make some magic happen πŸͺ„ gym( project: "./NutriFit.xcodeproj", clean: true, derived_data_path: "./derived-data", output_directory: "./build", output_name: "NutriFit.ipa", scheme: "NutriFit", export_options: { provisioningProfiles: { "dev.polpiella.NutriFit" => "NutriFit App Store", } } ) deliver( ipa: "build/Nutrifit.ipa" ) end πŸ§‘πŸ’» Show me the code!

Slide 37

Slide 37 text

lane :nightly do # Let's make some magic happen πŸͺ„ gym( project: "./NutriFit.xcodeproj", clean: true, derived_data_path: "./derived-data", output_directory: "./build", output_name: "NutriFit.ipa", scheme: "NutriFit", export_options: { provisioningProfiles: { "dev.polpiella.NutriFit" => "NutriFit App Store", } } ) deliver( ipa: "build/Nutrifit.ipa", verify_only: true ) end πŸ§‘πŸ’» Show me the code!

Slide 38

Slide 38 text

fastlane nightly

Slide 39

Slide 39 text

πŸ§‘πŸ’» Let’s see it in action!

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

🧐 Want to learn a bit more? Source: https://github.com/fastlane/fastlane/pull/20247

Slide 42

Slide 42 text

🧐 The output might look familiar

Slide 43

Slide 43 text

🧐 The output might look familiar

Slide 44

Slide 44 text

🧐 What else could we do?

Slide 45

Slide 45 text

🧐 What else could we do? πŸ“ˆ β€’ List the number of modules in the app. β€’ Show the amount of legacy vs new code (swift vs obj-c?) Collect metrics πŸ§ͺ β€’ End 2 End tests β€’ Accessibility testing β€’ Any other β€˜specialised’ long-running test plans. Run expensive tests 🧽 β€’ Track the size of the IPA β€’ Vulnerability checks β€’ Unused code analysis - Feature Flags? β€’ Create dependency reports Sanity checks

Slide 46

Slide 46 text

When to use scheduled workflows… 1⃣ Gaining con fi dence in processes which are very important but don’t get run very often (e.g. release pipeline). 2⃣ Performing useful tasks that are time consuming with minimal disruption. 3⃣ Automating repetitive tasks which always happen at a certain time (once a week, twice a month, once a day, etc.)

Slide 47

Slide 47 text

polpiella.dev @polpielladev Say hi! πŸ‘‹