Making a dummy package.swift so I can edit my iOS app's code in an editor that works

I want to be able to edit my Kotlin Multiplatform Mobile app's swift code in VS Code and/or vim. XCode is too limited and buggy, with error highlighting not really working at all at the moment, which is fairly dire.

I've created a dummy Package.swift to try to make this possible. VS Code/sourcekit_lsp needs a successful build before completion etc will work, and I've run into a couple of problems with it.

One problem is this:

error: the library 'GCios' requires macos 10.13, but depends on the product 'GoogleSignIn' which requires macos 10.15; consider changing the library 'GCios' to require macos 10.15 or later, or the product 'GoogleSignIn' to require macos 10.13 or earlier.

It's no good adding .macOS("10_15") to my platforms argument, because that makes it try to generate macos versions of all my code, which obviously won't work because it contains iOS specifics. There doesn't seem to be any way to disable the macos support in the Google package other than forking it; and I'm worried that even then, merely removing the macos platform from its package.swift will not work, and I'll have to laboriously remove all the macos stuff from multiple source files.

The second problem is with the compiled Kotlin, which is output as a framework. I've tried adding its path to the swift command line:

GC_FW_PATH="$SRCROOT/GCshared/build/xcode-frameworks/Debug/iphoneos16.4/"
GC_SHARED="$GC_FW_PATH/GCshared.framework"
swift build --arch arm64 -Xswiftc -sdk \
    -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" \
    -Xswiftc "-target" -Xswiftc "arm64-apple-ios15.5-simulator" \
    -Xswiftc "-I" -Xswiftc "$GC_SHARED/Headers" \
    -Xswiftc "-L" -Xswiftc "$GC_SHARED" \
    -Xswiftc "-l" -Xswiftc "GCshared"

but I still get a volley of errors (from my import statements) saying the module can't be found. I also tried adding it as a binary target in Package.swift:

    .binaryTarget(
      name: "GCshared",
      path: "../GCshared/build/xcode-frameworks/Debug/iphoneos16.4/GCshared.framework"
    )

but that causes another error:
error: 'gcios': unsupported extension for binary target 'GCshared'; valid extensions are: 'zip', 'xcframework', 'artifactbundle'

There might be a possible solution in the form of a gradle plugin that makes Kotlin Multiplatform Mobile generate a swift package instead of a framework, but I'd like to see if there's a quick fix for the current system first.

The full Package.swift, including the attempted workarounds that don't work:

// swift-tools-version:5.6
import PackageDescription
let packageName = "GCios"
let package = Package(
  name: packageName,
  platforms: [.iOS("15.5"), .macOS("10.15")],
  products: [
    .library(name: packageName, targets: [packageName])
  ],
  dependencies: [
    .package(url: "https://github.com/firebase/firebase-ios-sdk.git",
      from: "10.9.0"),
    .package(url: "https://github.com/facebook/facebook-ios-sdk.git",
      from: "16.1.0"),
    .package(url: "https://github.com/google/GoogleSignIn-iOS",
      from: "7.0.0"),
    .package(url: "https://github.com/grpc/grpc-swift.git", branch: "main"),
  ],
  targets: [
    .target(
      name: packageName,
      dependencies: [
        .product(name: "FacebookCore", package: "firebase-ios-sdk"),
        .product(name: "FacebookLogin", package: "firebase-ios-sdk"),
        .product(name: "FirebaseAuth", package: "firebase-ios-sdk"),
        .product(name: "FirebaseMessaging", package: "firebase-ios-sdk"),
        .product(name: "GoogleSignIn", package: "GoogleSignIn-iOS"),
        .product(name: "GRPC", package: "grpc-swift"),
      ],
      path: packageName,
      exclude: ["Common/TryObjC.h", "Common/GCios-Bridging-Header.h"]
    )
    ,
    .binaryTarget(
      name: "GCshared",
      path: "../GCshared/build/xcode-frameworks/Debug/iphoneos16.4/GCshared.framework"
    )
  ]
)

My build script:

#!/bin/sh
cd "$(dirname $0)"
IOSROOT="`pwd`"
cd ..
SRCROOT="`pwd`"
cd $IOSROOT
GC_FW_PATH="$SRCROOT/GCshared/build/xcode-frameworks/Debug/iphoneos16.4/"
GC_SHARED="$GC_FW_PATH/GCshared.framework"
swift build --arch arm64 -Xswiftc -sdk \
    -Xswiftc "`xcrun --sdk iphonesimulator --show-sdk-path`" \
    -Xswiftc "-target" -Xswiftc "arm64-apple-ios15.5-simulator" \
    -Xswiftc "-I" -Xswiftc "$GC_SHARED/Headers" \
    -Xswiftc "-L" -Xswiftc "$GC_SHARED" \
    -Xswiftc "-l" -Xswiftc "GCshared"

This isn't really the case. SPM packages support all Swift platforms by default and there really isn't a way to disable that. Best thing you can do is put a bit in your code which produces a build error when building for platforms you don't support.

#if os(macOS) || os(Windows) || os(Linux)
#error("<Package> does not support building for this OS.")
#endif

However, there are two fundamental issues here. First, swift build doesn't support building for iOS. You can try to trick it, as you've done, but that's not really something that's supported. You'll want to use xcodebuild. Second, you're trying to use a framework for iOS support. You need an xcframework so you can support not only the different architectures but simulator and device platforms.

I don't see how that would help. It will still produce an error that will stop the build, won't it? Surely there must be some way to stop SPM from trying to build for systems you don't want to support, just because a dependency supports it.

First, swift build doesn't support building for iOS.

What if I wanted to write a program for Linux which depended on a cross-platform Swift package that included iOS support? It wouldn't be possible, because the iOS build would simultaneously be compulsory and unsupported.

You can try to trick it, as you've done, but that's not really something that's supported. You'll want to use xcodebuild.

Will that still produce the index files or whatever that the VS Code plugin and/or sourcekit_lsp need? What is the syntax to make it do that?

Second, you're trying to use a framework for iOS support. You need an xcframework so you can support not only the different architectures but simulator and device platforms.

I didn't realise frameworks and Xcframeworks were two distinct things. The gradle plugin I was considering apparently outputs an XCframework as well as a Package.swift for it, so it looks like that's the answer to that problem.

I didn't realise frameworks and XCFrameworks were two distinct things.

Yeah, our platforms, like most platforms, have a bunch of jargon. For linker and library topics, I have An Apple Library Primer over on DevForums.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

2 Likes

I think one issue here is that passing an override for -target to the Swift compiler doesn't affect what SwiftPM thinks you are building for, so that will still be macOS.

It is also important to note that we do not support using swift build for any Apple platforms besides macOS. Building for these platforms is only supported in Xcode and xcodebuild.

1 Like