Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
[Do iOS '24] Ship your app on a Friday...and en...
Search
Pol Piella Abadia
November 17, 2024
Programming
1.4k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
[Do iOS '24] Ship your app on a Friday...and enjoy your weekend!
Pol Piella Abadia
November 17, 2024
More Decks by Pol Piella Abadia
See All by Pol Piella Abadia
[SwiftConf '24] Shipping your apps should be fast and easy
polpielladev
0
2.3k
[Workshop] Ship your apps faster with Xcode Cloud
polpielladev
0
170
[SwiftCraft '24] Back to the Future: Swift 6 Edition!
polpielladev
0
2.3k
[London Tech Leaders x AppCircle] The future of mobile releases
polpielladev
0
2.2k
[Swift Heroes '24] Delightful on-device AI experiences
polpielladev
0
2.3k
[SwiftLeeds '23] Delightful Swift CLI applications
polpielladev
0
2.1k
[iOS Dev UK 23] Making developer tools with Swift
polpielladev
0
2.4k
[NSBarcelona/AppTalks Manchester] - Delightful Swift CLI applications
polpielladev
0
2.1k
[Swift Heroes 2023] Making developer tools with Swift
polpielladev
0
2.3k
Other Decks in Programming
See All in Programming
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
15
8.7k
No More NoSuchMethodError
exoego
1
110
dotNET_vulnclicker_intro.pdf
deka_morita
0
150
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
320
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
ActionController meets Protocol Buffers
sorah
0
680
ジョジョの世界をXRで再現する
mofurune
0
180
Simple Storage Service(S3) is not simple
iwatsukayura
0
320
AI主導のプログラミング学習アプリを作ってみた
econanringo
0
180
AWSに止められる覚悟してますか?
morizo_1984
2
550
JIS keyboard support in Omarchy
lef237
0
160
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
370
Featured
See All Featured
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
The Curious Case for Waylosing
cassininazir
1
570
Into the Great Unknown - MozCon
thekraken
41
2.8k
[SF Ruby Conf 2025] Rails X
palkan
3
1.5k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
530
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
4
4.4k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
380
The Pragmatic Product Professional
lauravandoore
37
7.5k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
460
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Test your architecture with Archunit
thirion
2
2.5k
Transcript
Ship your app on a Friday and enjoy your weekend
@POLPIELLADEV @
[email protected]
! Do iOS 2024 /IN/POLPIELLADEV
Indie developer and content creator based in Barcelona Pol Hi!
I’m Pol
None
polpiella.dev
ioscinewsletter.com
None
Mobile releases are difficult there is no denying that!
Accurate representation of an iOS Engineer being asked if they
want to manage a release
None
None
None
None
None
None
None
None
Mobile releases can be risky. Let’s just not ship them
on a Friday.
Gain confidence in your releases and ship anytime! A few
tips to…
#1 Automate as much as possible
None
Fully automated
desc "Distributes a new build to App Store Connect" lane
:distribute do version_number = git_branch.split("/", -1).last setup_ci xcode_select("/Applications/Xcode_16.app") sync_code_signing( type: "appstore", app_identifier: ["com.appdiggershq.fosi"], readonly: true ) increment_version_number(version_number: version_number, xcodeproj: "Fosi.xcodeproj") app_store_connect_api_key( key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"], issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"], key_content: ENV["APP_STORE_CONNECT_KEY_CONTENT"] ) build_number = latest_testflight_build_number + 1 increment_build_number(xcodeproj: "Fosi.xcodeproj", build_number: build_number) build_app(scheme: "Fosi", configuration: 'Release', output_name: "Fosi.ipa") upload_to_app_store(ipa: "Fosi.ipa", precheck_include_in_app_purchases: false) if !git_status(path: "Fosi.xcodeproj/project.pbxproj").empty? git_commit(path: "Fosi.xcodeproj/project.pbxproj", message: "[ci skip] [" release] Updating version") push_to_git_remote(remote: "origin", remote_branch: git_branch) end end
name: Distribute to the App Store Connect concurrency: group: $!"
github.workflow !#-$!" github.ref !# cancel-in-progress: true permissions: contents: write on: push: branches: - 'release!!$' jobs: distribute: runs-on: [macos-14] steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true - run: bundle exec fastlane distribute env: APP_STORE_CONNECT_API_KEY_ID: $!" secrets.APP_STORE_CONNECT_API_KEY_ID !# APP_STORE_CONNECT_ISSUER_ID: $!" secrets.APP_STORE_CONNECT_ISSUER_ID !# APP_STORE_CONNECT_KEY_CONTENT: $!" secrets.APP_STORE_CONNECT_KEY_CONTENT !# MATCH_GIT_BASIC_AUTHORIZATION: $!" secrets.MATCH_GIT_BASIC_AUTHORIZATION !# MATCH_USERNAME: $!" secrets.MATCH_USERNAME !# MATCH_PASSWORD: $!" secrets.MATCH_PASSWORD !#
A comprehensive test suite will drastically improve confidence
A comprehensive test suite will drastically improve confidence* If you
actually remember to run them!
Where should you run tests? PRs to the development branch
Before uploading to ASC On Nightly or Scheduled Workflows
If there is a manual task that causes friction, automate
it!
None
#2 Test your release pipeline
# App Target $ ModuleA.framework $ ModuleB.framework
None
$ ModuleA.framework $ ModuleB.framework # App Target
None
None
lane :nightly do # Let's make some magic happen %
gym( project: "./AwesomeApp.xcodeproj", clean: true, derived_data_path: "./derived-data", output_directory: "./build", output_name: "AwesomeApp.ipa", scheme: "AwesomeApp", export_options: { provisioningProfiles: { "your.bundle.identifier" !% "Your Provisioning Profile Name", } } ) deliver( ipa: "build/AwesomeApp.ipa", verify_only: true ) end
name: Nightly on: schedule: - cron: '0 0 * *
*' jobs: nightly: runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Run nightly lane run: bundle exec fastlane nightly
None
None
#3 Adopt a release strategy
None
None
None
None
Quality matters WAY MORE than quantity The silver bullet of
release frequency
Release less, more o!en*! Use release trains to… *Always allowing
ample time for beta testing and QA
You’ll unleash these benefits & Reduced risk ' Predictability (
Faster Feedback
None
#4 Use Feature Toggles
You can just turn it off! If something goes wrong
in production…
enum ToggleableFeature: String { case homeSwiftUIRefactor = "homeRefactor" } struct
HomeViewWrapper: View { @Environment(FeatureToggleService.self) var featureToggles var body: some View { if featureToggles.isOn(.homeSwiftUIRefactor) { HomeView() } else { LegacyHomeView() } } }
None
#5 Observe and Monitor
You first need to know that one is happening! To
quickly react to an incident
Crashes & Performance
Crashes & Performance
Crashes & Performance
None
Analytics
Customer Satisfaction
Customer Satisfaction
User Feedback
User Feedback
User Feedback
None
CI/CD Velocity Metrics
None
#6 Do not release to all users at once
None
None
None
None
None
import AWSLambdaRuntime import AWSLambdaEvents import AppStoreConnect_Swift_SDK @main struct PausePhasedReleaseWebhook: SimpleLambdaHandler
{ func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws !& APIGatewayV2Response { guard let body = event.body else { return .init(statusCode: .badRequest) } !' Read the webhook payload and decide whehter to stop the release or not!!( } }
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws !& APIGatewayV2Response
{ }
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws !& APIGatewayV2Response
{ !' … !' Get latest live version let endpoint = APIEndpoint .v1 .apps .id("1234567890") .appStoreVersions .get(parameters: .init(filterPlatform: [.ios], filterAppVersionState: [.readyForDistribution])) let response = try await provider.request(endpoint) let allPhasedReleases = response.included? .compactMap { item in if case .appStoreVersionPhasedRelease(let phasedRelease) = item { return phasedRelease } return nil } guard let id = allPhasedReleases!)first!)id else { return .init(statusCode: .notFound) } }
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws !& APIGatewayV2Response
{ !' … let attributes = AppStoreVersionPhasedReleaseUpdateRequest.Data.Attributes( phasedReleaseState: .paused ) let data = AppStoreVersionPhasedReleaseUpdateRequest.Data( type: .appStoreVersionPhasedReleases, id: id, attributes: attributes ) let requestBody = AppStoreVersionPhasedReleaseUpdateRequest(data: data) let phasedReleaseEndpoint = APIEndpoint .v1 .appStoreVersionPhasedReleases .id(id) .patch(requestBody) _ = try await provider.request(phasedReleaseEndpoint) return .init(statusCode: .ok) }
*Things that are out of your control and fail oMen
*Your core business logic that your app depends on #7 Move risky* logic to the server!
None
None
None
Ship bug fixes worldwide for all versions of your app
Within seconds…
# Your App • Risky logic • Risky models
$ Risky Models # Your App • Risky logic
$ Risky Models # Your App ⚙ Risky Logic (BE)
None
#8 Roll back to a stable version immediately
1.0.0
1.0.0 1.0.1
1.0.0
1.0.0 1.0.1
1.0.0 1.0.1 1.0.2
1.0.0 1.0.2
None
Gain confidence on your release process! Automate manual processes Use
Feature Toggles Leverage the BE ecosystem Observe and Monitor Ship less, more o!en! Roll back to stable versions of your app How to use CI/CD to ship your mobile apps
And ship your apps on a Friday… or anytime! Gain
confidence in your releases
Find me online! Last tickets!