Swift Packages and Modularizing an App
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Xcode Projects, Targets, Schemes and Previews described the
Xcode side of a project: workspaces, projects, targets and schemes. This page covers the other half of how an Apple
app is put together: Swift packages. You use them to pull in other people’s code, and, just as importantly, to
split your own app into modules with clear boundaries. The examples target the 2026 baseline used throughout this
section: iOS, iPadOS, macOS, watchOS and visionOS 27, built with Xcode 27 and Swift 6.4, with a deployment minimum
of 26 on every platform. The running example is FieldNotes, whose code is split into two local packages,
FieldNotesKit and FieldNotesUI, next to the FieldNotes.xcodeproj project.
The Swift Package Manager itself, meaning the swift build, swift test and swift package commands, the manifest
format in general, version rules and publishing a package, is documented in the Swift reference on
Swift Package Manager. The toolchain behind it is covered
on Build and Tooling. This page does not repeat that
material. It focuses on how an app uses packages inside Xcode, and on the decisions that are specific to Apple
platforms.
Why Modularize an App at All
A new Xcode app template puts every source file into a single app target. That works for a prototype, but as an app grows, one target turns into one large Swift module where every type can see every other type. Build times suffer too: a change to a model file forces the compiler to reconsider the whole module. Moving code into Swift packages gives you:
-
Enforced boundaries. A package target is a separate Swift module. Code in
FieldNotesUIcan only use whatFieldNotesModelmarkspublic(orpackage, see below), so accidental coupling becomes a compile error instead of a code-review comment. The access levels are explained in the Swift reference on Access Control. -
Reuse across targets. FieldNotes has an iOS/iPadOS/macOS/visionOS app, a watchOS app and, later, a widget extension. All three need
Note,NotebookandNoteStore. A library product links into each of them without copying files between targets or managing target membership by hand. -
Faster, more parallel builds. Independent modules build in parallel, and a change inside
FieldNotesServicesdoes not recompileFieldNotesModel. -
Isolated tests. A package’s test target builds and runs without launching the app, with
swift teston the command line or from Xcode’s Test navigator. Unit Testing with Swift Testing builds on this. -
Faster previews. Xcode previews for a view inside
FieldNotesUIonly need that module and its dependencies, not the whole app.
Apple’s recommended workflow is to start with local packages that live in the app’s repository, and to move a package into its own repository only once another app needs it.
The FieldNotes Module Graph
FieldNotes is split into three layers. The app targets at the top contain only the App entry point, platform
glue and a few platform-specific screens. The feature layer, FieldNotesUI, holds the shared SwiftUI views and
styles. The core layer, FieldNotesKit, holds the model types and services and has no dependency on SwiftUI at all.
Remote packages sit at the bottom, and only the layer that needs a remote package depends on it.
Figure: FieldNotes targets, local packages and remote packages
iOS, iPadOS, macOS, visionOS"] FNW["FieldNotesWatch
watchOS"] FNX["FieldNotesWidgets
widget extension"] end subgraph Feature["Packages/FieldNotesUI (feature layer)"] UI["FieldNotesUI
shared views and styles"] end subgraph Core["Packages/FieldNotesKit (core layer)"] SVC["FieldNotesServices
NoteStore, NoteSyncing"] MOD["FieldNotesModel
Note, Notebook"] end subgraph Remote["Remote packages"] COL["swift-collections"] ASY["swift-async-algorithms"] end FN --> UI FNW --> UI FNX --> UI FN --> SVC FNW --> SVC FNX --> MOD UI --> SVC UI --> MOD SVC --> MOD SVC --> ASY MOD --> COL
The rule the graph encodes is simple: arrows only point downwards. FieldNotesModel knows nothing about services,
services know nothing about views, and nothing in a package knows about the app targets. If a lower layer ever needs
something from a higher one, that is a sign the code belongs in a protocol at the lower level, such as
NoteSyncing, with the concrete implementation injected from above
(App Architecture the Apple Way shows this injection).
On disk, the repository looks like this:
FieldNotes/
FieldNotes.xcodeproj
FieldNotes/ # app target: FieldNotesApp.swift, platform glue, Assets.xcassets
FieldNotesWatch/ # watchOS app target
Packages/
FieldNotesKit/
Package.swift
Sources/
FieldNotesModel/ # Note, Notebook
FieldNotesServices/ # NoteStore, NoteSyncing, SummaryGenerating
Resources/ # Localizable.xcstrings, SampleNotes.json
Tests/
FieldNotesKitTests/
FieldNotesUI/
Package.swift
Sources/FieldNotesUI/ # NoteRow, TagChip, CaptureButton, styles
Resources/ # Media.xcassets, Localizable.xcstrings
Adding a Remote Package Dependency
A remote package is one Xcode fetches from a Git repository (or a package registry) by URL. To add one to an app target, choose File > Add Package Dependencies…, paste the repository URL or search your connected GitHub, GitHub Enterprise or GitLab accounts, choose a dependency rule, and pick which of the package’s library products to link into which targets. The rules are:
| Rule | When to use it |
|---|---|
Up to Next Major Version (default) |
The recommended choice. Accepts every minor and patch release under semantic versioning, so bug fixes arrive automatically, but a new major version that may break the API does not. |
Up to Next Minor Version |
More conservative: accepts only patch releases. Useful for a dependency whose minor releases have surprised you. |
Exact Version or Range |
Pins one version, or a hand-written range. Use sparingly: you stop getting fixes. |
Branch |
Tracks a branch. Only for packages you develop in tandem with the app and don’t publish versions of yet. |
Commit |
Pins a commit hash. Apple discourages this except in exceptional cases. |
Xcode records the exact revision it resolved for every dependency in Package.resolved, stored at
FieldNotes.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved. Commit this file, so that every
developer and every CI machine builds the same versions. File > Packages > Update to Latest Package Versions
re-resolves the file within the rules you chose, and Resolve Package Versions restores exactly what it records.
Once a product is linked, you use it with a normal import. FieldNotes adds Apple’s swift-collections package
so that the model can keep a note’s tags ordered and unique:
// Packages/FieldNotesKit/Sources/FieldNotesModel/TagSet.swift
import OrderedCollections
extension Note {
/// Tags in the order the user added them, without duplicates.
public var orderedTags: OrderedSet<String> {
OrderedSet(tags.map { $0.lowercased() })
}
}
Only add packages from authors you trust. A package’s code runs inside your app with all of its entitlements, and package plugins can run code on your Mac at build time, which is why Xcode asks you to trust a plugin before its first run. For binary dependencies, Xcode also shows who signed the XCFramework, as described below.
Creating the Local Packages
A local package is a package folder inside the app’s repository that the Xcode project references by path instead
of by URL. To create one, choose File > New > Package…, pick the Multiplatform > Library template, save it into
Packages/, choose the FieldNotes project and group, and leave Create Git repository unchecked, because the app
repository already tracks it. Then, in the app target’s General pane, click + under Frameworks, Libraries, and
Embedded Content and add the package’s library product. A local package appears in the Project navigator as an
editable folder, and changes to it rebuild the app immediately. There is no version to publish or resolve.
The same steps also let you override a remote dependency with a local checkout: drag a local clone of the package into the project, and Xcode uses it in place of the remote version with the same identity until you remove it. This is the standard way to debug or patch a dependency.
The FieldNotesKit Manifest
Every package is described by Package.swift, its manifest: a Swift file that builds a Package value with the
PackageDescription library. This is the complete manifest for FieldNotes' core package:
// swift-tools-version: 6.4
// Packages/FieldNotesKit/Package.swift
import PackageDescription
let package = Package(
name: "FieldNotesKit",
defaultLocalization: "en",
platforms: [
.iOS(.v26),
.macOS(.v26),
.watchOS(.v26),
.visionOS(.v26),
],
products: [
.library(name: "FieldNotesModel", targets: ["FieldNotesModel"]),
.library(name: "FieldNotesServices", targets: ["FieldNotesServices"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-collections", from: "1.1.0"),
.package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.0"),
],
targets: [
.target(
name: "FieldNotesModel",
dependencies: [
.product(name: "OrderedCollections", package: "swift-collections"),
]
),
.target(
name: "FieldNotesServices",
dependencies: [
"FieldNotesModel",
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
],
resources: [
.process("Resources"),
]
),
.testTarget(
name: "FieldNotesKitTests",
dependencies: ["FieldNotesModel", "FieldNotesServices"]
),
],
swiftLanguageModes: [.v6]
)
Walking through it from the top:
-
// swift-tools-version: 6.4. The first line is not a comment in the usual sense. It declares the minimum Swift tools version that can read the manifest, and it decides whichPackageDescriptionAPIs the manifest may use. The.v26platform versions, for example, need tools version 6.2 or later. -
defaultLocalization. The development language of the package’s resources. It is required as soon as a target contains localized resources, covered below. -
platforms. The minimum deployment version for each platform. FieldNotes' deployment minimum is 26 everywhere, so every package it uses declares.v26. A package whose minimum is higher than the app’s deployment target fails to link into it. Platforms that aren’t listed still build, using SwiftPM’s oldest supported default, so listing each one you support makes the intent explicit. APIs that are new in 27 still need anif #available(iOS 27, macOS 27, watchOS 27, visionOS 27, *)check inside the package, exactly as in the app. -
products. What the package makes visible to its clients. A target that is not in any product is private to the package. FieldNotes exposes the model and the services as two separate libraries, so the widget extension can link onlyFieldNotesModel. -
dependencies. Other packages this package needs, with a version requirement (from:means "up to next major"). -
targets. Each.targetis one Swift module, with sources inSources/<name>/by convention. A target lists its dependencies either as a sibling target name ("FieldNotesModel") or as a product of a dependency package (.product(name:package:)). A.testTargetholds tests inTests/<name>/. -
swiftLanguageModes: [.v6]. Compiles every target in the Swift 6 language mode, with complete data-race safety checking. This replaced the olderswiftLanguageVersionsparameter. What Swift 6 mode checks is explained in Actors, Isolation and Sendable.
The model and services modules deliberately do not import SwiftUI. NoteStore is annotated @MainActor in its
declaration, as shown in the canonical FieldNotes code, instead of relying on a package-wide default. The
FieldNotesUI package, whose code is almost all views, is the one place where a module-wide default of main-actor
isolation makes sense. You set it with the defaultIsolation Swift setting, the package equivalent of the
SWIFT_DEFAULT_ACTOR_ISOLATION build setting that new Xcode app targets turn on:
// swift-tools-version: 6.4
// Packages/FieldNotesUI/Package.swift
import PackageDescription
let package = Package(
name: "FieldNotesUI",
defaultLocalization: "en",
platforms: [.iOS(.v26), .macOS(.v26), .watchOS(.v26), .visionOS(.v26)],
products: [
.library(name: "FieldNotesUI", targets: ["FieldNotesUI"]),
],
dependencies: [
.package(path: "../FieldNotesKit"),
],
targets: [
.target(
name: "FieldNotesUI",
dependencies: [
.product(name: "FieldNotesModel", package: "FieldNotesKit"),
.product(name: "FieldNotesServices", package: "FieldNotesKit"),
],
resources: [.process("Resources")],
swiftSettings: [
.defaultIsolation(MainActor.self),
]
),
],
swiftLanguageModes: [.v6]
)
.package(path:) is how one local package depends on another: no URL, no version, just a relative path. How
main-actor-by-default interacts with nonisolated code and background work is the subject of
Swift Concurrency in Apps.
Public API and the package access level
Because each target is its own module, the types the app uses must be public. The canonical Note type from the
model module therefore looks like this once it lives in FieldNotesModel:
// Packages/FieldNotesKit/Sources/FieldNotesModel/Note.swift
import Foundation
public struct Note: Identifiable, Hashable, Sendable {
public var id: UUID
public var title: String
public var body: String
public var createdAt: Date
public var notebookID: Notebook.ID?
public var tags: [String]
public var isPinned: Bool
public init(id: UUID = UUID(), title: String, body: String = "", createdAt: Date = .now,
notebookID: Notebook.ID? = nil, tags: [String] = [], isPinned: Bool = false) {
self.id = id; self.title = title; self.body = body; self.createdAt = createdAt
self.notebookID = notebookID; self.tags = tags; self.isPinned = isPinned
}
/// Visible to FieldNotesServices and the package's tests, but not to the app.
package static let maximumTitleLength = 200
}
A public struct’s memberwise initializer is internal, so the explicit public init is required for the app to
create notes. The package access level exposes a declaration to every module in the same package, here
FieldNotesServices and FieldNotesKitTests, without making it part of the public API the app sees. The Swift
reference covers access levels in Access Control.
Swift 6.4 adds module selectors (SE-0491) for the case where two imported modules declare the same name.
If FieldNotes ever imported a third-party module that also declared a Note type, FieldNotesModel::Note would
name the FieldNotes type unambiguously:
// FieldNotes/Import/ImportedNoteMapper.swift
import FieldNotesModel
import ThirdPartyNotesKit // hypothetical module that also declares `Note`
func convert(_ imported: ThirdPartyNotesKit::Note) -> FieldNotesModel::Note {
FieldNotesModel::Note(title: imported.headline, body: imported.text)
}
Resources in Packages
A target can bundle resources: asset catalogs, String Catalogs, JSON sample data, Core Data models, storyboards and
so on. Place them under the target’s source folder, conventionally in a Resources subfolder, and declare them in the
manifest with one of two rules:
-
.process(_:)applies platform-specific processing: asset catalogs are compiled, images may be optimized, String Catalogs become.stringstables. When no processing applies, the file is copied. Passing a directory applies the rule recursively. This is the rule to use by default. -
.copy(_:)copies the file or directory unchanged, keeping its folder structure. Use it when code relies on that exact structure.
Xcode recognizes asset catalogs, .lproj folders, Interface Builder files and Core Data models automatically, even
without a resources: entry. Anything else has to be declared, and a file inside a target folder that is neither
code nor a declared resource must be listed in exclude:.
When a target has resources, the build creates a resource bundle for it and generates an internal Bundle.module
accessor. Code in the package must always load its resources through that bundle, never through Bundle.main,
because in the app the package’s resources live in their own bundle. FieldNotes' services module ships sample notes
for previews and first launch:
// Packages/FieldNotesKit/Sources/FieldNotesServices/SampleData.swift
import Foundation
import FieldNotesModel
public enum SampleData {
/// Notes shown on first launch and in Xcode previews.
public static func notes() throws -> [Note] {
guard let url = Bundle.module.url(forResource: "SampleNotes", withExtension: "json") else {
return []
}
let data = try Data(contentsOf: url)
return try JSONDecoder().decode([SampleNote].self, from: data).map(\.note)
}
}
The decoding types (SampleNote conforms to Decodable) follow
Codable and Serialization. For code that may run in
an app target or a package, the Foundation #bundle macro expands to the right bundle in either context, so a
helper moved from the app into a package keeps working unchanged:
// Packages/FieldNotesUI/Sources/FieldNotesUI/EmptyStateView.swift
import SwiftUI
public struct EmptyStateView: View {
public init() {}
public var body: some View {
ContentUnavailableView(
String(localized: "No notes yet", bundle: #bundle,
comment: "Title shown when a notebook is empty"),
systemImage: "note.text",
description: Text("Tap + to capture your first field note.", bundle: #bundle)
)
}
}
Localizing package resources
To localize a package, set defaultLocalization in the manifest (both FieldNotes manifests above use "en") and add
a String Catalog (Localizable.xcstrings) or language-specific .lproj folders to a target’s resources. Xcode
validates the localized resources at build time and warns about missing locales, duplicate resources and
subdirectories inside .lproj folders. Localized text is then looked up in the module’s bundle, which is why the
EmptyStateView above passes bundle: #bundle (equivalently, .module) to both String(localized:) and Text.
Without it, SwiftUI would search the app’s main bundle and fall back to the key.
For an Interface Builder file that uses base internationalization, declare it with an explicit localization:
// In a target's resources: an AppKit settings panel in a hypothetical FieldNotesMacUI target
resources: [
.process("Resources/SyncSettings.xib", localization: .base),
]
String Catalogs, plural variations and the Xcode 27 agent-assisted translation workflow are covered on Localization with String Catalogs.
Binary Targets and XCFrameworks
Most packages ship source, but a vendor SDK is often available only as a compiled XCFramework: a bundle with one binary slice per platform and architecture. A package wraps it in a binary target, either from a local path or from a remote zip archive with a SHA-256 checksum. FieldNotes might use a (hypothetical) offline map-tiles SDK for the visionOS map space this way:
// In Packages/FieldNotesKit/Package.swift
targets: [
.binaryTarget(
name: "MapTilesSDK",
url: "https://downloads.example.com/maptiles/2.3.0/MapTilesSDK.xcframework.zip",
checksum: "6f1d3c1b0a7e5e2d9c8b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d"
),
.binaryTarget(
name: "LegacyGeocoder",
path: "Frameworks/LegacyGeocoder.xcframework"
),
]
The vendor publishes the checksum next to the download. You can compute it yourself from the zip file:
swift package compute-checksum MapTilesSDK.xcframework.zip
Binary targets work only on Apple platforms. They also have real costs compared with source: you can’t read, step
into or patch the code, the binary must already contain slices for every platform FieldNotes supports (including
visionOS and watchOS), and a binary built against an older SDK can hold the whole app back. Xcode helps you check
where a binary came from: it shows whether an XCFramework is signed and by whom, and it records the checksum in
Package.resolved, so a changed binary fails resolution instead of silently shipping.
Explicitly Built Modules
Before any Swift file in FieldNotesUI can compile, the compiler needs the modules it imports: SwiftUI,
FieldNotesModel, Foundation, and all the Clang modules below them. Historically, each compiler invocation
discovered and built those modules implicitly, as it went, hidden inside a single compile task. Since Xcode 16,
Xcode builds them explicitly. The build is divided into three visible stages:
-
Dependency scanning. The compiler scans sources to find every module they import.
-
Module compilation. The build system compiles those modules in dependency order, building unrelated modules in parallel, and shows each one as its own task in the build log and the build timeline.
-
Source compilation. Your source files compile against the modules that were just built.
This scheduling makes builds more parallel, makes errors inside a dependency easier to read, and makes debugging more reliable, because LLDB loads exactly the modules the compiler used. Swift 6.4 completes this work in the debug info, which now records the precise module dependencies instead of looking modules up by name. That fixes a class of "couldn’t load module" failures in the debugger and makes dSYM bundles smaller.
Explicit modules are on by default for Swift targets using the default C and Objective-C interoperability mode.
Targets using Swift’s C++ interoperability don’t support them. The build setting is SWIFT_ENABLE_EXPLICIT_MODULES,
which you can set in FieldNotes' shared .xcconfig:
// FieldNotes/Config/Shared.xcconfig
SWIFT_ENABLE_EXPLICIT_MODULES = YES
CLANG_ENABLE_MODULES = YES
The main way to lose time here is building module variants. If two targets import the same module with different
compiler options, for example different Preprocessor Macros values, the module is built twice. Product > Perform
Action > Build With Timing Summary lists every module that was built and the option differences that forced extra
variants. The fix is to move such settings into one shared .xcconfig so that every FieldNotes target compiles with
the same options.
Xcode 27 also tightens module naming. Its faster dependency scanner requires every Clang module reachable from one
Swift scan to have a unique name. A vendored library that ships its own module.modulemap redeclaring an SDK
module, or the same module name reachable from two header search paths, now causes a scan error instead of being
tolerated.
Mergeable Libraries
Modularizing an app raises a linking question. Static libraries make launch fast but are relinked into every binary that uses them, which slows incremental builds and duplicates code between an app and its extensions. Dynamic frameworks keep builds fast but add dynamic-loader work at every launch. Mergeable libraries, introduced in Xcode 15, give you both behaviours:
-
In debug builds, a mergeable library is linked as an ordinary dynamic library and re-exported, so incremental builds stay fast.
-
In release builds, Xcode adds merge metadata to the library and merges it into the app binary, much like linking a static library, so there is no dynamic loading cost at launch.
You opt in with two build settings: MERGED_BINARY_TYPE on the target that merges (the app), and, for manual
control, MERGEABLE_LIBRARY on each library to merge:
// FieldNotes/Config/FieldNotes.xcconfig (app target)
// automatic: every direct dependency built by this project becomes mergeable
// and is merged into the app binary in release builds.
MERGED_BINARY_TYPE = automatic
// FieldNotesWidgets/Config/FieldNotesWidgets.xcconfig (widget extension)
// manual: only direct dependencies with MERGEABLE_LIBRARY = YES are merged.
MERGED_BINARY_TYPE = manual
automatic merges direct dependencies: libraries in the target’s Link Binary With Libraries phase that another
target in the same project produces, plus any mergeable pre-built XCFrameworks listed there. Indirect dependencies
and pre-built libraries are not merged unless you configure them manually. A package can opt its library products
into this scheme by declaring them dynamic, for example .library(name: "FieldNotesUI", type: .dynamic,
targets: ["FieldNotesUI"]). Without an explicit type, SwiftPM lets Xcode choose, which in practice means
static linking into each client. For FieldNotes, static linking of the package products is the default and is fine.
Mergeable libraries become worthwhile once the app and its widget extension share a large amount of code and you
measure launch time on the App Launch template in Instruments
(Performance and Instruments).
A vendor can also ship a pre-built mergeable library: enable Build Mergeable Library on the library target, then
build an XCFramework from its archive. Xcode records a MergeableMetadata key in the XCFramework’s Info.plist, and
only Xcode 15 or later can use such an XCFramework.
Swift Build: the Default SwiftPM Engine in 6.4
Xcode has always built packages with its own build engine, and SwiftPM’s command-line tools used to have a separate
one. Apple open-sourced the Xcode build engine as Swift Build in 2025, and in Swift 6.4 it becomes the default
build system of swift build, swift test and swift run. In practice, a package now builds the same way in
Xcode 27, on the command line on macOS, and on Linux and Windows CI runners. Differences that used to surface only
in one of them, such as resource processing or how test discovery works, largely disappear. Swift Build also brings
Android support to SwiftPM without a post-install script.
Most packages need no changes. If a package fails under the new engine, --build-system selects the engine
explicitly, which is useful when bisecting a regression:
cd Packages/FieldNotesKit
# Swift 6.4 default: the Swift Build engine
swift build
swift test
# The previous SwiftPM-native engine, to compare behaviour
swift build --build-system native
Swift 6.4’s SwiftPM also generates a Software Bill of Materials (SE-0509) in SPDX or CycloneDX format, which is
useful when an enterprise customer asks what third-party code FieldNotes ships. The Xcode 27 release notes add two
swift test improvements that help package test targets: a summary of every failing test target at the end of the
run, and repeating tests with --maximum-repetitions and --repeat-until:
# Hunt a flaky sync test: repeat until it fails, at most 50 times.
swift test --filter NoteStoreSyncTests --maximum-repetitions 50 --repeat-until fail
Building packages and apps in CI, including xcodebuild -resolvePackageDependencies and caching the package cache
between runs, is covered on CI/CD with Xcode Cloud and
the Command Line.
Practical Guidelines
-
Start local, stay small. Create one core package first (FieldNotes started with
FieldNotesKit) and split further only when a boundary is obvious. A module per screen is usually too fine-grained: every module boundary addspublicdeclarations and an initializer to maintain. -
Keep the core free of UI frameworks.
FieldNotesModelandFieldNotesServicesimport Foundation and Observation, not SwiftUI, so they compile for every target, including a widget extension or a command-line tool. -
Declare platforms in every package, matching the app’s deployment minimum of 26.
-
Pin behaviour, not revisions. Use "Up to Next Major" rules and commit
Package.resolved, instead of commit-pinned dependencies. -
Prefer source packages to binary targets unless the vendor offers nothing else, and check the signature of any XCFramework.
-
Keep compiler options uniform across targets, to avoid building duplicate module variants.
-
Test at the package level.
swift testinPackages/FieldNotesKitruns in seconds without a simulator.
References
-
Apple Developer Documentation — Adding package dependencies to your app
-
Apple Developer Documentation — Organizing your code with local packages
-
Apple Developer Documentation — Creating a standalone Swift package with Xcode
-
Apple Developer Documentation — Bundling resources with a Swift package
-
Apple Developer Documentation — Localizing package resources
-
Apple Developer Documentation — Identifying binary dependencies
-
Apple Developer Documentation — Building your project with explicit module dependencies
-
Apple Developer Documentation — Configuring your project to use mergeable libraries
-
Apple Developer Documentation — binaryTarget(name:url:checksum:)