HomeiOS DevelopmentThe Swift bundle manifest file

The Swift bundle manifest file


This text is a whole Swift Bundle Supervisor cheatsheet for the bundle manifest file, utilizing the newest Swift 5.2 instruments model.

Swift


If you wish to be taught use the Swift Bundle Supervisor it is best to learn my different article, as a result of that’s extra like an introduction for individuals who have by no means labored with SPM but.


Bundle sorts

There are a number of bundle sorts which you could create with the swift bundle init command. You’ll be able to specify the --type flag with the next values: empty, library, executable, system-module, manifest. It’s also possible to outline a customized bundle identify by the --name flag.

  • The empty bundle will create the default file construction with out the pattern code recordsdata.
  • The library sort will create a reusable library product template.
  • The executable sort will create a Swift software with an executable product definition within the bundle and a fundamental.swift file as a place to begin.
  • The system-module sort will create a wrapper round a system offered bundle, akin to libxml, we’ll speak about this in a while.
  • The manifest sort will solely create a Bundle.swift file with out anything.





The Bundle manifest file

Each single SPM challenge has this particular file within it known as Bundle.swift. I already wrote a put up about how the bundle supervisor and the Swift toolchain works behind the scenes, this time we will focus solely on the manifest file itself. Let’s get began. 📦

Each single Bundle.swift file begins with a particular remark line the place it’s a must to outline the model of the used Swift instruments. The most recent model is sort of completely different from the older ones.



Subsequent it's a must to import the PackageDescription framework so as to outline your Swift bundle. This framework incorporates the bundle manifest construction as Swift objects.


import PackageDescription

That is it now you might be prepared to explain the bundle itself. Oh by the way in which you possibly can change the model of the used instruments, you possibly can learn extra about this within the Bundle Supervisor utilization readme.





Bundle

A bundle is only a bunch of Swift (or different) recordsdata. The manifest file is the outline of what and construct from these sources. Each single bundle ought to have a reputation, however this isn't enought to truly generate one thing from it. You'll be able to solely have precisely one bundle definition contained in the file. That is the shortest and most ineffective one which you could create. 🙈


let bundle = Bundle(identify: "myPackage")


The bundle identify goes for use when you find yourself importing packages as dependencies, so identify your pacages fastidiously. If you happen to select a reserved identify by a system framework there could be points with linking. If there is a battle it's a must to use static linking as an alternative of dynamic. If you happen to generate a challenge by way of the swift bundle generate-xcodeproj command that challenge will attempt to hyperlink every little thing dynamically, however if you happen to open the Bundle.swift file utilizing Xcode 11, the dependencies will probably be linked statically if this was not set explicitly within the product definition part.




Platform

A platform is mainly an working system with a given model which you could help.


let bundle = Bundle(
    identify: "myPackage",
    platforms: [
        .iOS(.v13),         
        .macOS(.v10_15),    
        .tvOS(.v13),        
        .watchOS(.v6),      
    ]
)


While you add a platform you might be placing a constraint on it by way of the required model. Each single dependency ought to match the requirement of the primary bundle platforms. Lengthy story quick if you'll want to add help for Apple platforms, it is best to specify a platform flag with a supported model, in any other case SPM will use the oldest deployment goal based mostly on the put in SDK, apart from macOS, that is going to be v10_10. Each bundle has Linux help by default, you possibly can't add such restrictions but, however possibly this can change within the close to future, additionally Home windows is coming.





Product

A bundle can have a number of last merchandise (construct artifacts). At present there are two kinds of construct merchandise: executables and libraries. The executable is a binary that may be executed, for instance this generally is a command line software. A library is one thing that others can use, it's mainly the general public API product illustration in your targets.



import PackageDescription

