No such module in a Swift Package using Xcode - package listed in dependencies - swift

I create a blank template package:
> swift package init --name Temp
> open Package.swift
Xcode Version 13.2.1 (13C100) opens the package.
I add a dependency to the package.
dependencies: [
.package(url: "https://github.com/johnsundell/publish.git", from: "0.7.0")
],
Xcode > Product > Build succeeds at this point.
I edit Temp/Sources/Temp/Temp.swift to insert the first line the package that is defined in dependencies.
import Publish
A build now generates the following error:…/Temp/Sources/Temp/Temp.swift:1:8: error: no such module 'Publish'.
I feel certain this is an Apple bug. Or I could be missing something.
There are several posts about this problem when there is an xcodeproj and the additional structure that provides. Some of them hint at workarounds that help some people.
Has anyone seen this and/or know of how to resolve it?
Apple's Creating a Standalone Swift Package with Xcode document doesn't provide any insight.

thanks for the chatter in the comments, #Larme & #koen, it helped
The issue was user error (and/or a documentation lapse). Living on the (bleeding) edge.
Sometimes updates from changes are slow or require a clean or a relaunch.
Xcode auto-generates Schemes from the targets defined in your package. My build was targeting MyTarget.
Two things were missing:
name: "Publish" was not included in the package dependency - it's needed so you can reference it below (or maybe this can be derived, it's hard to tell because of Xcode refresh issues), and
a reference is needed in the dependencies for each target using the package-dependency, i needed to add dependencies: ["Publish"] in the related target
dependencies: [
.package(name: "Publish", url: "https://github.com/johnsundell/publish.git", from: "0.7.0")
],
…
targets: [
.target(
name: "MyTarget",
dependencies: ["Publish"]),
]

Related

Swift Package - How to exclude files in root git directory from the actual Swift Package?

I am creating a Swift Package that is essentially a wrapper for multiple XCFrameworks generated from Objective-C frameworks so they can be installed via SPM.
Everything works fine as far as creating the SP and ability to add it as a dependency to an app. But I have a bunch non-essential files included in the SP's repository that I don't want to include in the actual SP - i.e. They shouldn't show up in Xcode's navigator when the SP is added as a dependency.
(These consist of the source Obj-C Frameworks, README, Changelog, Xcode Workspace for demo app, Script files for generating the XCFrameworks, etc).
Is this even possible?
Or will SPM always checkout the entire repo and make all files visible to the user?
I have tried using various permutations of the Target specifiers: source, path, exclude but to no avail.
Here is the closest I can get with a valid manifest, but when I check out the SP in a dummy Xcode app, I can still see all the files from the repo included:
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "WrapperSwiftPackage",
platforms: [.iOS(.v13)],
products: [
.library(name: "WrapperSwiftPackage", targets: ["WrapperSwiftPackage"])
],
dependencies: [],
targets: [
.target(
name: "WrapperSwiftPackage",
dependencies: [
"ObjCFramework1",
"ObjCFramework2"
],
path: "", // Set to root directory so we can exclude files below
exclude: [
"CHANGELOG.md",
"Dangerfile.swift",
"README.md",
"Workspace.xcworkspace",
"Scripts/generate-xcframework.sh",
"Scripts/link_git_hooks.sh",
"Objective-C Frameworks/"
],
sources: [
"Sources/WrapperSwiftPackage/main.swift",
"XCFrameworks/ObjCFramework1.xcframework",
"XCFrameworks/ObjCFramework2.xcframework"
]
),
.binaryTarget(name: "ObjCFramework1", path: "XCFrameworks/ObjCFramework1.xcframework"),
.binaryTarget(name: "ObjCFramework2", path: "XCFrameworks/ObjCFramework2.xcframework")
]
)
Not sure if that isn't a bug though, but I've accidentaly came up to one solution for this.
If you put an empty Package.swift (I mean, one like this):
// swift-tools-version:5.5
import PackageDescription
let package = Package()
into one of project subfolders, then even though SPM is checking the subfolder out, it's excluded from Xcode navigator, and thus, from the project visibility.
I would like to know if that's a bug or is it documented somewhere, every hint is appreciated.
Works with local and remote dependencies.

How can I enable ABI stability for a SwiftPM package?

Given my bog standard Package Description of
let package = Package(
name: "MyLib",
products: [
.library(name: "MyLib", targets: ["MyLib"]),
],
dependencies: [],
targets: [
.target(
name: "MiniRxSwift",
dependencies: [],
swiftSettings: [
.define("<see below>")
]),
...
I'm trying to get swiftpm to pass the -enable-library-evolution flag through to swiftc, but I've been unsuccessful.
Using swiftSettings of .define("-enable-library-evolution"), I get a compile error which states
"error: conditional compilation flags must be valid Swift identifiers (rather than '-enable-library-evolution')"
I get the same error if I omit the leading hyphen e.g. `.define("enable-library-evolution")
I've tried the Xcode setting of .define("BUILD_LIBRARIES_FOR_DISTRIBUTION") which doesn't result in a compile error, but also doesn't result in the flag getting set, it instead results in -DBUILD_LIBRARIES_FOR_DISTRIBUTION on the command line for swiftc, which doesn't do anything.
After a bit more research, I worked out that .define in swiftSettings is hardwired to produce things with -D - hence it's name.
Instead I needed to use unsafeFlags, which does indeed result in the flag getting passed correctly to the swift compiler:
swiftSettings: [
.unsafeFlags(["-enable-library-evolution"])
]
BUT then when I attempt to consume this package, Xcode fails to load the package, with the error:
The package product 'MyLib' cannot be used as a dependency of this target because it uses unsafe build flags.
If I can't enable library evolution without unsafe build flags, and I can't use unsafe build flags in a library, then what can I do? What is the point of having unsafeFlags if you can't use any libraries which set them?
Why do you need ABI stability if you have access to the package? You should avoid it if possible. That said, you should be able to use unsafeFlags with editable packages (ones you have dragged into the project) but not ones you include the normal way via URL.
If you need to vend someone a swift package as a binary you will need to build your package as an XCFramework and then you can put that in a place where SwiftPM can depend on it. That means it can be hosted at a static place where you give the URL or it can be embedded in the git repo with the Package.swift manifest that you have for vending the package. The only tool I know of to help with this is found here which works for Swift only package when I have used it.

Cant run/find Executable Swift Package

Bottom line I am trying to run an executable from a swift package I created and linked to my target.
I think I'm missing something here, so I've created an executable swift package with the content
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "AutoLocalized",
platforms: [
.macOS(.v10_15), .iOS(.v12)
],
products: [
.executable(name: "autolocalized", targets: ["AutoLocalized"])
],
targets: [
.target(name: "AutoLocalized", dependencies: []),
.testTarget(name: "AutoLocalizedTests", dependencies: ["AutoLocalized"]),
]
)
so as I understand adding the ".executable" outputs a file I can then run via Xcode.
I am trying to run a build phase script using that output but I dont know how to access it.
When I try to add dependencies to my target I can find the "autolocalized" executable
but building the project I get an error:
After a lot of searching I found A way, maybe not the best but it workes.
So what I did is adding the package via SPM but without a dependency to any target, that means I had to make sure I build the package manually, finding it in the Build folder and using it directly from there, this was the only way I found to run mac os scripts using build phases in Xcode, I am positive others tried but no one found a good answer, so here it is.

How to use Swift Package Manager's binaryTarget?

I'm trying to use Swift Package Manager's binaryTarget to include the Stripe xcframework available here https://github.com/stripe/stripe-ios/releases/tag/v19.3.0. The package manager doesn't complain, and lets me link to it, but I can't import it im. I've made a sample repo to show it here https://github.com/martyu/StripePackageTest. Am I missing something?
First of all, your example is not testable because you have forgotten to provide a version tag, so this is not a real package.
Second, and more important, I think you have a misconception about how a package works as a binary target. You seem to think that your Swift Package can contain code that sees the XCFramework. (That's why you are trying to import in the framework module in the Sources code of the package.) That's wrong. It's the app that imports the framework module. The package is merely a way of distributing the framework.
In other words, you can write a source code package or a framework-bearing package. One package cannot be both.
But of course you can write a source code package that depends on a framework-bearing package.
First, you don't need a version tag for it to be "real package". You can specify package dependencies via commit SHA and branch as well. Also you can add local package repos in xcode via file://. Note, this is NOT the same as a local dev override.
I didn't have much luck with swift build but I did get it to work fine by creating an app in Xcode and adding the package to it. I think this is what #matt is getting at. You need to import it into a project (xcode, or another SP) and then xcode will assemble all the dependencies when it ~~builds~~ feels like it.
Here's the modified Package.swift I used. I changed the name to Example (as that's presumably the SDK you are building which depends on Stripe). You can include "Stripe" in the Example library's targets if you want it embedded in its framework. Otherwise the client app just needs to import it as well (via the tickboxes when you add it in Xcode, or via dependencies in another Package.swift).
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Example",
platforms: [
.iOS(.v11)
],
products: [
.library(
name: "Example",
type: .dynamic,
targets: ["Example"]),
.library(
name: "Stripe",
targets: ["Stripe"])
],
dependencies: [],
targets: [
// I thought this was defining the Stripe binaryTarget...
.binaryTarget(name: "Stripe",
url: "https://github.com/stripe/stripe-ios/releases/download/v19.3.0/Stripe.xcframework.zip",
checksum: "fe459dd443beee5140018388fd6933e09b8787d5b473ec9c2234d75ff0d968bd"),
// ... and then linking it to the Example project here via "dependencies" ...
.target(name: "Example", dependencies: ["Stripe"], path: "Sources")
// ... so when I'm in "Example" files, I thought I'd be able to import "Stripe" to them
]
)

Importing modules with Swift package manager

I am trying to use Swift's package manager to import external modules in my project. My first module come from the Vapor project. I cannot seem to get it working. I start with
swift package init
swift package generate-xcodeproj
My Package.swift looks like this:
import PackageDescription
let package = Package(
name: "OpenTools",
products: [
.library(
name: "OpenTools",
targets: ["OpenTools"]),
],
dependencies: [
.package(url: "https://github.com/vapor/json.git", from: "2.0.0")
],
targets: [
.target(name: "OpenTools", dependencies: ["JSON"]),
]
)
I then run
swift package update
swift package generate-xcodeproj # to regenerate with dependencies
and then try to import the JSON package in my main file
import JSON
The modules are there as shown below but the import gets back with an No such module 'JSON' error.
Any thoughts?
Probably the problem lies within Xcode, as it does not know yet that JSON exists, because it was not built yet. This can easily be solved by just building your project (with cmd-B). With the generated xcodeproj, Xcode should know that it first needs to build JSON and then the rest, because JSON is marked as a dependency for your target.
You can check this, by navigating in Xcode to your target (when you click on the project description file) and afterwards to "Build Phases". Under Target Dependencies you should find your JSON module.
In addition you should find a JSON module under your targets, which compiles the sources you gathered from github.
Your project should also build when executing swift build in your project root.
With Xcode 11 you should be able to open Package.swift directly which will give you a proving ground for verifying the package manifest (aka: the Package.swift file) and compiling the target. This should help see what is actually causing the error that's preventing the module from being compiled.