What is a .swiftinterface file, and what does it show about @inlinable and @frozen?
Predict first
What does the interface show for each function?
public func plain(_ x: Int) -> Int { x * 2 }
@inlinable public func inline(_ x: Int) -> Int { x * 3 }
public struct Point { public var x: Int; public init(x: Int) { self.x = x } }
@frozen public struct Fixed { public var x: Int; public init(x: Int) { self.x = x } }
- Both bodies
- Only the @inlinable function’s body
- Neither body
Predict first
Which struct carries @frozen in the interface?
public func plain(_ x: Int) -> Int { x * 2 }
@inlinable public func inline(_ x: Int) -> Int { x * 3 }
public struct Point { public var x: Int; public init(x: Int) { self.x = x } }
@frozen public struct Fixed { public var x: Int; public init(x: Int) { self.x = x } }
- Point
- Fixed
- Both
Mental model. The interface file is the library’s public contract printed on one page. Anything not on it is private, and anything with its body on it is promised to stay that way.
A compiled .swiftmodule is a binary tied to one compiler version. A .swiftinterface is a textual description of a module’s public API, generated with library evolution on, so that a client built with a different compiler can still import it: the client’s compiler rebuilds the module from the interface. It is why an XCFramework built with one Xcode works in an app built with a newer one.
Reading it shows exactly what the library has promised. Ordinary public functions appear as declarations only, with no body, so clients can only call them. A function marked @inlinable has its body in the file, which clients may copy into their own code, so it must stay valid forever. A @frozen struct or enum appears with the attribute and its layout is part of the contract, while an ordinary public struct does not. When a review asks “what did this change break?”, diffing the .swiftinterface answers it.
Clients only see implementations the library has chosen to expose with @inlinable. The plain function is a declaration.
Only the struct you marked @frozen keeps the attribute and so fixes its layout in the contract. Point stays resilient.
Common traps
- Adding a body-visible
@inlinablefunction casually. The implementation becomes part of every client binary and cannot change. - Shipping only the binary module. Clients on a different compiler version cannot import it; ship the interface.
They might follow up with
- How do you generate one?
swiftc -emit-module-interface-path Lib.swiftinterface -enable-library-evolution, or the build setting that does the same in Xcode. - Can you edit it by hand? It is generated; changes should be made in source and the interface regenerated.
Related questions
- 5.4.1 What does -enable-library-evolution change, and why would you not turn it on for an app?
- 5.2.3 How does the inliner decide, and what do @inline(never), @inline(__always) and @inlinable do?
- 1.7.4 What are @usableFromInline and @inlinable in relation to access control?
← What does -enable-library-evolution change, and why would you not…