let bundle = Bundle(identify: "myPackage", merchandise: [
    .library(name: "myPackageLib", targets: ["myPackageLib"]),
    .library(identify: "myPackageStaticLib", sort: .static, targets: ["myPackageLib"]),
    .library(identify: "myPackageDynLib", sort: .dynamic, targets: ["myPackageLib"]),
    .executable(identify: "myPackageCli", targets: ["myPackage"])
], targets: [
    .target(name: "myPackageLib"),
    .target(name: "myPackageCli"),
])


If the library sort is unspecified, the Bundle Supervisor will robotically select it based mostly on the shopper's desire. As I discussed this earlier generated Xcode initiatives want dynamic linking, however if you happen to merely open the manifest file the app will probably be statically linked.





Dependency

Packages can depend on different packages. You'll be able to outline your dependencies by specifying a neighborhood path or a repository url with a given model tag. Including a dependency into this part isn't sufficient to make use of it in your targets. You even have so as to add the product offered by the bundle on the goal degree.

let bundle = Bundle(
    identify: "myPackage",
    dependencies: [
        .package(path: "/local/path/to/myOtherPackage"),
        .package(url: "<git-repository-url>", from: "1.0.0"),
        .package(url: "<git-repository-url>", .branch("dev")),
        .package(url: "<git-repository-url>", .exact("1.3.2")),
        .package(url: "<git-repository-url>", .revision("<hash>")),
        .package(url: "<git-repository-url>", .upToNextMajor(from: "1.0.0")),
        .package(url: "<git-repository-url>", .upToNextMinor(from: "1.0.0")),
        .package(url: "<git-repository-url>", "1.0.0"..<"1.3.0"),
    ]
)


The url generally is a GitHub url, fortuitously you possibly can add non-public repositories as properly by utilizing an ssh key based mostly authentication. Simply use the [email protected]:BinaryBirds/viper-kit.git url format, as an alternative of the HTTP based mostly, if you wish to add non-public packages. 🤫




Goal

A goal is one thing which you could construct, in different phrases it is a construct goal that may end up in a library or an executable. You need to have no less than one goal in your challenge file in any other case you possibly can't construct something. A goal ought to all the time have a reputation, each different settings is optionally available.


Settings

There are lots of settings that you should utilize to configure your goal. Targets can rely on different targets or merchandise outlined in exterior packages. A goal can have a customized location, you possibly can specify this by setting the trail attribute. Additionally you possibly can exclude supply recordsdata from the goal or explicitly outline the sources you wish to use. Targets can have their very own public headers path and you may present construct settings each for the C, C++ and the Swift language, and compiler flags.


.goal(identify: "myPackage",
        dependencies: [
            .target(name: "other"),
            .product(name: "package", package: "package-kit")
        ],
        path: "./Sources/myPackage",
        exclude: ["foo.swift"],
        sources: ["main.swift"],
        publicHeadersPath: "./Sources/myPackage/headers",
        cSettings: [
            .define("DEBUG"),
            .define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
            .outline("DEBUG", to: "yes-please", .when(platforms: [.iOS], configuration: .debug)),
            .headerSearchPath(""),
            .headerSearchPath("", .when(platforms: [.android, .linux, .windows], configuration: .launch)),
            .unsafeFlags(["-D EXAMPLE"]),
            .unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
        ],
        cxxSettings: [
            
        ],
        swiftSettings: [
            .define("DEBUG"),
            .define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
            .unsafeFlags(["-D EXAMPLE"]),
            .unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
        ],
        linkerSettings: [
            .linkedFramework("framework"),
            .linkedLibrary("framework", .when(platforms: [.iOS], configuration: .debug)),
            .linkedLibrary("library"),
            .linkedLibrary("library", .when(platforms: [.macOS], configuration: .launch)),
            .unsafeFlags(["-L example"]),
            .unsafeFlags(["-L example"], .when(platforms: [.linux], configuration: .launch)),
        ]),

As you possibly can see you possibly can outline preprocessor macros for each single language. You need to use the secure circumstances for fundamental stuff, however there's an unsafeFlags case for the reckless ones. The good factor is which you could help a platform situation filter together with construct configuration to each single settings because the final param.

Accessible platforms are: .iOS, .macOS, .watchOS, .tvOS, .android, .linux, .home windows
The construct configuration could be .debug or .launch


Take a look at targets

Take a look at targets are used to outline take a look at suites. They can be utilized to unit take a look at different targets utilizing the XCTest framework. They appear to be precisely the identical as common targets.


.testTarget(identify: String,
    dependencies: [Target.Dependency],
    path: String?,
    exclude: [String],
    sources: [String]?,
    cSettings: [CSetting]?,
    cxxSettings: [CXXSetting]?,
    swiftSettings: [SwiftSetting]?,
    linkerSettings: [LinkerSetting]?)


I believe the one distinction between a goal and a take a look at goal is which you could run a take a look at goal utilizing the swift take a look at command, however from a structural viewpoint, they're mainly the identical.




Bundle configs and system libraries

You'll be able to wrap an current system library utilizing Swift, the fantastic thing about that is that you should utilize packages written in C, CPP or different languages. I am going to present you a fast instance by the superb Kanna(鉋) - XML/HTML parser repository. I am utilizing this software rather a lot, thanks for making it Atsushi Kiwaki. 🙏




#if swift(>=5.2) && !os(Linux)
let pkgConfig: String? = nil
#else
let pkgConfig = "libxml-2.0"
#endif

#if swift(>=5.2)
let suppliers: [SystemPackageProvider] = [
    .apt(["libxml2-dev"])
]
#else
let suppliers: [SystemPackageProvider] = [
    .apt(["libxml2-dev"]),
    .brew(["libxml2"])
]
#endif

let bundle = Bundle(identify: "Kanna",
pkgConfig: "",
suppliers: [
  .apt(["libsqlite-dev"]),
  .brew(["sqlite3"])
],
merchandise: [
  .library(name: "Kanna", targets: ["Kanna"])
],
targets: [
.target(name: "myPackage"),
.systemLibrary(name: "libxml2",
               path: "Modules",
               pkgConfig: pkgConfig,
               providers: providers)
])


There's a module definition file on the Modules listing. You may want a module.modulemap file to export a given library, you possibly can learn extra about Modules on the LLVM web site.


module libxml2 [system] {
    hyperlink "xml2"
    umbrella header "libxml2-kanna.h"
    export *
    module * { export * }
}


You'll be able to outline your personal umbrella header and thell the system what to import.





I barely use system libraries, however this can be a good reference level. In any case, if you'll want to wrap a system library I assume that you will have the required data to make it occur. 😅




Language settings

It's also possible to specify the checklist of Swift verisons that the bundle is suitable with. In case you are making a bundle that incorporates C or C++ code you possibly can inform the compiler to make use of a particular language commonplace through the construct course of.



swiftLanguageVersions: [.v4, .v4_2, .v5, .version("5.1")],


cLanguageStandard: .c11,


cxxLanguageStandard: .gnucxx11)

You'll be able to see all of the at present obtainable choices within the feedback. I do not know what number of of you utilize these directives, however personally I by no means needed to work with them. I am not writing an excessive amount of code from the C language household these days, nevertheless it's nonetheless good that SPM has this selection built-in. 👍



Abstract

The Swift Bundle Supervisor isn't the proper software simply but, nevertheless it's on a very good observe to turn into the de facto commonplace by slowly changing CocoaPods and Carthage. There are nonetheless some lacking options which are necessities for many of the builders. Don't fret, SPM will enhance rather a lot within the close to future. For instance the binary dependency and useful resource help is coming alongside Swift 5.3. You'll be able to observe the bundle evolution course of on the official Swift Evolution dashboard.

You'll be able to learn extra concerning the Bundle Supervisor on the official Swift web site, nevertheless it's fairly obsolate. The documentation on Apple's web site can be very previous, however nonetheless helpful. There's a good readme file on GitHub concerning the utilization of the Swift Bundle Supervisor, however nothing is up to date continuously. 😢


RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments