Swift Package Manager with resources compile errors
You missed a ,
after the target
close parentheses:
.target(
name: "BioSwift",
resources: [
.process("Resources/unimod.xml"),
.process("Resources/aminoacids.json"),
.process("Resources/elements.json"),
.process("Resources/enzymes.json"),
.process("Resources/functionalgroups.json"),
.process("Resources/hydropathy.json")
]
), // Here is the missed `,`
Also, you don't need to add files one by one! Instead, you can add a directory:
.process("Resources")
Swift package manager unable to compile ncurses installed through Homebrew
Problem
The main problem are conflicts of the header files, since ncurses is also supplied in /Applications/Xcode.app/.../MacOSX10.14.sdk/usr/include.
A common pure C solution in such situations is to just specify the custom include and lib directories with -I repective -L and it would work, see my answer regarding C ncurses here: https://stackoverflow.com/a/56623033/2331445
This approach does not seem to work with the Swift package manager. But that doesn't mean it's not possible with a little effort.
Possible Solution
We need to make sure that the ncurses header files provided by the macOS SDK are ignored. We can do this by specifying the -Xcc -D__NCURSES_H parameter for the swift build command.
This works because in the header file, there is this typical:
#ifndef __NCURSES_H
#define __NCURSES_H
...
#endif
The problem, of course, is that our custom installation of ncurses using Brew is also affected. But we can work around it:
- copy the new ncurses header files into our Sources/Cncurses directory
- replace __NCURSES_H through something different, e.g. __CNCURSES_H (note leading 'C')
- then make sure that all further nested includes are first searched in our local include directory by replace the angle brackets of the includes with quotes, so e.g. instead of
#include <ncursesw/unctrl.h>
the form '#include "ncursesw/unctrl.h"' is used
This can actually be done with the following command line commands:
cd Sources/Cncurses
cp -r /usr/local/Cellar/ncurses/6.1/include include
find . -name '*.h' -exec sed -i '' 's/__NCURSES_H/__CNCURSES_H/g' {} \;
find . -name '*.h' -exec sed -i '' -E -e "s/<(.*(`find . -name '*.h' -exec basename {} \; | paste -sd "|" -`))>/\"\1\"/g" {} \;
The last statement may require some explanation. With the help of an echo command, you can look at the generated sed expression, i.e. if you execute
echo "s/<(.*(`find . -name '*.h' -exec basename {} \; | paste -sd "|" -`))>/\"\1\"/g"
you get the following output:
s/<(.*(termcap.h|form.h|term.h|panel.h|ncurses.h|termcap.h|cursesp.h|cursesf.h|etip.h|form.h|cursesw.h|nc_tparm.h|unctrl.h|cursesapp.h|term.h|cursslk.h|panel.h|ncurses.h|tic.h|eti.h|ncurses_dll.h|term_entry.h|menu.h|cursesm.h|curses.h|curses.h|cncurses.h))>/"\1"/g
As you can see, it searches and replaces only local available include files.
Test
For a test we need a simple ncurses example program. It should be built and we should make sure that the correct version of the library is used.
module.modulemap
My header file is called cncurses.h. The module.modulemap looks like this:
module cncurses [system]
{
umbrella header "cncurses.h"
link "ncurses"
export *
}
cncurses.h
cncurses.h is a one-liner, it imports our copied and customized ncurses.h file from our local include folder:
#include "include/ncurses.h"
main.swift
In the NcursesExample folder we have main.swift where we have a simple cncurses swift app:
import cncurses
initscr()
curs_set(0)
move(5, 10)
addstr("NCURSES")
move(10, 10)
addstr("Hello World!")
refresh()
select(0, nil, nil, nil, nil)
Package.swift
Please note here the pkgConfig: "ncurses"
in the systemLibrary targets:
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "NcursesExample",
dependencies: [
],
targets: [
.systemLibrary(name: "cncurses", pkgConfig: "ncurses"),
.target(name: "NcursesExample", dependencies: ["cncurses"]),
.testTarget(
name: "NcursesExampleTests",
dependencies: ["NcursesExample"]),
]
)
Build
For pkg-config to do its job properly, we must first call the following:
export PKG_CONFIG_PATH="/usr/local/opt/ncurses/lib/pkgconfig"
Finally we initiate the build with:
swift build -Xcc -D__NCURSES_H
So first we should test if the correct ncurses lib was used. We can do that with:
otool -L .build/x86_64-apple-macosx/debug/NcursesExample
Among other lines, the output contains this:
/usr/local/opt/ncurses/lib/libncursesw.6.dylib (compatibility version 6.0.0, current version 6.0.0)
which looks promising.
Finally calling the binary:
Xcode Project
If you want to generate a Xcode project, use the following command:
swift package generate-xcodeproj
Then load the project in Xcode and
- select the project node
- in Build settings enter Preprocessor in the search field in the upper right
- under Apple Clang - Preprocessing / Preprocess Macros add __NCURSES_H=1 for Debug and Release
Swift Package Manager - Type 'Bundle' has no member “module” error
Besides all errors in the test file, the only issue remains is that module
is not an optional
. To fix that, just change this:
public let unimodURL = Bundle.module?.url(forResource: "unimod", withExtension: "XML")
to:
public let unimodURL = Bundle.module.url(forResource: "unimod", withExtension: "xml")
Update:
If you use .xcodeproj
file, you will continue seeing this error. You should consider opening it with the package.swift
or import it as a package (instead of converting it to a project)!
by the way, here is the generated file, so you can add it as a development asset when you are working with the xcodeproject:
import class Foundation.Bundle
private class BundleFinder {}
extension Foundation.Bundle {
/// Returns the resource bundle associated with the current Swift module.
static var module: Bundle = {
let bundleName = "BioSwift_BioSwift"
let candidates = [
// Bundle should be present here when the package is linked into an App.
Bundle.main.resourceURL,
// Bundle should be present here when the package is linked into a framework.
Bundle(for: BundleFinder.self).resourceURL,
// For command-line tools.
Bundle.main.bundleURL,
]
for candidate in candidates {
let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
return bundle
}
}
fatalError("unable to find bundle named BioSwift_BioSwift")
}()
}
Getting 'no such module' error when importing a Swift Package Manager dependency
It turned out that Swift Package Manager implicitly depends on the project's Configuration names. I had them at live/qa instead of Release/Debug, and changing them back resolved the issue. Very odd, but I hope it saves you some trouble dear reader.
Xcode binary Swift Package static framework can't find resources at runtime
Static libraries don't have bundles and so can't include resources. (Instead, their code is part of the main bundle.)
You can create a separate resources bundle, and link the app to both the static library and the resources bundle.
There's a nice article with detailed explanation here: https://medium.com/onfido-tech/reusing-code-and-resources-with-swift-static-libraries-and-resource-bundles-d070e82d3b3d .
Related Topics
Updated Approach to Reauthenticate a User
Swift Spritekit 3D Touch and Touches Moved
Use of or Operator on Cloudkit Predicate
Code Only Retrieving One Value from Data in Firebase
Cfdictionary Get Value for Key in Swift3
How to Get All Characters of the Font with Ctfontcopycharacterset() in Swift
Swift Uikit Dynamics Add Collision Boundary. After Rotation Does Not Work Correctly
Why Aren't My Custom Classes Appearing in the Dropdown in Interface Builder
Uibuttons Are Not Correctly Shaped on All Devices
Can't Change Uiimageview Image in Function (Swift)
Custom Cell with Uitableview Inside Uicollectionviewcell
What the Difference of Keys and Values in Dictionary of Swift
Swiftui Mysterious Spacing Between Large Text and Textfield in VStack
Swift Why Isn't My Date Object That's (Equatable) Equal After Converting It to a String and Back
Presenting a Uiviewcontroller from Skscene Shows Black Screen
How to Store the Progress of Progressview into Arraylist as One Element