Wednesday, January 4, 2023
HomeiOS DevelopmentThe Swift bundle manifest file

The Swift bundle manifest file


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

Swift


If you wish to study how one can use the Swift Package deal Supervisor it’s 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.


Package deal sorts

There are a number of bundle sorts which you could create with the swift bundle init command. You possibly can specify the --type flag with the next values: empty, library, executable, system-module, manifest. You can even outline a customized bundle identify by way of the --name flag.

  • The empty bundle will create the default file construction with out the pattern code recordsdata.
  • The library kind will create a reusable library product template.
  • The executable kind will create a Swift utility with an executable product definition within the bundle and a foremost.swift file as a place to begin.
  • The system-module kind will create a wrapper round a system supplied bundle, reminiscent of libxml, we’ll discuss this in a while.
  • The manifest kind will solely create a Package deal.swift file with out the rest.





The Package deal manifest file

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

Each single Package deal.swift file begins with a particular remark line the place it’s a must to outline the model of the used Swift instruments. The newest model is sort of totally different from the older ones.



Subsequent it's a must to import the PackageDescription framework with a purpose 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 best way you'll be able to change the model of the used instruments, you'll be able to learn extra about this within the Package deal Supervisor utilization readme.





Package deal

A bundle is only a bunch of Swift (or different) recordsdata. The manifest file is the outline of what and how one can 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 possibly can solely have precisely one bundle definition contained in the file. That is the shortest and most ineffective one which you could create. 🙈


let bundle = Package deal(identify: "myPackage")


The bundle identify goes for use if you find yourself importing packages as dependencies, so identify your pacages rigorously. If you happen to select a reserved identify by a system framework there may 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 undertaking by way of the swift bundle generate-xcodeproj command that undertaking will attempt to hyperlink all the pieces dynamically, however should you open the Package deal.swift file utilizing Xcode 11, the dependencies might 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 = Package deal(
    identify: "myPackage",
    platforms: [
        .iOS(.v13),         
        .macOS(.v10_15),    
        .tvOS(.v13),        
        .watchOS(.v6),      
    ]
)


Once 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 principle bundle platforms. Lengthy story brief if it's essential add help for Apple platforms, it's best to specify a platform flag with a supported model, in any other case SPM will use the oldest deployment goal primarily based on the put in SDK, apart from macOS, that is going to be v10_10. Each bundle has Linux help by default, you'll be able to't add such restrictions but, however possibly this may change within the close to future, additionally Home windows is coming.





Product

A bundle can have a number of closing merchandise (construct artifacts). At present there are two varieties of construct merchandise: executables and libraries. The executable is a binary that may be executed, for instance this could be a command line utility. 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 = Package deal(identify: "myPackage", merchandise: [
    .library(name: "myPackageLib", targets: ["myPackageLib"]),
    .library(identify: "myPackageStaticLib", kind: .static, targets: ["myPackageLib"]),
    .library(identify: "myPackageDynLib", kind: .dynamic, targets: ["myPackageLib"]),
    .executable(identify: "myPackageCli", targets: ["myPackage"])
], targets: [
    .target(name: "myPackageLib"),
    .target(name: "myPackageCli"),
])


If the library kind is unspecified, the Package deal Supervisor will robotically select it primarily based on the shopper's desire. As I discussed this earlier generated Xcode initiatives desire dynamic linking, however should you merely open the manifest file the app might be statically linked.





Dependency

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

let bundle = Package deal(
    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 could be a GitHub url, thankfully you'll be able to add non-public repositories as effectively through the use of an ssh key primarily based authentication. Simply use the [email protected]:BinaryBirds/viper-kit.git url format, as an alternative of the HTTP primarily based, 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 can lead to a library or an executable. It is best to have at the very least one goal in your undertaking file in any other case you'll be able to't construct something. A goal ought to at all times have a reputation, each different settings is elective.


Settings

There are numerous 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'll be able to specify this by setting the trail attribute. Additionally you'll be able to exclude supply recordsdata from the goal or explicitly outline the sources you need 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'll be able to see you'll be able to outline preprocessor macros for each single language. You need to use the secure instances for primary stuff, however there may be 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 may be .debug or .launch


Check targets

Check targets are used to outline check suites. They can be utilized to unit check different targets utilizing the XCTest framework. They seem like 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 feel the one distinction between a goal and a check goal is which you could run a check goal utilizing the swift check command, however from a structural standpoint, they're mainly the identical.




Package deal configs and system libraries

You possibly can 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 way of the superb Kanna(鉋) - XML/HTML parser repository. I am utilizing this device lots, 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 = Package deal(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'll be able to learn extra about Modules on the LLVM web site.


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


You possibly can 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 it's essential wrap a system library I assume that you will have the required information to make it occur. 😅




Language settings

You can even specify the listing of Swift verisons that the bundle is appropriate with. In case you are making a bundle that incorporates C or C++ code you'll be able to inform the compiler to make use of a selected language normal through the construct course of.



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


cLanguageStandard: .c11,


cxxLanguageStandard: .gnucxx11)

You possibly can see all of the presently 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 Package deal Supervisor will not be the proper device simply but, nevertheless it's on a very good monitor to develop into the de facto normal by slowly changing CocoaPods and Carthage. There are nonetheless some lacking options which might be necessities for many of the builders. Don't be concerned, SPM will enhance lots within the close to future. For instance the binary dependency and useful resource help is coming alongside Swift 5.3. You possibly can monitor the bundle evolution course of on the official Swift Evolution dashboard.

You possibly can learn extra concerning the Package deal 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 Package deal Supervisor, however nothing is up to date ceaselessly. 😢




Supply hyperlink

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments