Mobile Development 18 min read

Swift Package Manager (SwiftPM) Practical Guide: Structure, Resources, Linking, and Best Practices

This article provides a detailed, step‑by‑step guide on using Swift Package Manager (SwiftPM) in iOS projects, covering package creation, directory layout, single‑ and multi‑target designs, dependency handling, Obj‑C/Swift mixing, resource management, static vs dynamic linking, compilation options, ARC disabling, macro definitions, linker settings, common pitfalls, and best‑practice recommendations.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Swift Package Manager (SwiftPM) Practical Guide: Structure, Resources, Linking, and Best Practices

Swift Package Manager (SwiftPM) is Apple’s official tool for managing source code distribution, integrating with the Swift build system to automatically download, compile, and link dependencies for Swift, Objective‑C, C, and C++ projects on macOS and Linux.

The FoxFriend iOS team replaced CocoaPods with SwiftPM and shares their practical experience across five areas: structure design, resource handling, linking mode selection, compilation/link parameters, and exception handling.

Structure Design

Organize modules by component nature (base vs business) and dependency direction (generic vs specific). Prefer a unified Swift codebase; if Obj‑C code must remain, either convert it to Swift or isolate it in a separate package to avoid circular dependencies.

Two target layout modes are recommended:

Single‑Target Mode : SwiftPM creates one target directory (e.g., Sources/MyPackage ) that matches the target name.

Multi‑Target Mode : Create parallel directories under Sources (e.g., TargetA , TargetB ) and declare each in Package.swift , optionally specifying path when names differ.

let package = Package(
    name: "MyPackage",
    products: [.library(name: "MyPackage", targets: ["MyPackage"])],
    targets: [.target(name: "MyPackage", dependencies: [])]
)

Resource Handling

SwiftPM supports three resource processing modes:

.process : Flattens the directory hierarchy into the bundle root (ideal for images in Asset Catalog).

.copy : Preserves the original directory structure (useful for JSON, plist, or multiple theme assets).

Direct inclusion of files placed at the package root.

Example target configuration:

.target(
    name: "SpmResourceTest",
    dependencies: [],
    resources: [
        .copy("HYContentShare.bundle"),
        .process("images"),
        .copy("json")
    ]
)

Reading resources can be done via Bundle.module for processed assets or by constructing a sub‑bundle URL for copied files.

Linking Mode Selection

Static linking reduces app binary size and launch time, while dynamic linking speeds up incremental builds during development. SwiftPM’s Product.Library.LibraryType can be set to .static or .dynamic , or left nil to let the consumer decide.

Compilation & Linking Parameters

SwiftPM can replicate CocoaPods configuration capabilities, such as conditional target dependencies, ARC disabling, and custom compiler flags:

.target(
    name: "ProtobufFiles",
    dependencies: ["Protobuf"],
    cSettings: [
        .define("GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS"),
        .unsafeFlags(["-fno-objc-arc"]) // disable ARC
    ]
)

Linker settings can be added similarly to CocoaPods OTHER_LDFLAGS :

.target(
    name: "MyTarget",
    linkerSettings: [
        .linkedLibrary("c++"),
        .linkedFramework("CoreLocation")
    ]
)

Exception Handling

When integrating non‑module Clang libraries (e.g., older SDKs without a module.modulemap ), a bridging header is required. Persistent build issues may be resolved by cleaning SwiftPM caches:

rm -rf ~/Library/Caches/org.swift.swiftpm
rm -rf ~/Library/org.swift.swiftpm

Best Practices Summary

Keep dependency graphs acyclic; refactor if cycles appear.

Choose single‑ or multi‑target layouts based on code size and reuse.

Prefer .static libraries for release builds; allow dynamic linking for rapid iteration.

Use Asset Catalog for images, .process for simple resources, and .copy when directory hierarchy matters.

Leverage SwiftPM’s conditional target dependencies to include debug‑only helpers.

References

https://github.com/apple/swift-evolution/blob/master/proposals/0273-swiftpm-conditional-target-dependencies.md#proposed-solution

https://useyourloaf.com/blog/add-resources-to-swift-packages/

https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package

iOSbest practicesresourceslinkingPackage ManagementSwiftPM
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.