How do you build iOS specific packages using SPM? - swift

I am using Swift Package Manager that doesn't having a xcodeproject file associated with it and I get an error when building through he terminal. When I call the swift build command I get an error that the MacOS build failed. The package I'm building doesn't support MacOS (It uses UIKit), but only iOS. I can't figure out a way to call the command to only specify that the build is targeted for iOS. I've Google searched around with no luck. Does anybody know the correct syntax if it exists to build an SPM package for iOS from the terminal?
The version of Swift I'm using is: "Apple Swift version 5.2.4 (swiftlang-1103.0.32.9 clang-1103.0.32.53)"
I have specified the platform in the Package.swift file
let package = Package(
name: "Package",
platforms: [.iOS(.v10), ],
products: [
...
I have created a sample project on github https://github.com/mike011/Swift-Package-Manager-Example. When I run swift build it fails with
/git/Swift-Package-Manager-Example/Sources/Swift-Package-Manager-Example/iOSSpecificFile.swift:9:8: error: no such module 'UIKit'
import UIKit
^
/git/Swift-Package-Manager-Example/Sources/Swift-Package-Manager-Example/iOSSpecificFile.swift:9:8: error: no such module 'UIKit'
import UIKit
^

I "solved" this by wrapping all my files in #if !os(macOS) #endif blocks. So the package builds on a Mac, but it doesn't have any content.

SwiftPM doesn't currently have a way to disallow building for a specific platform but if you want you can take advantage of the minimum build version to cause compile time errors on platforms you don't support.
For example if you don't want to allow building on macOS you can use the platform version: .macOS("99.0") in your platforms section of your manifest and you will be given the compilation warnings and errors similar to this when building in Xcode:
The macOS deployment target 'MACOSX_DEPLOYMENT_TARGET' is set to 99.0, but the range of supported deployment target versions is 10.8 to 10.16.99
Invalid Darwin version number: macos99.0
Invalid version number in 'target x86_64-apple-macos99.0'

Related

XCode 14 - Minimum deployment target for SPM package

I am actually compiling for macOS 10.12, as stated. I import the Reachability.swift package, that is compatible down to 10.9. No minimum version is indicated within the Package.swift file. However, I get this error while compiling :
Compiling for macOS 10.12, but module 'Reachability' has a minimum
deployment target of macOS 10.13
It happens since I updated to XCode 14, with version 13, there was no problem.
If I specify the minimum version in the Package.swift of Reachability platforms:[.macOS(.v10_10)],, I clean all the caches, I get the same problem.
I could probably upgrade deployment target to 10.13 (same computer compatibility) or simply include the Reachability.swift file in my code, but I would rather that it works...
[EDIT] Note : other packages, like AppCenter, do not have the same problem… which is weird.
[EDIT 2] With the release of XCode 14.1, the problem remains the same.
Do you have any suggestion ?
I can reproduce your problem: Using AppCenter is OK, but the reachability throws an error.
Here is what I found and maybe this is the root cause:
Make Reachability a local SPM, and modify its swift-tool-version settings and platform settings to align with AppCenter.
Then if you click into the .v10_12 MacOSVersion, you will find os versions lower than 10.13 has all been marked as unsupported
If you head to the built files, test it with otool, you will find the built product's minimum supported version matches with the above description:
So even if you specify the minimum support version to 10.12, the build tools will still target 10.13.
If you check LC_MIN_VERSION_MACOSX on AppCenter. It is also targeting 10.13 instead of 10.12 in its package.swift file.
But why Xcode accepts AppCenter and raise an error for Reachability?
Check the build log below. You will find it in the emitting module stage, the build system is using swift-frontend to embed those 3rd party dependencies. So I guess since the AppCenter is written in objective C.
The checking minimum os version behavior of swift-frontend is different between OC Dependency "AppCenter" and Swift Dependency "Reachability."

Swift Version Conflict: this SDK is not supported by the compiler - using BUILD_LIBRARY_FOR_DISTRIBUTION setting. What could be the issue?

I'm building a Swift static library with:
Xcode 13.2 (Swift compiler 5.5.2)
iOS Deployment target 12.0
Build library for distribution YES
Skip install NO
Swift language version 5 and tried with 4.2
Dependency managed with CocoaPods:
Japx 3.0.0
Alamofire 5.2.2
RxSwift
When I try to add it to an iOS project with this setup:
Xcode 13.3 (Swift compiler 5.6)
iOS Deployment target 12.0
Swift language version 5 and tried with 4.2
and build I get this error:
Failed to build module 'LibraryX'; this SDK is not supported by the compiler (the SDK is built with 'Apple Swift version 5.5.2 effective-4.2 (swiftlang-1300.0.47.5 clang-1300.0.29.30)', while this compiler is 'Apple Swift version 5.6 effective-4.2 (swiftlang-5.6.0.323.62 clang-1316.0.20.8)'). Please select a toolchain which matches the SDK.
These are the headers in the .swiftinterface
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.5.2 effective-4.2 (swiftlang-1300.0.47.5 clang-1300.0.29.30)
// swift-module-flags: -target arm64-apple-ios12.0 -enable-objc-interop -enable-library-evolution -swift-version 4.2 -enforce-exclusivity=checked -Onone -module-name LibraryX
Obviously if I build the project with the same compiler version or building the library with the iOS project everything works fine. But the purpose here is to release a precompiled library and not its source code.
Every time that I lookup this error online I've found "You have to set Build library for distribution to Yes"
I tried to add the source code of the static library to a new Framework project, but I get the same result.
I also had this error, in my case it was unable to import a sub-dependency of my XCFramework during reading .swiftinterface file, however was showing this absolutely misleading error.
After repeatedly cleaning up DerivedData and trying different Xcode versions (13.0, 13.4.1) I've seen 2 errors: "this SDK is not supported by the compiler", and "No module named 'AnotherLib'" which was an incorrectly specified dependency inside of my Swift Package.
So, maybe only first part of the message is correct "Failed to build module 'LibraryX'", but the reason is something else, NOT the difference in compiler versions.

How to use CoreML Model in a Swift Package Manager macOS Command Line App?

I'm trying to create a command line app that runs on macOS and uses CoreML. I am using Swift Package Manager. Here are some steps to reproduce the problem:
Run
swift package init --type executable
Move a CoreML model into the Sources folder, for example, CNNEmotions.mlmodel
In main.swift, write
print(CNNEmotions()) // CNNEmotions is the name of the generated model type
I know that CoreML only runs on macOS and iOS., so in Package.swift, I added a platform parameter:
let package = Package.init(
name: "MyProject",
platforms: [
.macOS(.v10_13), // this is the minimum macOS version that supports CoreML
],
...
Run
swift build
And there will be an error saying that CNNEmotions is an unresolved identifier.
If I do swift package generate-xcodeproj and open up the project in Xcode, I can see that the .mlmodel file is not added to the project at all!
This person on Apple Forums seems to also be in the same situation as me, but there are no answers to their question...
How can I make SPM work with mlmodels?
Just when I thought it's the end of the world, I found that if I add the .mlmodel file into the project in Xcode, the program can compile and run successfully. But the problem with building it in Xcode is that it's quite inconvenient. It doesn't produce an executable in a convenient location (I have to go all the way to the DerivedData folder to find it). To be honest, I would just like a convenient command to build & run it, like swift build. This is my ultimate goal.

Swift Package Manager - Speech dependency not loading

I'm encountering an issue creating a Swift Package for my project. I have CocoaPods and Travis CI running and both are working swimmingly, however I now intend to also offer the project through the Swift Package Manager. This is where I'm running into issues. My package file is looking like the following:
// 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: "Voxosonus",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "Voxosonus",
targets: ["Voxosonus"]),
],
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: "Voxosonus",
path: "Voxosonus"),
.testTarget(
name: "VoxosonusTests",
dependencies: ["Voxosonus"],
path: "VoxosonusTests"),
]
)
However when I run swift build I encounter the following:
/Users/<hidden>/Documents/Projecten/Voxosonus/Voxosonus/SpeechRecognizer.swift:8:8: error: no such module 'Speech'
import Speech
My question is - why is it unable to find the 'Speech' module? This is core functionality from Apple themselves and so far the internet has made me none the wiser. Some details on the project:
Build target: iOS 12.x
macOS: 10.14
Swift Version: 4.2.1
As explained in the Swift Package Manager Github, you can't define the target platform of a Swift Package yet:
At this time there is no explicit support for depending on UIKit,
AppKit, etc, though importing these modules should work if they are
present in the proper system location. We will add explicit support
for system dependencies in the future. Note that at this time the
Package Manager has no support for iOS, watchOS, or tvOS platforms.
So when you are try to build the library with Xcode (or with the swift build command), the compiler can't find the Speech module because it is only available on iOS 10+.
You can check this github project (swift-package-manager-ios) which provides a ruby script that modify the xcodeproj generated by the Swift Package Manager by adding the necessary information in order to build for the iOS platform.

