Constructing static and dynamic Swift libraries utilizing the Swift compiler


What the heck is a library?

A library) is a set of Swift parts that different purposes can use.

Think about that you’re making a easy software to pluralize a string. It really works nice, you end the app and also you begin working in your subsequent one. In your subsequent software, you face the very same situation, it’s a must to print countable objects (e.g 2 bananas). What would you do? 🤔

The very first thing that may cross your thoughts is to repeat all of the supply code from the primary software into the second. Nicely, this might work in fact, however what occurs when you uncover a bug within the pluralization element? Now it’s a must to repair the difficulty at two locations, since you have simply duplicated the complete stuff. There should be a greater approach… 🧠

Luckily laptop programmers confronted the very same situation, so that they invented shared libraries. A shared library is a particular form of binary element that you should utilize in your principal software. This fashion you’ll be able to outsource Swift code right into a separate file (or bunch of recordsdata), throw in some entry management to permit different apps to make use of public strategies and name capabilities out of your library and right here we go, we simply shared our widespread code between our purposes.

Oh wait, there’s a bug within the lib, how can I repair it? Nicely, that is the place issues get a bit difficult, however don’t be concerned an excessive amount of, I am going to attempt to clarify the way it works. So, final time, you understand, after we talked in regards to the Swift compiler and linker, I discussed, that they’ll resolve dependencies in your program. Whenever you use a library you’ll be able to select between two approaches.

  • static linking
  • dynamic linking

Static linking implies that the supply code contained in the library will probably be actually copy-pasted into your software binary. Dynamic linking however implies that your library dependencies will probably be resolved at runtime. By the way in which, it’s a must to determine this upfront, since it’s a must to construct both a static or a dynamic library. Huhh? Okay, let me do this once more… 🙃

The static library strategy is extra easy. You’ll be able to simply construct a static library utilizing the compiler (you may see find out how to make one in a while), then you’ll be able to import this library inside your software supply (import MyLibrary). Now while you compile the principle app, it’s a must to inform the compiler the placement of your static (binary) library, and the publicly accessible objects (headers or module map) which might be out there to make use of. This fashion when your app consists the symbols from the lib (lessons, strategies, and so on) could be copied to the principle executable file). Whenever you run the app, required objects will probably be there already contained in the binary file, so you’ll be able to run it as it’s.

The principle distinction between a static and a dynamic library is that you do not copy each required image to the executable software binary while you use a dylib file, however a number of the “undefined” symbols will probably be resolved at runtime. First it’s a must to construct your library as a dynamic dependency utilizing the Swift compiler, it will produce a dynamic (binary) library file and a module map (header recordsdata). Whenever you make the ultimate model of your app, the system will put references of the dynamic library to your executable as a substitute of copying the contents of the dylib file. If you wish to run your software it’s a must to be sure that the referenced dynamic library is on the market to make use of. The working system will attempt to load the generated dylib file so the appliance resolves the symbols based mostly on the reference pointers. 👈

Ought to I select dynamic or static linking?

Nicely, it will depend on the atmosphere. For instance the Swift Bundle Supervisor prefers to make use of static linking, however Xcode will attempt to construct SPM packages as dynamic dependencies. You can too explicitly inform SPM to construct a static or dynamic library, however in a lot of the instances it is best to stick to the automated worth, so the system can construct the proper module dependency for you.


import PackageDescription

let package deal = Bundle(
    identify: "MyLibrary",
    merchandise: [
        
        .library(name: "MyLibrary", targets: ["MyLibrary"]),
        
    ],
    targets: [
        .target(name: "MyLibrary", dependencies: []),
    ]
)

By the way in which in case you are confused sufficient, I’ve an article for rookies about Swift packages, modules, frameworks and the instruments that makes this entire dependency administration doable. It is best to positively have a look, it is a some form of a deep dive into FAT frameworks, however the first a part of the article is filled with helpful definitions and introductions to varied instructions.

Again to the unique query: static vs dynamic? Do you bear in mind the bug within the library that we now have to repair? Should you use a static library it’s a must to rebuild all of the apps which might be relying on it (they should be linked with the fastened library in fact) with the intention to make the difficulty disappear. 🐛

Since a dynamic library is loaded at runtime and the symbols are usually not embedded into the appliance binary, you’ll be able to merely construct a brand new dylib file and exchange the outdated one to repair the bug. This fashion all of the apps which might be referencing to this dependency can have the repair without spending a dime. There isn’t a must recompile everyting, besides the defective code within the framework itself. 💪

It’s also price to say that the ultimate app measurement is smaller while you use a dylib.

Okay, however why ought to I ever use static linking if dylibz are so cool? The reality is that typically you need to encapsulate every thing right into a single binary, as a substitute of putting in plenty of different dylib recordsdata into the system. Additionally what occurs if one thing deletes a dylib that your app would require to work flawlessly? That’d suck for certain, particularly if it’s a mission-critical script on a server… 😳

Hopefully, I over-explained issues, so we are able to begin constructing our very first static library.

Compiling a static Swift library

Do you continue to have that little Level struct from the earlier tutorial? Let’s construct a static library from that file, however earlier than we accomplish that, we now have to explicitly mark it as public, plus we’d like a public init technique so as to have the ability to create a Level struct from our software. You understand, in Swift, entry management permits us, programmers, to cover particular components of a library from different builders.

