Shared SwiftUI App Workflow
End-to-end Xcode workflow for architecting, debugging, profiling, and shipping a shared SwiftUI app on iOS and macOS.
What this skill does
# Instructions
- **Overview:** This workflow outlines best practices to **build, test,
and deploy** a SwiftUI app targeting both iOS and macOS (with an
option for Catalyst). It covers project setup, architecture choices,
development practices, and continuous delivery steps.
- **Prerequisites:** Ensure you have the latest Xcode installed (Xcode
15 or newer) and are enrolled in the Apple Developer Program (required
for code signing, Xcode Cloud, and TestFlight). Familiarity with
SwiftUI, Git source control, and basic iOS/macOS app development is
assumed.
- **Usage:** Follow the steps below in sequence to configure a
multi-platform Xcode project, manage dependencies, implement an
architecture (MVVM or TCA), debug and profile efficiently, set up
CI/CD pipelines, and finally distribute the app via TestFlight.
- **Conventions:** This guide uses **bold titles** for key actions and
*italics* for tool names or concepts. Replace example placeholders
(like bundle identifiers or scheme names) with your own
project-specific values when applying these steps.
# Workflow
1. **Create a Multiplatform Xcode Project:** Begin by creating a new
Xcode project that supports both iOS and macOS targets. Xcode 14+
offers a **Multiplatform App** template, which sets up a single
target capable of building for iOS (and iPadOS) and macOS using
SwiftUI. This unified target shares most code and assets across
platforms. If you prefer separate targets, you can instead create an
iOS app and then add a macOS target (or enable Mac Catalyst for the
iOS target). Ensure that shared code (like SwiftUI views and models)
is grouped in a cross-platform group, and use platform checks
(`#if os(iOS)`, `#if os(macOS)`) for any platform-specific code. By
structuring the project as a **shared codebase**, you minimize
duplication while still tailoring the UI where necessary for each
device type.
2. **Set Up Targets, Schemes, and Configurations:** With a
multi-platform project, Xcode may already include separate
configurations for Debug and Release. You might add custom **Build
Configurations** (e.g. Staging or QA) if needed. If using separate
targets (for Catalyst or environment flavors), give each target a
unique **Bundle Identifier** and Info.plist. For example, suffix the
bundle ID with \".mac\" for a macOS target or \".dev\" for a
development build. Create corresponding **Schemes** for each app
target or environment so you can easily run and archive each
version. Mark schemes as "Shared" to include them in source control
(important for team use and CI). This multi-target setup allows you
to, for instance, have an iOS app, a native macOS app, and even a
Catalyst app all in one project, each with its own scheme and bundle
ID.
3. **Configure Environment Settings:** Manage environment-specific
settings by using Xcode's build configuration options. For example,
you can define custom **XCConfig** files or use **User-Defined Build
Settings** for values like API endpoints or feature flags. Define
keys in Info.plist that reference these settings. For instance, add
a key for `BaseURL` in your Info.plist and assign it a value like
`$(BASE_URL)` which is set per configuration. In Build Settings,
create a user-defined variable `BASE_URL` for each configuration
(e.g. Dev, QA, Prod), each pointing to the appropriate URL. Your app
can read these at runtime, for example:
<!-- -->
let apiURL = Bundle.main.object(forInfoDictionaryKey: "BaseURL") as? String
This way, you avoid hardcoding environment values. Additionally,
consider using **Compiler Flags** for conditional code. In each
configuration's build settings, you might add Swift flags like
`-DDEVELOPMENT` or `-DPRODUCTION`. Then in Swift code, use
`#if DEVELOPMENT` to include debug-only logic or use placeholders for
testing. This approach keeps configuration differences isolated at build
time. Finally, ensure each app target uses distinct app icons and names
if needed (e.g. add suffix "Dev" to the app name for a development
build) -- you can set this via Info.plist or Asset catalogs per target.
1. **Manage Dependencies (SPM and CocoaPods):** Use **Swift Package
Manager (SPM)** as the primary tool for adding libraries and
frameworks. SPM is built into Xcode, making dependency management
seamless for SwiftUI projects. To add a package, go to **File ▸ Add
Packages\...** and enter the package Git URL. Target the dependency
to your app target (and not to any CocoaPods-generated target) so
the package integrates correctly. SPM automatically fetches and
updates packages and keeps them sandboxed within Xcode. Commit the
`Package.resolved` file so that team members and CI use the same
versions. If you need a library that isn't available via SPM (or
contains significant Objective-C/legacy code), you can integrate
**CocoaPods**. Initialize a Podfile (`pod init`) and specify pods,
then run `pod install` to generate an `.xcworkspace`. Continue
working from the workspace thereafter. It's possible to mix SPM and
CocoaPods in one project -- just ensure that when adding SPM
packages you select your main project in the add dialog (not the
Pods project). Keep your Pod dependencies updated with `pod update`
as needed. In general, prefer SPM for pure Swift dependencies due to
its native Xcode support and ease of use, using CocoaPods only for
exceptions. Maintain clear documentation of third-party packages in
your README.
2. **Apply an Architecture Pattern (MVVM or TCA):** Structure your
SwiftUI code using a robust architecture to manage complexity. A
popular choice is **MVVM (Model-View-ViewModel)**, which works
naturally with SwiftUI's data binding. In MVVM, define your data
models to represent app data, use SwiftUI Views for the UI, and
create **ViewModel** classes (conforming to `ObservableObject`) to
handle business logic and state. For example, a `GameViewModel`
might publish a `@Published var score` and handle methods to update
the score. The corresponding `GameView` uses `@StateObject` or
`@ObservedObject` to watch the ViewModel and update the UI. This
separation keeps the SwiftUI view declarative and lightweight, while
logic lives in the ViewModel (making it easier to test). For larger
apps or more complex state management, consider adopting **The
Composable Architecture (TCA)**. TCA is a library (addable via SPM)
that follows a unidirectional data flow (inspired by Redux). You
break down your app into **State**, **Actions**, and **Reducers**. A
Reducer is a pure function that takes the current State and an
Action and produces a new State (and optionally, side effects known
as Effects). A **Store** connects your SwiftUI View to the state and
business logic: the View sends Actions (for example, button taps),
which the Store receives and feeds into the Reducer, updating State
which then flows back to the View. TCA encourages a very modular
structure: you can compose small features into larger ones, and it
provides tools to manage dependencies and side effects in a
controlled way. While TCA has a learning curve, it excels in
testability (you can easily write tests for reducer logic) and
scalability for big apps. Choose either MVVM (simpler, uses
SwiftUI's built-in reactive state features) or TCA (more structured,
ideal for complex apps) depending on project needs -- both will help
maintain a clear separation of concerns in your code.
3. **Debugging and Profiling Practices:** During development, use
Xcode's robust debugging tools to catch and fix issues early. Set
**breakpoints** in your code (by clicking the gutter next to a line
number) to pause execution and inspect variables at runtime. Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.