How to create an SPM package based on ObjC code? - swift

I'd like to create a SwiftPackageManager library based on Objective-C code but I can't seem to grasp what I'm missing.
My latest change to just vanilla ObjC interface .h file inside the include folder was to add an extra C header that includes de ObjC but still had no success. What am I missing?
The Package.swift file is the default generated one and from what I read it should automatically generate the module map from the include folder.
My swift-tools-version is 5.5

Figured it out.
I added a modulemap that specifies my ObjC header and it worked
Not sure if it is the correct way to do it since the include folder should already do this automatically.

So just had the same issue. (my 5 cent)
This is may Package.swift:
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "AlgorithmSDK",
platforms: [
.iOS(.v13)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages. , "AlgorithmSDKObjc"
.library(
name: "AlgorithmSDK",
targets: ["AlgorithmSDK","AlgorithmSDKObjc" ]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "AlgorithmSDK",
dependencies: []),
.testTarget(
name: "AlgorithmSDKTests",
dependencies: ["AlgorithmSDK"]),
.target(
name: "AlgorithmSDKObjc",
dependencies: [],
publicHeadersPath:"include"),//<----- This path is relative to the target! (and can be ignored)
]
)
With the structure:
The publicHeadersPath should be publicHeadersPath:"include" OR ignored according to docs (it seems that it takes it relative to target and not the root). I don't think we should touch module.modulemap for such a simple structure

Related

Conditional dependency for target in SwiftPM

I have a custom swift package I am building that has as one of its dependencies a package (Bugfender) that is an xcframework and is iOS only (and Mac Catalyst). Understandably, when I try to compile this package in Xcode (12.5) I get an error that a mac library for that package cannot be found (all iOS specific code is wrapped in a #if os(iOS) block).
Based on this (https://github.com/apple/swift-evolution/blob/master/proposals/0273-swiftpm-conditional-target-dependencies.md) addition to the Swift package spec, I thought I could use a condition to exclude the dependency for mac, but when I try the following Swift.package file, I still get the same error when building for mac. Is this a bug or am I doing something wrong? It seems like it should work based on this post as well (Swift package manager: How best to indicate platform dependent code?)
// 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: "GTSApplicationLogging",
platforms: [
.iOS(.v12), .macOS(.v10_13),
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "GTSApplicationLogging",
targets: ["GTSApplicationLogging"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(name: "BugfenderPackage", url: "https://github.com/bugfender/BugfenderSDK-iOS", .exact("1.10.2")),
.package(url: "https://github.com/SwiftyBeaver/SwiftyBeaver.git", .exact("1.9.5")),
.package(url: "https://github.com/marmelroy/Zip.git", .exact("2.1.1")),
.package(path: "../GTSPureAppleExtensions"),
.package(path: "../GTSApplicationError"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "GTSApplicationLogging",
dependencies: ["SwiftyBeaver", "GTSPureAppleExtensions", "GTSApplicationError", "Zip", .product(name: "BugfenderLibrary", package: "BugfenderPackage", condition: .when(platforms: [.iOS]))]),
.testTarget(
name: "GTSApplicationLoggingTests",
dependencies: ["GTSApplicationLogging", "GTSApplicationError"]),
])

How to use a Swift Package (SPM) with Objective code in Objective C project?

I've seen mixed reviews about being able to use mixed language projects with SPM. I have a framework that includes Objective-C code. I am using the framework in a mixed language project (Objective-C & Swift); specifically, I need to use it in Objective-C code. I have converted the framework to a Swift Package. The package compiles fine. But I can't get the Objective-C code to recognize my Swift Package. Everything worked fine with the old framework. I've added the new Swift Package to the project.
When I use "#import CalculatorModel;" I get "Module 'CalculatorModel' not found".
I'm trying to import to header into the Objective-C code like this:
#import <CalculatorModel/CalculatorModel.h>
But, of course, the header isn't found.
Here's my package.swift.
let package = Package(
name: "CalculatorModel",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "CalculatorModel",
targets: ["CalculatorModel"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "CalculatorModel",
dependencies: []),
.testTarget(
name: "CalculatorModelTests",
dependencies: ["CalculatorModel"]),
]
)
How can I use a Swift Package that includes Objective C in a project with objective-C?
Make sure that your header file (CalculatorModel.h) is public by:
using publicHeadersPath & headerSearchPath in your Package.swift file:
// Package.swift
let package = Package(
name: "CalculatorModel",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "CalculatorModel",
targets: ["CalculatorModel"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "CalculatorModel",
publicHeadersPath: "include",
cSettings: [
.headerSearchPath("."),
]
),
.testTarget(
name: "CalculatorModelTests",
dependencies: ["CalculatorModel"]),
]
)
adding header file to proper subdirectory:
├── README.md
├── Package.swift
└── Sources
├── CalculatorModel
│ └── include
│ └── CalculatorModel.h
Try to use #import in your header file:
#import CalculatorModel;

Swift Package Manager, how to handle fixtures?

I'm looking to integrate DVR into my tests for an HTTP request-heavy library I am writing. I'm using Swift Package Manager (on macOS 10.15 using the Xcode 11 Beta) to manage my dependencies and I'm unsure how to include my fixtures generated by DVR into my test target. How do I add my fixtures from DVR to my test target?
I've tried using the default Session configuration as well as attempting to store the fixtures in a Fixtures directory using Session(outputDirectory: "Fixtures", cassetteName: "example", testBundle: .main, backingSession: .shared)
My package file is as follows:
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "Example",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "Example",
targets: ["Example"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/venmo/DVR.git", from: "2.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "Example",
dependencies: []
),
.testTarget(
name: "ExampleTests",
dependencies: ["Example", "DVR"]
)
]
)
When running my tests, I get the error [DVR] Persisted cassette at Fixtures/example.json. Please add this file to your test target
I'm unsure how to add this directory to my test target.
Looks like this may be impossible as of Swift 5.1. Swift ticket: SR-2866

How to add a .framework as dependency to a swift package in Xcode?

I want to know if there is anyway to link a swift package against a framework like SQLite.framework in Xcode? I'm trying to make a swift package for a sqlite library wrapper.
Here is my current swift package manifest:
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SQLiteDB",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "SQLiteDB",
targets: ["SQLiteDB"]),
],
dependencies: [
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "SQLiteDB",
dependencies: []),
.testTarget(
name: "SQLiteDBTests",
dependencies: ["SQLiteDB"]),
]
)
I ended up creating my own Sqlite package by embedding the amalgamation sources of sqlite. This gives you the ability to have any arbitrary version of Sqlite in your apps.
With the Xcode 12 beta, you can do this if you update your swift-tools-version to 5.3, and then add a binaryTarget to your package's targets:
.binaryTarget(
name: "Stripe",
url: "https://github.com/stripe/stripe-ios/releases/download/v19.3.0/Stripe.xcframework.zip",
checksum: "fe459dd443beee5140018388fd6933e09b8787d5b473ec9c2234d75ff0d968bd"
)

/src: error: could not find source files for target(s): MyKituraAppTests; use the 'path' property in the Swift 4 manifest to set a custom target path

I'm trying to compile using swift build
Package.swift
// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
// swift-tools-version:x.x
import PackageDescription
let package = Package(
name: "MyKituraApp",
dependencies: [
.package(url: "https://github.com/IBM-Swift/Kitura", from: "2.7.0")
],
targets: [
.target(
name: "MyKituraApp",
dependencies: ["Kitura"],
path: "Sources"),
.testTarget(
name: "MyKituraAppTests",
dependencies: ["MyKituraApp"],
path: "Test")
]
)
But, I get the following error although I did add the path property.
'MyKituraApp' /src: error: could not find source files for target(s): MyKituraAppTests; use the 'path' property in the Swift 4 manifest to set a custom target path
Without knowing your project structure I can't give you a definite answer but I'll do my best!
I'm going to assume you've generated your project using the Swift Package Manager tool, something like this:
swift package init --type executable
So... Typically you shouldn't need to set the path property unless you've moved the tests for your application to another directory. The Swift Package Manager, by default, will create a Tests directory and when you do not provide a value for the path property the Swift Package Manager will look for that Tests directory by default when you run swift build. In your path property you are providing a value of Test not Tests
So my first solution to test would be:
To remove the path property from the .testTarget section
OR
Rename the path properties value to Tests rather than Test.
I've provided an example Package.swift that I was able to run swift build with:
// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MyKituraApp",
dependencies: [
.package(url: "https://github.com/IBM-Swift/Kitura", from: "2.7.0")
],
targets: [
.target(
name: "MyKituraApp",
dependencies: ["Kitura"],
path: "Sources"),
.testTarget(
name: "MyKituraAppTests",
dependencies: ["MyKituraApp"])
]
)
As you can see I've also removed an extra line from the top of the file:
// swift-tools-version:x.x
You've already provided a swift-tools-version at the top of your file, this line may end up confusing things later down the line.
I hope this helps!