Closed heckj closed 1 year ago
I was thinking about good naming for this Zoo of dependencies and packages, but I don't have anything good to propose for now. I will post to this thread once I have something.
Regarding the consumption goal: if I am getting it right, the way you described it (just adding dependency to Package.swift
file) is exactly the way it works now:
https://github.com/y-crdt/y-uniffi/blob/main/yswift/Tuist/Dependencies.swift#L4-L6
The only difference being that we are importing by the path right now, but it will change once we split y-uniffi
to the separate repo.
Maybe Tuist/Dependencies.swift
file confuses things. But it basically is an abstraction over Package.swift
, Cartfile
(for Carthage) manifest files.
E.g. I can create a new vanilla project and just add the dependency from lib
folder by the path. Note that I am saying dependency, because lib/
folder contains SPM Manifest file lib/Package.swift
Let me know if I am missing on something here
That's definitely the way it's working now, with Tuist specifically - but the primary difference there that what's set up only works with a local library reference. That's perfect for where we are: learning, experimenting, and supports very quickly iterating on changes.
What the current setup doesn't yet do is support external, versioned releases - for which I'm noodling nailing down and trying a process to make a release so that it can be used externally - and that same setup can be used to host documentation with the Swift Package Index, with multiple versions tracked based on major version, etc. I think this was a goal all along, I just wanted to spell it out to make sure. And to start the discussion around the soup of names, as you noted.
I don't have a strong opinion about the internal names for the C FFI library - The double Y prefix works perfectly fine for me, but I would like think a bit more explicitly about the type names as they're exposed in Swift. The primary reason there is Swift has no real namespace control, and Y-CRDT is already using common terms for its internal components. This wouldn't be any sort of issue at all if there was better namespacing, but alas - it's the world that is.
To illustrate, let me present a hypothetical setup. Within the Y-CRDT swift module, if a versioned branch of the CRDT has a data structure named View
, and you were working with another module that also had View
(for example, SwiftUI
), then in the code it would far less than clear which view type you were referring to. As a general pattern, quite a number of Swift libraries (mostly from the Objective-C days with a similar problem space) prefix their most common terms to make the type names unique, and to some extent Yjs has already been doing that - and I think we definitely want to continue that setup.
We've got YDocument
, YProtocol
, and YArray
already, and I think it makes good sense to stick with those type names to make it clear they're the wrapped Y-CRDT types (and extend that to YMap
when I "get off my duff" and dive into that bit of the Rust/UniFFI setup).
After bungling adding macOS support to the XCFramework, backing it out, and adding CI to try and save myself from future missteps, I think I've got a path forward. There are a few key constraints here though, so the output of this effort could change how and where we're doing things in this repo.
First off - the current setup with build-xcframework.sh
and tuist
ends up creating a chain of 3 module targets, and another for the swift example iOS app code:
YniffiXC <- Yniffi <- YSwift <- iOS Example code
The first two are build by build-xcframework.sh
in the lib
directory:
YniffiXC
)yniffi.swift
) resulting in the module Yniffi
, defined in Package.swift
in the lib
directoryThe last (YSwift) is formed by Package.swift
in the yswift
directory of this repository.
(My earlier mistakes and confused were in incorrectly assuming there was only a single swift package that used the associated C FFI)
Using uniffi
ends up setting a constraint: the generated swift scaffolding is tightly coupled to the headers and static libraries in terms of specific symbol names. In practice, this means that any time we iterate on the underlying dependencies such that we need to rebuild the static library, we'll want to use the associated swift code that the uniffi process generates as well.
Another constraint comes from Swift itself: In order to use a repository as a remote package reference, the Package.swift
is expected to reside in the root directory. The mono-repo setup of two swift packages (and associated example code) doesn't easily allow for an external developer to be able to add the package dependency and get the related binary bits that we release.
I'm also taking our fast iteration using all local packages as an exceedingly useful setup, and want to preserve that setup so that we can continue to iterate on exploring the swift language binding construction without being forced to do a bunch of explicit releases.
Based on all that, I'd like to propose the following:
yswift
and maintain the tuist
configuration to load packages from the other directories in the repository.Yniffi and YSwift
) by moving the swift code (and the Package.swift that defines the module) to the root of this repository. YCRDT
uniffi-bindgen
creates into the repository (I just removed it) since it's tightly coupled to the symbol names in the associated XCFramework C static libraries.When we get to point where we want to release this code for use to external swift developers, we would need to maintain a coordinated, coupled release of both the .xcframework
binary target and the associated swift package YCRDT
, updating the Package.swift
in the root repository from a local XCFramework to using a remote XCFramework that we reference and host from Github as a release. The process for that release would end up needing to look something like:
./build-xcframework.sh
Yniffi
and YSwift
), with Yniffi
only containing the code generated from uniffi-bindgen
. We could still move this work (effectively, what's in the lib
directory) to the root of the repository so that external packages can depend on it. Then migrate the developer-generated additional swift code (such as YArray
, YDoc
, etc - the stuff in the YSwift
module today) into a separate repository. I still think it would be valuable to rename the module generated from YSwift
to YCRDT
to reflect that a developer is importing the language bindings that they can then directly use. The primary benefit of this alternative would be to separate the release process of the rust-based code (and any dependency updates) from the swift-based code.
The reason that I am preferring YSwift
– because it follows the pattern of yjs
, yrs
, ywasm
, yrb
, ypy
and it is kind of natural to assume that Swift binding follows the same pattern. Eventually all of that projects fall under Y organization. Which is kind of cool umbrella-unification.
Historically GitHub organization that current repository reside in was called y-crdt
, and I think it is also a good name something that serves umbrella purpose, as well as something like y-org
.
I still think it would be valuable to rename the module generated from YSwift to YCRDT to reflect that a developer is importing the language bindings that they can then directly use.
I honestly think that importing YSwift
reflects intention of Swift language binding to Y-thing better than YCRDT
, which feels more generic and might imply that you are importing something more low-level than Swift language binding.
Intuition that I have packaging could be phrased as – optimize for simplicity and ease-of-use for now, until we cover core functionality and be ready to cut v1 release.
Basically I am for moving everything uniffi-related to separate repository, which will produce artifacts for Swift and Kotlin as part of its release process. If I am getting it right, it is what you've suggested as Alternative approach.
So there would be 3 repos (omit the names): y-uniffi
, y-kotlin
and y-swift
:
y-uniffi
repo will contain: Rust-wrapping code and generate language-specific artifacts (.xcframework
and gradle module)y-swift
(and y-kotlin
I hope) will contain: YArray
, YDoc
and etc (stuff in today's YSwift
)And there would be 2 reasons to change & release the final package (YSwift
/YCRDT
) that will be consumed by users:
yrs
. Something changes in yrs
→ we change that in y-uniffi
and produce artifact → we release new version of the package (YSwift
/YCRDT
) with that updated artifact.y-swift
and we cut new release.That's how I see it, happy to discuss more!
Btw, feels so great to have someone to discuss all these details with. Thanks Joe! Amazing attention to details, and very neat write-up here!
Sounds good. YSwift it is then! There's some generated code from the uniffi-bindgen that we'll need to continue to include in the y-uniffi repo - so that repo will need to have not only the C static libraries but also a small bit of swift in a Swift Package to go along with it. I haven't yet looked at anything equivalent in the Kotlin space to have any sense of how that platform manages its modules.
I'd like to see the end result of what we create here be usable through Swift Package Manager - the idea being that someone could add a dependency to "Y-CRDT" (or whatever we call the module), and have that automatically pull in the dependencies that we create and make available from this repo - the swift overlay and the underlying static C libraries.
The target for something like this would be to allow a developer to add a dependency to their Package.swift file:
and from there be able to add an import that makes YDoc available in the code:
Along these lines, what do we want the various names to be that are used here? There are three we need to nail down:
1) repository to use for the C library/XCFramework dependency (currently
y-uniffi
) 2) a repository for the swift overlay dependency (also currentlyy-uniffi
, but only because it's all in the same place - I don't know how possible it is to have two different dependencies served from the same repository) 3) The module name that a developers imports to use the library (currentlyYSwift
- but I'd like to suggestYCRDT
instead)