Swift Package Manager - UIKit Dependency

I have a Package.swift in my project like:
import PackageDescription
let package = Package(
name: "ProjectName",
dependencies: [
.Package(url: "https://github.com/example/repo.git", majorVersion: 0)
]
)
When I run swift build I get errors like…
/project/Packages/WebViewController.swift:1:8: error: no such module 'UIKit'
import UIKit
^
Where should I tell the swift package manager where to find UIKit?
You have to change some swiftc options to build the project against proper sdk and target
swift build -Xswiftc "-sdk" -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" -Xswiftc "-target" -Xswiftc "x86_64-apple-ios13.0-simulator"
Make it work without limit the platforms:
You should select an iOS-based target to make it available:
If you leave it selecting macOS (by default), you will get the error.
Limit to a specific platform
if you want your package to be available only for specific platforms (for example only for iOS), you should specify the platform in the package.swift file:
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v10)
],
products: [
,,,
Support Multiplatform
If you need your framework to be available on multiple platforms, don't forget to check the availability of the imported framework like:
#if canImport(UIKit)
import UIKit
#endif
Currently Swift Package Manager has full Xcode support. I was able to get around this error by specifying in my Package.swift manifest that the platform was iOS.
let package = Package(
name: "MyPackage",
platforms: [
.iOS(.v8)
],
Then you can open the Package.swift file in Xcode and it will just work.
The Swift Package Manager builds executables to run on OS X (or Linux); UIKit is a framework in iOS and won't be accessible.
It may be iOS, tvOS and others become accessible as Swift Package Manager evolves.
On Dec 4, 2015, at 5:39 PM, Daniel Dunbar (#apple.com) wrote:
...
Right, now we only compile for the host platform (OS X or Linux, currently). Among other things, we currently have no knowledge (or options to choose) what SDK or architecture you are targeting. We also have no mechanisms for specifying what platforms targets are compatible with in the manifest.
Make sure you select an iPhone as a simulator target. a Mac target is the default and that won't work...It would be awesome if Xcode could look at the manifest and choose a default simulator based on that...
Use conditional compilation blocks:
#if canImport(UIKit)
// Code specific to platforms where UIKit is available
#endif
Source: https://developer.apple.com/documentation/xcode/creating_a_swift_package_with_xcode
If your umbrella header(<module_name.h>)[About] contains
#import <UIKit/UIKit.h>
it means that you can skip specifying import UIKit in every file. But it seems that SPM doesn't support it. Also I am not a supporter of using #_exported attribute
You can write import UIKit explicitly