Last time my signing certificate had expired, I regenerated them manually, as described in this old post.

This time I tried to let XCode do it by itself.

I checked this option. Then it had me sign into my Apple Developer account. After a few seconds of spinning, it successfully created a Signing Certificate. This was so painless, I can hardly believe my eyes!

After running for a bit, compilation failed with this error:

Building a deployable iOS app requires a selected Development Team with a 
Provisioning Profile. Please ensure that a Development Team is selected by:
  1- Open the Flutter project's Xcode target with
       open ios/Runner.xcworkspace
  2- Select the 'Runner' project in the navigator then the 'Runner' target
     in the project settings
  3- Make sure a 'Development Team' is selected under Signing & Capabilities > Team. 
     You may need to:
         - Log in with your Apple ID in Xcode first
         - Ensure you have a valid unique Bundle ID
         - Register your device with your Apple Developer Account
         - Let Xcode automatically provision a profile for your app
  4- Build or run your project again

For more information, please visit:
  https://flutter.dev/docs/get-started/install/macos#deploy-to-ios-devices

Or run on an iOS simulator without code signing
════════════════════════════════════════════════════════════════════════════════
Encountered error while building for device.

When I opened XCode again, the field Team was indeed marked in red. I opened the dropdown and picked my name and tried again. I compiled the app with flutter build ios --release --no-codesign, then ran Fastlane to publish: FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=[redacted] fastlane ios beta.

This time the error was:

error: exportArchive: No signing certificate "iOS Distribution" found

When I check in XCode, I can see that the provisioning profile it generated for me does not indeed include an iOS Distribution certificate.

The automatically generated provisioning profile includes only a Development certificate. No Distribution certificate.

I went over to https://developer.apple.com/account/resources/profiles/list to try and find that Provisioning profile. I thought I could perhaps open it, and manually add a Distribution certificate to it. Unfortunately, the page only contained the expired Provisioning profiles I had created in the past.

I tried to search for "Automatically manage signing" and "Distribution certificate". I ended up on posts that were several years old, and had no luck until I stumbled upon https://docs.flutter.dev/deployment/ios. Their first instruction is to:

Run flutter build ipa to produce a build archive.

When I ran this command, it automatically produced a build specifically for Deployment:

% flutter build ipa
Archiving com.wafrat.flashcards...
Automatically signing iOS for device deployment using specified development team
in Xcode project: [redacted]
Running Xcode build...                                                  
 └─Compiling, linking and signing...                        14.9s
Xcode archive done.                                         311.8s
Built [project path]/build/ios/archive/Runner.xcarchive.

Building without sound null safety
For more information see https://dart.dev/null-safety/unsound-null-safety

Building App Store IPA...                                          80.5s
Built IPA to [project folder]/build/ios/ipa.
To upload to the App Store either:
    1. Drag and drop the "build/ios/ipa/*.ipa" bundle into the Apple Transport macOS app
    https://apps.apple.com/us/app/transporter/[redacted]
    2. Run "xcrun altool --upload-app --type ios -f build/ios/ipa/*.ipa --apiKey your_api_key --apiIssuer your_issuer_id".
       See "man altool" for details about how to authenticate with the App Store Connect API key.

This doc also introduces two ways to upload to the App Store. By going onto some Apple webapp, or running a commandline tool. I'd never heard of xcrun before!

After this compiled correctly, I simply re-ran the Fastlane deploy command, and this time it uploaded fine.

[21:17:44]: Successfully exported and compressed dSYM file
[21:17:44]: Successfully exported and signed the ipa file:
[21:17:44]: [project path]/ios/Runner.ipa
[21:17:44]: ----------------------------------
[21:17:44]: --- Step: upload_to_testflight ---
[21:17:44]: ----------------------------------
[21:17:44]: Login to App Store Connect
...
[21:19:59]: Going to upload updated app to App Store Connect
[21:19:59]: This might take a few minutes. Please don't interrupt the script.
[21:21:38]: iTunes Transporter successfully finished its job
[21:21:39]: ------------------------------------------------------------------------------------------------------------------
[21:21:39]: --- Successfully uploaded package to App Store Connect. It might take a few minutes until it's visible online. ---
[21:21:39]: ------------------------------------------------------------------------------------------------------------------
[21:21:39]: Successfully uploaded the new binary to App Store Connect

Even when I come back to XCode and check the Provisioning profile, there's still only one certificate. So I am not entirely sure how this all worked, especially since my Fastlane script seems to recompile the project:

default_platform(:ios)

platform :ios do
  desc "Push a new beta build to TestFlight"
  lane :beta do
    build_app(workspace: "Runner.xcworkspace", scheme: "Runner")
    upload_to_testflight(
      skip_waiting_for_build_processing: true
    )
  end
end

So how did the flutter build ipa have an effect on it? No clue.