Constructing a customized C library utilizing SPM
You should use the Swift Package deal Supervisor to create C household based mostly supply recordsdata (C, C++, Goal-C and Goal-C++) and ship them as standalone parts. If you do not know a lot in regards to the Swift Package deal Supervisor, it’s best to learn my complete tutorial about how SPM works. 📦
The one factor that it is advisable setup a library is a typical Package deal.swift
manifest file with a barely altered listing construction to help header recordsdata. Let’s make a MyPoint
library.
import PackageDescription
let bundle = Package deal(
title: "MyPoint",
merchandise: [
.library(name: "MyPoint", targets: ["MyPoint"]),
],
targets: [
.target(name: "MyPoint"),
]
)
All the things that you just put into the header file will probably be publicly out there for different builders to make use of, the implementation particulars are going to be positioned immediately below the Sources/[target]/
listing, however it’s a must to create an extra embrace
folder on your headers. Let’s make a MyPoint.h
file below the Sources/MyPoint/embrace
path with the next contents.
struct MyPoint {
int x;
int y;
};
We have simply outlined the general public interface for our library. Now when you attempt to compile it by the swift construct command, it’s going to complain that the venture is lacking some supply recordsdata. We are able to simply repair this by creating an empty MyPoint.c
file below the Sources/MyPoint
listing.
While you import a neighborhood header file to make use of in your implementation code, you may skip the “embrace” path and easily write #embrace “MyPoint.h”. You can additionally put every kind of C household parts into this venture, this methodology works with C++, Goal-C and even Goal-C++ recordsdata.
You can additionally place header recordsdata subsequent to the implementation supply code, however in that case the system will not be capable to auto-locate your public (umbrella) header recordsdata, so that you additionally should create a modulemap
file and supply the proper location of your headers explicitly. Should you use the construction with the embrace listing SPM will generate every little thing for you robotically.
Congratulations, you simply shipped your first C code with Swift Package deal Supervisor. 🥳
Interacting with C libraries utilizing Swift
We’ll create a model new Swift bundle to construct an executable software based mostly on the beforehand created C library. As a way to use a neighborhood bundle you may merely specify it as with the trail argument below the dependencies in your Package deal.swift
manifest file.
import PackageDescription
let bundle = Package deal(
title: "Pattern",
merchandise: [
.executable(name: "Sample", targets: ["Sample"]),
],
dependencies: [
.package(path: "../MyPoint")
],
targets: [
.target(name: "Sample", dependencies: [
.product(name: "MyPoint", package: "MyPoint"),
]),
]
)
This time we’re going to use the MyPoint library as a neighborhood dependency, however in fact you may handle and publish your individual libraries utilizing a git repository someplace within the cloud. Subsequent we must always create our Sources/Pattern/primary.swift
file, import the library and write some code.
import MyPoint
let p = MyPoint(x: 4, y: 20)
print("Good day, world!", p.x, p.y)
If each packages can be found domestically, be sure to place them subsequent to one another, then every little thing ought to work like a allure. You’ll be able to open the Pattern venture manifest file utilizing Xcode as properly, the IDE can resolve bundle dependencies robotically for you, however when you favor the command line, you should use the swift run
command to compile & run the executable goal.
With this method you may import the MyPoint module from some other Swift bundle and use the out there public parts from it. You simply have so as to add this module as a dependency, by the best way you may even name this module from one other C (C++, ObjC, Objc++) venture made with SPM. 😎
The way to use C system libraries from Swift?
There are millions of out there instruments which you could set up in your working system (Linux, macOS) with a bundle supervisor (apt, brew). For instance there’s the well-known curl command line instrument and library, that can be utilized for transferring knowledge from or to a server. In different phrases, you may make HTTP requests with it, simply sort curl "https://www.apple.com/"
right into a terminal window.
These system parts are normally constructed round libraries. In our case curl comes with libcurl, the multiprotocol file switch library. Typically you may wish to use these low degree parts (normally written in C) in your software, however how will we add them as a dependency? 🤔
The reply is straightforward, we will outline a brand new systemLibrary goal in our bundle manifest file.
import PackageDescription
let bundle = Package deal(
title: "Pattern",
merchandise: [
.executable(name: "Sample", targets: ["Sample"]),
],
dependencies: [
.package(path: "../MyPoint")
],
targets: [
.systemLibrary(
name: "libcurl",
providers: [
.apt(["libcurl4-openssl-dev"]),
.brew(["curl"])
]
),
.goal(title: "Pattern", dependencies: [
.product(name: "MyPoint", package: "MyPoint"),
.target(name: "libcurl"),
]),
]
)
Contained in the Package deal.swift
file you may set the suppliers for the library (equivalent to brew for macOS or aptitude for a lot of Linux distributions). Sadly you continue to should manually set up these packages, as a result of SPM will not do that for you, consider it as “only a reminder” for now… 😅
It will permit us to create a customized modulemap file with further headers (common or umbrella) and linker flags inside our venture folder. First, we must always add the next modulemap definition to the Sources/libcurl/module.modulemap
file. Please create the libcurl
listing, if wanted.
module libcurl [system] {
header "libcurl.h"
hyperlink "curl"
export *
}
The idea of modules are coming from (clang) LLVM, I extremely suggest checking the linked article if you wish to know extra about modulemaps. This fashion we inform the compiler that we wish to construct a module based mostly on the curl library, therefore we hyperlink curl. We additionally wish to present our customized header file to make some further stuff out there or extra handy. Folks normally name these header recordsdata shims, umbrella headers or bridging headers.
An umberlla header is the principle header file for a framework or library. A bridging header permits us to make use of two languages in the identical software. The shim header works across the limitation that module maps should comprise absolute or native paths. All of them exposes APIs from a library or language to a different, they’re very related, however they don’t seem to be the identical idea. 🙄
In our case we’ll create a libcurl.h
header file contained in the Sources/libcurl
folder. The module map merely refers to this header file. This is what we’ll place within it.
#embrace <stdbool.h>
#embrace <curl/curl.h>
typedef size_t (*curl_func)(void * ptr, size_t measurement, size_t num, void * ud);
CURLcode curl_easy_setopt_string(CURL *curl, CURLoption possibility, const char *param) {
return curl_easy_setopt(curl, possibility, param);
}
CURLcode curl_easy_setopt_func(CURL *deal with, CURLoption possibility, curl_func param) {
return curl_easy_setopt(deal with, possibility, param);
}
CURLcode curl_easy_setopt_pointer(CURL *deal with, CURLoption possibility, void* param) {
return curl_easy_setopt(deal with, possibility, param);
}
This code comes from the archived SoTS/CCurl repository, however when you examine the shim file contained in the Kitura/CCurl bundle, you will discover a just about related method with much more handy helpers.
The primary purpose why we want these capabilities is that variadic capabilities cannot be imported by Swift (but), so we now have to wrap the curl_easy_setopt
calls, so we’ll be capable to use it from Swift.
Okay, let me present you the right way to write a low-level curl name utilizing the libcurl
& Swift.
import Basis
import MyPoint
import libcurl
class Response {
var knowledge = Information()
var physique: String { String(knowledge: knowledge, encoding: .ascii)! }
}
var response = Response()
let deal with = curl_easy_init()
curl_easy_setopt_string(deal with, CURLOPT_URL, "http://www.google.com")
let pointerResult = curl_easy_setopt_pointer(deal with, CURLOPT_WRITEDATA, &response)
guard pointerResult == CURLE_OK else {
fatalError("Couldn't set response pointer")
}
curl_easy_setopt_func(deal with, CURLOPT_WRITEFUNCTION) { buffer, measurement, n, reference in
let size = measurement * n
let knowledge = buffer!.assumingMemoryBound(to: UInt8.self)
let p = reference?.assumingMemoryBound(to: Response.self).pointee
p?.knowledge.append(knowledge, depend: size)
return size
}
let ret = curl_easy_perform(deal with)
guard ret == CURLE_OK else {
fatalError("One thing went improper with the request")
}
curl_easy_cleanup(deal with)
print(response.physique)
I do know, I do know. This appears horrible for the primary sight, however sadly C interoperability is all about coping with pointers, unfamiliar varieties and reminiscence addresses. Anyway, here is what occurs within the code snippet. First we now have to outline a response object that may maintain the info coming from the server as a response. Subsequent we name the system capabilities from the curl library to create a deal with and set the choices on it. We merely present the request URL as a string, we go the consequence pointer and a write perform that may append the incoming knowledge to the storage when one thing arrives from the server. Lastly we carry out the request, examine for errors and cleanup the deal with.
It isn’t so unhealthy, however nonetheless it appears nothing such as you’d anticipate from Swift. It is only a fundamental instance I hope it’s going to allow you to to grasp what is going on on below the hood and the way low degree C-like APIs can work in Swift. If you wish to observe it’s best to strive to check out the Kanna library and parse the response utilizing a customized libxml2 wrapper (or you may examine a SQLite3 wrapper). 🤓
The system library goal characteristic is a pleasant approach of wrapping C [system] modules with SPM. You’ll be able to learn extra about it on the official Swift boards. If you’re nonetheless utilizing the outdated system library bundle sort format, please migrate, because it’s deprecated and it will be fully eliminated afterward.