Make reachable public API members public - #2
Merged
Merged
Conversation
Several types are public and reachable from the public API, but members declared inside them default to internal, so a client can hold the value and not use it. Nesting a type in a public extension makes the type public without extending that to its own members, and a synthesized memberwise initializer is internal even for a public struct. Multiproperties.Layer is the clearest case: layerSequences hands out layers whose property and blendMethod cannot be read, which leaves multiproperties unresolvable outside the module. ObjectType.default is named by Object.type's own documentation but cannot be referenced. Alternative, Mesh.TriangleSet and Texture2DGroup.Coordinate can be read back from a parsed model but not constructed, though the public initializers that take them accept arrays of exactly those types. The new tests import ThreeMF plainly rather than with @testable, so they see the package the way a client does and stop compiling if any of this regresses.
Owner
|
Good catch! Merged, thanks. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes five members public that are already reachable from the public API but cannot be used from outside the module. No behaviour changes, no signature changes, no renames.
Multiproperties.Layer.property/.blendMethodlayerSequences, unreadableObject.ObjectType.defaultObject.type's own doc comment, unreferenceableMesh.TriangleSetTexture2DGroup.CoordinateAlternativeWhy
Two Swift rules combine here. Nesting a type inside a
public extensionmakes the type public but does not extend that to members declared inside the type. And a synthesized memberwise initializer is internal even when the struct is public.The result is a value a client can obtain and then do nothing with. Each of these is reachable from public API, so this is not a matter of taste about what should be exposed. The package already decided to expose them; the access levels just did not follow.
Multiproperties.Layeris the one that has practical consequences.layerSequencesis documented as the way to read a multiproperties resource as resolved layers, and it is the only such accessor. Outside the module it returns[[Layer]]where neitherpropertynorblendMethodcan be read, andLayerhas no public initializer either. So multiproperties cannot be resolved through the intended API at all. A consumer has to bypass it and pair uppropertyGroupIDswithmultisby hand, duplicating the logiclayerSequencesexists to provide, and losing the blend methods on the way. That is what I ended up doing while adding 3MF material support to Cadova (tomasf/Cadova#2), and it is the reason I went looking.ObjectType.defaultis a smaller but plainer contradiction.Object.typeis documented as "nilmeans the file doesn't say, which per the spec meansObjectType/model", which invitesobject.type ?? .default. That does not compile outside the module. The two sibling enums inTexture2Dalready get this right withpublic static let default.The three unconstructible types are all accepted by public initializers:
Mesh.init(vertices:triangles:triangleSets:)takes[TriangleSet],Texture2DGroup.init(id:texture2DID:displayPropertiesID:coordinates:)takes[Coordinate], andObject.init(...)takes[Alternative]. Today those parameters can only ever receive an empty array or values recovered from parsing an existing file, so writing a package with triangle sets, texture coordinates or alternatives is not possible from outside.Fixes
Four are a
publickeyword.Mesh.TriangleSetneeded an explicit memberwise initializer, since the synthesized one is internal regardless. Doc comments were added to the initializers to match the surrounding style.Tests
Tests/PublicAPIAccessTests.swift, five tests, one per symbol. The file uses a plainimport ThreeMFrather than@testable import ThreeMFon purpose, so it sees the module exactly as a client does. Every existing test file uses@testable, which is why none of this was caught.I wrote the tests before the fix and confirmed each one fails to compile against
main, with errors like:Full suite green after the fix: 142 tests.
Compatibility
Source and binary compatible. Widening access breaks no existing caller. Worth noting that it does commit
Layer's two field names and the three initializer signatures to the public API, so if you would rather shape any of them differently, now is the moment and I am happy to adjust.