public struct Level {
    public let x: Int
    public let y: Int

    public init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

Now we’re able to construct our static library based mostly on this single level.swift supply file. As I discussed this earlier than, we’d like a binary file and a module map file that accommodates the publicly accessible interface for the lib. You need to use the -emit-library flat to inform the Swift compiler that we’d like a binary library file plus utilizing the -emit-module parameter will produce a Swift module information file with all of the API and docs wanted for different modules. By default the compiler would emit a dylib (on macOS a minimum of), so we now have to make use of the -static flat to explicitly generate a static dependency. 🔨

swiftc level.swift -emit-module -emit-library -static

The command above ought to produce 4 new recordsdata:

  • libpoint.a – The binary static library itself
  • level.swiftdoc – Documentation for the module (binary format)
  • level.swiftmodule – Information in regards to the module, “Swift header file”
  • level.swiftsourceinfo – Supply info file

Transfer these recordsdata inside a lib folder, so it’s going to be less difficult to work with them. That is actually it, we have simply created a working static library, however how can we use it to hyperlink them towards our principal software? 🤔

To start with, we now have to import our newly created module contained in the principal.swift file if we need to use the objects (in our case the Level struct) from it. By the way in which you’ll be able to add a customized module identify to your library when you use the -module-name [name] argument with the earlier swiftc command.

import level

let p = Level(x: 4, y: 20)

print("Hey library!", p.x, p.y)

So, all of our library recordsdata are positioned in a lib folder, and our default module identify is level (based mostly on our single enter file). We are able to use the swiftc command once more, to compile the principle file, this time we use the -L flag so as to add a library search path, so the compiler can find our binary libpoint.a file. We additionally should set a search path for imports, the -I property will assist us, this fashion the general public API (headers) of the module will probably be out there in our supply file. The very very last thing that we now have to append to the top of the command is the -l[name] flag, this specifies the library identify we wish to hyperlink towards. Watch out, there is no such thing as a area in between the -l and the identify worth! ⚠️

swiftc principal.swift -L ./lib/ -I ./lib/ -lpoint

# run the app
./principal
# Hey library! 4 20

Voilá, we have simply separated a file from the principle software by utilizing a static dependency. 👏

Compiling a dynamic Swift library

In idea, we are able to use the identical code and construct a dynamic library from the level.swift file and compile our principal.swift file utilizing that shared framework. We simply drop the -static flag first.

swiftc level.swift -emit-module -emit-library

This time the output is barely totally different. We have a libpoint.dylib binary as a substitute of the libpoint.a, however all the opposite recordsdata look equivalent. Extension my range per working system:

  • macOS – static: .a, dynamic: .dylib
  • Linux – static: .so, dynamic: .dylib
  • Home windows – static: .lib, dynamic: .dll

So we now have our dylib file, however the true query is: can we construct the principle.swift file with it?

swiftc principal.swift -L ./lib/ -I ./lib/ -lpoint

# run the app
./principal
# Hey library! 4 20

Now rename the libpoint.dylib file into libpoint.foo and run the principle app once more.

./principal

# dyld: Library not loaded: libpoint.dylib
#   Referenced from: /Customers/tib/./principal
#   Motive: picture not discovered
# zsh: abort      ./principal

Whoops, looks like we now have an issue. Don’t be concerned, that is the anticipated output, since we renamed the dynamic library and the appliance cannot discover it. When the loader tries to get the referenced symbols from the file it seems to be up dynamic libraries at a couple of totally different locations.

  • The listing you specified by way of the -L flag (./lib/).
  • The listing the place your executable file is (./)
  • The /usr/lib/ or the /usr/native/lib/ directories

Because the /usr/lib/ listing is protected by the well-known SIP “guard”, it is best to ship your dylib recordsdata subsequent to your executable binary, or alternatively you’ll be able to set up them underneath the /usr/native/lib/ folder. Sadly, this lookup technique can result in all form of points, I actually do not need to get into the small print this time, however it may well result in compatibility and safety points. 🤫

The excellent news is that now when you change one thing within the dylib, and also you merely rebuild & exchange the file then you definately run the ./principal once more (with out recompiling), the altered dynamic library will probably be used. Simply attempt to put a print assertion into the init technique of the Level struct…

Abstract

Actually, I would moderately go together with a static library in a lot of the instances as a result of utilizing a static library will assure that your software has each crucial dependency embedded into the binary file.

In fact dynamic libraries are nice in case you are the writer of a generally used framework, such the Swift normal library, Basis or UIKit. These modules are shipped as shared libraries, as a result of they’re big and virtually each single app imports them. Simply give it some thought, if we would hyperlink these three frameworks statically that’d add lots to the scale of our apps, plus it might be approach tougher to repair system-wide bugs. That is the rationale why these packages are shipped as shared libz, plus Apple can provides us a promise that these parts will at all times be out there as a part of the working system. 😅

Anyhow, there are some instruments that you should utilize to change library loader paths, I am going to let you know extra about this subsequent time. It should be a extra superior matter together with totally different languages. I’ll present you find out how to construct a library utilizing C and find out how to name it utilizing Swift, with out SPM. 🤓

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Stay on op - Ge the daily news in your inbox