Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[NYSwifty] Getting started with Xcode Cloud

Pol Piella Abadia
April 18, 2023
1.1k

[NYSwifty] Getting started with XcodeΒ Cloud

Pol Piella Abadia

April 18, 2023
Tweet

Transcript

  1. Who am I? πŸ‘¨πŸ’» Senior software engineer at the BBC

    ✍ Weekly blog writer at polpiella.dev πŸ“¬ iOS CI/CD Newsle tt er curator 🐝 Based in Manchester πŸ“ From Barcelona
  2. What I’ll talk about β€’ What is CI/CD and why

    do you need it? β€’ What is Xcode Cloud? β€’ Two workflows β€’ Programmatic Xcode Cloud runs β€’ Custom Xcode Cloud functionality
  3. What is CI/CD? - RedHat β€œContinuous Integration and Continuous Deployment

    is a method to frequently deliver apps to customers by introducing automation into the stages of app development.”
  4. Push a new tag Notify of a change Perform action

    Report action output Submit Send m essage Trigger a scheduled work fl ow
  5. β€’ Automate complex processes β€’ Schedule repetitive tasks β€’ Flexibility

    and scalability β€’ Build con fi dence on your codebase β€’ Guard against vulnerabilities β€’ Enforce code styles Why do you need CI/CD?
  6. β€’ Apple’s CI/CD service β€’ Equivalent to GHA, Bitrise, etc.

    β€’ Deeply integrated into Xcode and App Store Connect β€’ Introduced in WWDC21 β€’ Subscriptions were available from August 2022 What is Xcode Cloud?
  7. How does it work? β€’ Products, Work fl ows and

    Builds. β€’ Product represents your app in Xcode Cloud. β€’ Multiple work fl ows in a product. β€’ Builds are runs of a speci fi c work fl ow.
  8. The anatomy of a workflow β€’ Where - Environment β€’

    macOS version, Xcode version, Environment variables and secrets, Clean β€’ When - Start conditions β€’ Branch, PR, Tag, Scheduled β€’ What - Actions + Post-Actions β€’ Actions β€’ Build, Test - Multiple devices and versions, Archive and Analyse β€’ Post-Actions β€’ Deploy to Test fl ight, send Slack messages and send emails
  9. Where can workflows run? β€’ macOS runners β€’ Set up

    per-work fl ow β€’ Multiple Xcode versions available β€’ Multiple macOS versions available β€’ No self-hosted runners β€’ Very app focused
  10. Let’s break it down and let’s read the small le

    tt er πŸ˜… β€’ A compute hour = 60 minutes of cloud execution. β€’ 12 serial tasks of 5 minutes each = 1 compute hour β€’ 12 parallel tasks of 5 minutes each = 1 compute hour. β€’ 25 free hours - It is more than you think! ⚠ Free tier ends in December 2023! It’s going to be $14.99/month on the cheapest plan
  11. The main workflow β€’ Triggered on every push to the

    main branch β€’ Runs all unit tests β€’ Archives the app β€’ Distributes the app to AppCenter β€’ Distributes the app to internal testers β€’ Noti fi es a slack channel on completion β€’ Needs to make use of cocoapods ⚠ πŸ§ͺ
  12. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then fi

    . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh
  13. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then #

    πŸ“ Set the install path to a local directory bundle config set -- local path 'vendor' fi . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh
  14. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then #

    πŸ“ Set the install path to a local directory bundle config set -- local path 'vendor' # ⬇ Install all dependencies bundle install fi . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh
  15. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then #

    πŸ“ Set the install path to a local directory bundle config set -- local path 'vendor' # ⬇ Install all dependencies bundle install # β˜• Install all pods bundle exec pod install fi . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh
  16. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then fi

    . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh β”‚ └── ci_post_xcodebuild.sh
  17. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then #

    πŸ†™ Go one directory up cd .. fi . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh β”‚ └── ci_post_xcodebuild.sh
  18. #! /bin/sh if [[ $CI_WORKFLOW == "Main" ]]; then #

    πŸ†™ Go one directory up cd .. # πŸƒ Run the custom lane ... bundle exec fastlane upload_to_appcenter fi . β”œβ”€β”€ ci_scripts β”‚ └── ci_post_clone.sh β”‚ └── ci_post_xcodebuild.sh
  19. default_platform(:mac) platform :mac do desc "Description of what the lane

    does" lane :upload_to_appcenter do archive_path_dir = ENV["CI_ARCHIVE_PATH"] pkg_file = File.join(archive_path_dir, "Products", "Applications", "QReate.app") dsym_file = File.join(archive_path_dir, "dSYMs", "QReate.app.dSYM") appcenter_upload( api_token: ENV["APP_CENTER_API_TOKEN"], owner_name: "polpielladev", app_name: "QReate", release_notes: "Bug fixing and new features", file: pkg_file, dsym: dsym_file, notify_testers: false ) end end
  20. default_platform(:mac) platform :mac do desc "Description of what the lane

    does" lane :upload_to_appcenter do archive_path_dir = ENV["CI_ARCHIVE_PATH"] pkg_file = File.join(archive_path_dir, "Products", "Applications", "QReate.app") dsym_file = File.join(archive_path_dir, "dSYMs", "QReate.app.dSYM") appcenter_upload( api_token: ENV["APP_CENTER_API_TOKEN"], owner_name: "polpielladev", app_name: "QReate", release_notes: "Bug fixing and new features", file: pkg_file, dsym: dsym_file, notify_testers: false ) end end
  21. default_platform(:mac) platform :mac do desc "Description of what the lane

    does" lane :upload_to_appcenter do archive_path_dir = ENV["CI_ARCHIVE_PATH"] pkg_file = File.join(archive_path_dir, "Products", "Applications", "QReate.app") dsym_file = File.join(archive_path_dir, "dSYMs", "QReate.app.dSYM") appcenter_upload( api_token: ENV["APP_CENTER_API_TOKEN"], owner_name: "polpielladev", app_name: "QReate", release_notes: "Bug fixing and new features", file: pkg_file, dsym: dsym_file, notify_testers: false ) end end
  22. Let’s schedule a build with Xcode Cloud πŸŽ‰ β€’ A

    build from the latest main commit β€’ Every Sunday evening β€’ Released only to external testers (me πŸ˜…) β€’ Once I have gathered feedback, I can release the version.
  23. β€’ Upload internal TestFlight builds on PRs β€’ I don’t

    want to upload automatically on every PR change β€’ I want to do this step manually β€’ I would like to trigger a build directly from the PR page. Building a custom start condition
  24. How does it work? issue_comment Webhook Trigger a new work

    fl ow API upload to Test fl ight Comment on a PR
  25. { "action": "created", "issue": { "pull_request": { "url": β€œhttps: //

    api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } } GitHub issue_comment webhook
  26. GitHub issue_comment webhook { "action": "created", "issue": { "pull_request": {

    "url": β€œhttps: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } } /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function
  27. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function {

    "action": "created", "issue": { "pull_request": { "url": β€œhttps: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } }
  28. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function let

    productId: String let repositoryId: String { "action": "created", "issue": { "pull_request": { "url": β€œhttps: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } }
  29. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function let

    productId: String let repositoryId: String { "action": "created", "issue": { "pull_request": { "url": β€œhttps: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } } /v1/ciProducts/productId/work fl ows? fi elds[ciWork fl ows]=name
  30. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function let

    productId: String let repositoryId: String { "action": "created", "issue": { "pull_request": { "url": β€œhttps: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } } /v1/ciProducts/productId/work fl ows? fi elds[ciWork fl ows]=name let workflowId: String
  31. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function let

    productId: String let repositoryId: String /v1/ciProducts/productId/work fl ows? fi elds[ciWork fl ows]=name let workflowId: String /repos/OWNER/REPO/pulls/PULL_NUMBER { "action": "created", "issue": { "pull_request": { "url": "https: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } }
  32. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function let

    productId: String let repositoryId: String /v1/ciProducts/productId/work fl ows? fi elds[ciWork fl ows]=name let workflowId: String /repos/OWNER/REPO/pulls/PULL_NUMBER { "action": "created", "issue": { "pull_request": { "url": "https: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } } /v1/scmRepositories/repositoryId/gitReferences let gitReferenceId: String
  33. GitHub issue_comment webhook /v1/ciProducts? fi lter[productType]=APP&include= primaryRepositories Serverless function let

    productId: String let repositoryId: String /v1/ciProducts/productId/work fl ows? fi elds[ciWork fl ows]=name let workflowId: String /repos/OWNER/REPO/pulls/PULL_NUMBER { "action": "created", "issue": { "pull_request": { "url": "https: // api.github.com/…" } }, "repository": { "name": "QRBuddy" }, "comment": { "body": "Upload to testflight" } } /v1/scmRepositories/repositoryId/gitReferences let gitReferenceId: String /v1/ciBuildRuns
  34. Xcode Cloud webhooks β€’ There are only 3 types β€’

    Create build β€’ Start build β€’ Complete buid β€’ Only con fi gurable in App Store Connect β€’ Can’t subscribe to speci fi c events. β€’ ⚠ Only 5 webhooks per product