Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document how to add Swift files to a generated Swift Package #129

Open
bes opened this issue Dec 18, 2022 · 7 comments
Open

Document how to add Swift files to a generated Swift Package #129

bes opened this issue Dec 18, 2022 · 7 comments

Comments

@bes
Copy link
Contributor

bes commented Dec 18, 2022

I would like to read some documentation on what you think the best place for auxillary Swift files for the generated Swift Package is.

Might it be

  • In the directory of the "target Swift Package" itself (could be another folder / git)?
  • In some directory in the "source crate" using swift-bridge?
  • Some other place
  • Undefined

Is there some automatic facility for copying Swift files already in place?

@chinedufn
Copy link
Owner

Is this related #138 ?

@bes
Copy link
Contributor Author

bes commented Feb 5, 2023

I don't think so. But I might be wrong.

Since swift-bridge generates a Swift package, and since I then import the Swift package from my XCode project, I don't have access to the code inside the larger XCode project from the Swift package generated by swift-bridge.

To solve that, I use inversion-of-control - I declare a public protocol Proxy inside the Swift package, and then the main application can bind to the Swift package like so:

public protocol Proxy {
    // ...
}

var proxy Proxy?

public func registerProxy(_ newProxy: Proxy) {
    proxy = newProxy
}

So basically this requires custom code inside the Swift package generated by swift-bridge, but there is no documentation on how to maintain / manage that code.

Some options:

  • Maybe the Swift proxy code should live inside the Rust project?
  • Maybe the Swift proxy code should live inside the generated Swift project?
  • Or maybe swift-bridge doesn't care, and you can do whatever you like.

Either way, the most preferred way should be documented, maybe?

@chinedufn
Copy link
Owner

I don't have access to the code inside the larger XCode project from the Swift package generated by swift-bridge

I see, thanks for the example code.


I'd like to better understand your use case so that we can figure out the best solution.

Can you explain what you're trying to do, exactly?
Why does your Swift package need to know about the project that is using it?

@bes
Copy link
Contributor Author

bes commented Feb 6, 2023

Sure —

Here's the actual Proxy protocol

public protocol Proxy {
    func getAuthToken() -> String
    func getApiUrl() -> String
    func log(line: String)
}

The main Swift application supplies the Rust library with (amongst other things) an authentication token (on demand with short lifetime), the URL to the API server (can be different depending on dev/prod/region) and a logging facility so that I can easily mix Swift and Rust logs.

@bes
Copy link
Contributor Author

bes commented Feb 6, 2023

To spell it out even more, the sequence of events would be:

Rust needs auth token -> Calls getAuthToken through swift-bridge -> ends up in Swift package -> Swift package uses Proxy protocol to get actual token from main app

@chinedufn
Copy link
Owner

chinedufn commented Feb 6, 2023

Ok thanks I understand the flow now.

So, would something like this work for your use case?

swift-bridge-cli create-package --sources /path/to/RustApp.swift --sources /path/to/AnotherFile.swift

The RustApp.swift and AnotherFile.swift would get included in the generated Swift package.

We'd also add support for protocols in swift-bridge.

Here's a rough sketch of what I mean:

// lib.rs

#[swift_bridge::bridge]
mod ffi {
    extern "Rust" {
        type RustApp;

        #[swift_bridge(init)]
        fn new(api_provider: ApiProvider);
    }

    extern "Swift" {
        // This would tell `swift-bridge` that `ApiProvider` is a protocol.
        #[swift_bridge(protocol)]
        type ApiProvider;

        fn get_api_url(&self) -> String;
        fn get_api_token(&self) -> String;
    }

    extern "Swift" {
        // Corresponds to `class Logger`
        type Logger;

        #[swift_bridge(init)]
        fn new();
        fn log(&self, line: &str);
    }
}
// RustApp.swift

public protocol ApiProvider {
    func get_api_url() -> String;

    func get_token() -> String;
}

public final class Logger {
    func log(line: RustStr) {
        print(line.toString())
    }
}

Would this approach work for what you're trying to do?
Are there any ways that you would want to see it improved?


Another thing we can consider is letting users generated a protocol from swift-bridge.

#[swift_bridge::bridge]
mod ffi {
    extern "Swift" {
        // This would automatically generate a `protocol ApiProvider` with the given methods.
        // note the `declare_protocol` instead of `protocol`.
        #[swift_bridge(declare_protocol)]
        type ApiProvider;

        fn get_api_url(&self) -> String;
        fn get_api_token(&self) -> String;
    }
}

So, we may want to let you both import as well as declare protocols.

@bes
Copy link
Contributor Author

bes commented Feb 13, 2023

Technically I only need to add the Swift files to the project, so just having that would be a nice addition.

But all the other suggestions are nice as well, but not strictly necessary to solve my requirements 👍. Those things would definitely make swift-bridge more rounded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants