Skip to content

Is header-translator viable for SDK usage code linting? #707

Closed
@complexspaces

Description

@complexspaces

At 1Password I've been trying to passively plan out a solution for resolving a lot of clunky code and tech debt in our "foundation" layer. Without making this sound like a product shill/pitch, I'll briefly explain the architecture and constraints. We have three layers of code in the iOS (macOS shares the lower two) app:

  • Frontend, which is written entirely in Swift
  • "Core" which is written entirely in Rust
  • "Foundation" which is a mixture of Rust and Swift code, compiled and linked in by a custom solution I designed and a larger xcodebuild produced .a file.

The FFI layer between the Core and foundation layer isn't great, primarily because the C ABI exposed by Swift is pretty limiting and cumbersome. For example passing state between them opaquely is fragile, returning errors with context out of deeply nested functions doesn't work well, etc.

To resolve this I'm hoping to do two things:

  • Use bridging classes (like mentioned in Use extern_class macro with classes declared in swift via @objc #642) for fairly complicated logic or Swift-only functionality where the files are still needed.
  • Convert a large amount of the existing Swift code to use the objc2_* family of framework crates and "inline" the FFI directly into the calling Rust code.

That last point is what I'm wanting to inquire on. A blocking concern I've found to adopting the objc2 crate family is deprecation and the liveness usually provided by compiling with swiftc from XCode and the most recent SDK in our build systems. For example, if we are using a function in Swift today that Apple decides to get rid of, we get an XCode-provided deprecation warning since its compiling against the latest SDK. If the same was used via objc2, we might not know until an arbitrary crate update in the future.

My workaround for this idea was developing a custom linting tool that scans the known callsites of Apple frameworks in Rust, collects the referenced symbols (functions, statics, and constants), and then parses their latest metadata from the current system's SDK. If anything deprecated (and not explicitly allowed) or missing is found, it raises an error to the developer or CI pipeline. For a while I wasn't sure how to do this but I found the header-translator project while looking at the other objc2 crates. I saw that it collects the deprecation information specifically and translates that nicely to Rust's built-in attributes but beyond that its semi-opaque to me.

The Rust collection side would need to be syn based, but do you think building a "diffing" tool like this would be feasible with the functionality offered by header-translator today? Could there potentially be a better way that doesn't damage performance which the framework crates themselves handled at build-time?

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-frameworkAffects the framework crates and the translator for themquestionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions