Tuist Project organisation, for editing in third party code editors with SourceKit-LSP

Question

Editing or developing a Tuist project in a third party code editors that utilises the SourceKit-LSP

Expectation

I expect the SourceKit-LSP to pick up the dependencies and so forth, like it does for a normal SPM project.
However, the only way I get the autocompletion or other LS Goodies in the manifest files, or the source files, is when I use tuist edit and tuist generate, (which is superb and very nice!). However if I open the file in other code editors, I get all these issues popping up, and sourcekit struggling to resolve the dependencies etc

Context

  • Tuist version: latest
  • Bare bone, blank Tuist project with one dependency
  • third party editors, Nova with Icarus, and Helix

Reproduction (mandatory for problems)

  • create a project by init, with ONE dependency
  • Open in third party editors
  • The manifest files will have issues due to dependency issues.
import ProjectDescription // for example
  • Add Alamofire as a dependency
  • Run tuist install
  • Include it in the main source code, and you will see issues regarding dependency resolution

My Current Hacky work around

  • I remove the Package.swift from Tuist/Package.swift and place it at the root ./
  • And also add the target, platform, dependency and product
  • run tuist install
  • run swift build

and everything seem to work fine in my third party editor

// ** Sample Package.swift at the root directory **\\ 

// swift-tools-version: 6.0
import PackageDescription

#if TUIST
    import struct ProjectDescription.PackageSettings

    let packageSettings = PackageSettings(
        // Customize the product types for specific package product
        // Default is .staticFramework
        productTypes: ["Alamofire": .framework]
//        productTypes: [:]
    )
#endif

let package = Package(
    name: "Playground",
    platforms: [.macOS(.v11)],
    products: [.library(name: "Playground", targets: ["PlaygroundApp"])],
    dependencies: [
        // Add your own dependencies here:
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
        // You can read more about dependencies here: https://docs.tuist.io/documentation/tuist/dependencies
    ],
    targets: [
        .target(name: "PlaygroundApp", dependencies: ["Alamofire"], path: "Playground/Sources/"),
    ]
)

Thanks!

1 Like

Hi @Alch3m1st

As you noted, the experience of editing your manifest feels degraded coming from editing a Package.swift.

I’m not very familiar with how code editors integrate with SourceKit-LSP. Still, if it works fine with Xcode projects, you should be able to integrate it with the project that Tuist generates for editing. We could explore what changes would be necessary to make that integration as smooth as possible.

If extensions don’t work well with Xcode projects, I’m afraid enabling the same experience outside of Xcode will be a bit tricky without a huge investment, which we can’t afford now.

1 Like

Ah! So I did a bit of digging, seems .xcodeproj etc are not used by the SourceKit-LSP directly. Even though Xcode also uses the LSP, it does some magic based on the manifest files putting everything in order, then kicking LSP somehow. :man_shrugging:

One last question! Is it really against convention (bad idea) if I bring Package.swift to the root dir and add a few details as described above? Basically, deleting the /Tuist dir.

What I realised is that, when I do swift build this way, the .swiftinterface file is cached somewhere for the sourcekit for the project.

The Tuist, build, generate as well as edit all seem to work fine as they use the Tuist manifest files such as Project.swift etc, and ignores all the extra bits I added :slight_smile:

If extensions don’t work well with Xcode projects, I’m afraid enabling the same experience outside of Xcode will be a bit tricky without a huge investment, which we can’t afford now.

Please not :laughing: ! The ideal is Apple just merging/buying Tuist, and scrap the ancient xcodeproj, xcodeworkspace etc! Or just fix the LSP, so it also parses and understands these directories…

// ** Sample Package.swift at the root directory **\\ 

// swift-tools-version: 6.0
import PackageDescription

#if TUIST
    import struct ProjectDescription.PackageSettings

    let packageSettings = PackageSettings(
        // Customize the product types for specific package product
        // Default is .staticFramework
        productTypes: ["Alamofire": .framework]
//        productTypes: [:]
    )
#endif

let package = Package(
    name: "Playground",
    platforms: [.macOS(.v11)],
    products: [.library(name: "Playground", targets: ["PlaygroundApp"])],
    dependencies: [
        // Add your own dependencies here:
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
        // You can read more about dependencies here: https://docs.tuist.io/documentation/tuist/dependencies
    ],
    targets: [
        .target(name: "PlaygroundApp", dependencies: ["Alamofire"], path: "Playground/Sources/"),
    ]
)

Not at all. In fact, having the Package.swift at the root is also a valid place for the file. So if it works for you, I recommend it to have it there. That’s the reason why all the commands work :).

:laughing: We’d love to take part in designing an evolution for that format. People are trying to use SwiftPM and Swift Packages to “escape” the issues with Xcode projects and workspaces, but it’s not the solution to the problem either, and they now find themselves having to reconcile the two worlds. We’ll see what the future holds, but they’ll have to do something if they want anything that depends on it (e.g. LLDB, SwiftUI previews…) to work reliably. And if they so, they should take the opportunity to decouple the layers so that people can create coding experiences outside of Xcode.

1 Like

Thanks. That works for a macOS project. For other platforms, this works like a charm: GitHub - SolaWing/xcode-build-server: a build server protocol implementation for integrate xcode with sourcekit-lsp

I run it after tuist generate.

1 Like

Thanks @ba1mn for sharing it. I had no idea about this project. Are you happy with the experience?

We try to use this as well (Cursor/Copilot are essentially mandatory at this point), it works generally ok but in our case the “linting” (really, compiler errors) do not auto-update as they should or would in Xcode, so the LLM cannot always do the “try something, fail, fix errors” autonomous loop which it can do with other languages like typescript that have better native support from Cusor/VSCode. What would be ideal (in the “pie-in-the-sky” sense) from my perspective is:

  • You have something like tuist managing the project
  • Tuist and the build system are tightly integrated with LSP via some VSCode extension to make them “first class” participants in the compile-lint-fix cycle.
    Right now it’s somewhat kludgy in that we need to often manually build in order to update the linter status, or instruct the LLM to do slow command-line builds after every change.

I’m hoping that swift-build will somehow eventually provide a magic bullet here so xcode can be taken out of the loop entirely.

Hi @pepicrft, yes, it works perfectly. The only small inconvenience is that, at the start of a project, a preview or build has to be run once in Xcode to kick off indexing, since sourcekit-lsp relies on the index store for its index.

Hi @dk-jlew, hopefully the Experimental Background Indexing gets extended to non-SwiftPM projects.

This would be amazing. The unification of the build system with swift-build should hopefully make that easier.