SE-0271: Package Manager Resources

Looks awesome! Can't wait to have this as it's one of the major blockages we have in migrating modules to SPM.

I have one question, but I'm not sure it would fit the scope of this. If the solution is to create a Bundle, it would mean you won't be able to access these assets from Storyboards, for example.

In CocoaPods the solution is separated to putting stuff in a bundle vs. copying the resources over to the host application so they can be properly accessed by things like Storyboards and XIBs. Do you make such distinction, or it would be up to consumers to find a solution to this?

Thanks!

2 Likes

The proposal mentions that tools such as Xcode are expected to integrate with this solution. I would guess that Xcode will have to adapt.

1 Like

Actually this seems to sort of address that point, but without many practical samples on how this is handled, which is why I asked specifically:

Tell me more, that sounds very interesting :)!

This is a proposal on the evolution side, shouldn't this require an implementation upfront? In general I do support the proposal because it finally fixes one of the major pain points of SPM, but I als would have wanted to play around with the new API before judging on its design.

1 Like

Obligatory text because posts can't just be a quote, for some reason.

3 Likes

I read this, but how is this different, SPM is part of the whole project?!

It's also kinda defeating own rules:


Ah there is at least a prototype implementation for this proposal available in the snapshot. However I still think SPM projects should require an implementation just as a regular swift proposal.

This is a very nice suggestion +1

This proposal is fantastic, but it does not help with any of my own nor with vendor libraries.

Most current packages already work fine but they cannot be localized. I honestly expected this problem to be addressed in the first place, and I’m sorry for not using storyboards, XIBs, Metal shaders and other “data” files with sophisticated processing workflows.

I’m really happy with considerations about Linux by the way, but then, in addition to localized resources, it would be nice to have support for Xcode asset catalogs on other platforms as well. In this case data assets would resolve other missed features.

Finally, it’s not clear at all how we will eventually have support for building Mac apps from Swift packages. The proposal seems to ignore this potential and extremely lacking feature altogether. Sorry again if I expected too much.

Maybe I’m wrong and this all will be possible, but we cannot know without an actual implementation. Thanks for listening!

2 Likes

As far as I understand the proposal:

  • If a Swift package is included in Xcode project for Mac, a newer version of Xcode would copy the resources into the app bundle. The module would not be aware of or care about the main app bundle, but Bundle.module would return Bundle.main behind the scene.

  • You will be able to use localized resources as usual:

I’ve just tested that NSLocalizedString does already work with Bundle on Linux if you place .lproj folder next to the executable.

I assume Asset catalog support on Linux is a question of support from Bundle in Foundation, not SwiftPM.

1 Like

On a second thought there might be file name clashing when including in the main bundle directly, so a separate resource only bundle would be nested in the main bundle, I guess.

I also assume that the Bundle.module would keep nested folder structure (e.g for .lproj’s) when copying.

1 Like

From the proposal, it is not clear if the original folder structure e.g. de.lproj / Localizable.stringsdict would be preserved or not. But from examples and Resource API, it looks like all files would be recursively processed the same as if they were included one-by-one. This is why localization is excluded from this proposal.

In my current project we currently make pretty liberal use of frameworks which we'd love to move to SPM packages and this proposal would alleviate the blocker we have from doing so, so I'm very happy to have this proposal hit evolution. I largely agree with the ideas and give them a +1!

We have similar Bundle extensions in our frameworks such as:

static var components: Bundle {
    class BundleFinder {}
    return Bundle(for: BundleFinder.self)
}

In an attempt to be proactive about testing this out, I renamed these properties to module akin to what SPM would be generating, so it becomes easier to adopt SPM in the near future. However, in a few of our tests, we use @testable to import a couple of frameworks with this property defined and the compiler gives an error of Ambiguous use of 'module' because for some reason they use this Bundle.module property themselves.

When I add a Bundle.module extension in the test target, the compiler is happy because (I figure) that supersedes the ones found in both frameworks (although I realise that will cause issues in this particular instance).

The proposal says the generation of the resources bundle only happens when there is one or more resources to put inside said bundle, so does this mean the Bundle.module extension is only generated when there is a a resource? If so this might cause issues for test targets with no resource files when they import more than one module using @testable that have resources in them.

Should/could the generated Bundle.module property be hidden from @testable imports to prevent test targets from being able to use it?

1 Like

I'm missing an option to customise the build settings.
I would like to use custom CIKernel's written in Metal in a Swift Package.
As explained in https://developer.apple.com/metal/MetalCIKLReference6.pdf in the Xcode Integration chapter, you need to add the -fcikernel compiler flag and a user-defined setting named MTLLINKER_FLAGS set to -cikernel.
With the current state of the proposal, it would not be possible or am I missing something?

2 Likes

Yah, we thought about this at some point and came to the same conclusion.

With this proposal, SwiftPM, the commandline tool will not do anything automatically w.r.t. localization, so NSLocalizedString would not work by default, because the strings files wouldn't be available at runtime. It might be possible to use the copy rule to the get those files in the right place, though, but it is not something we're explicitly designing for here.

IDEs using libSwiftPM could however automatically handle strings files if they choose, too, so e.g. potentially NSLocalizedString could just work when using packages in Xcode.

1 Like

Thanks for trying this out in such detail!

We decided to always generate the resource bundle accessor for test targets back on the pitch thread here.

1 Like

You're not missing anything, I don't think we were aware of the custom CIKernel feature. From the documentation, it sounds like in Xcode today, you have to do this partially via run script phases, is that correct? If that's the case, it sounds as if this would be something that potentially needs the extensible build tools feature pitched here.

1 Like

Overall I love this! A few thoughts:

  1. My main concern right now is one concerning syntax. This syntax doesn't seem to follow the existing Package.swift conventions. .library, .package, .target, etc all use nouns for their declaration. In this proposal, we have .process and .copy, which are verbs.

    A simpler API which more closely matches the other structs could be defined as follows:

    public struct Resource {
        public static func resource(path: String, process: Bool = true) -> Resource
    }
    

    Or, if we think that .copy and .process will grow to other ways in which to handle a resource, those could be defined in an enum:

    public struct Resource {
        public enum Rule {
            case process
            case copy
        }
    
        public static func resource(path: String, rule: Rule = .process) -> Resource
    }
    

    I don't love the naming of Rule, but it matches the existing terminology in the proposal.

  2. In the proposal, the comments around the Resource structure seem to be a bit misleading. One comment states that an instance of a Resource represents an individual resource file. However, the comments around process and copy both say that they can take paths to directories and that the rule will be applied recursively. It might be clearer to note that a Resource can define an individual resource file or a directory containing numerous resource files.

  3. Regarding the following:

    Each file in a target will be required to have a rule. SwiftPM will emit an error if it is not able to automatically determine the rule for a file. For example, if there is a README.md in the target, the package will need to make an explicit decision about this file by either adding to the exclude list or in the resources list.

    This seems to be a bit hostile to the package maintainer. I can envision this frustrating developers by breaking builds whenever a new README file is dropped into the project. Would it be preferable to simply ignore files without implicit or explicit rules? For example, all of the so-called “multi-purpose file types” listed in the proposal would be ignored and NOT be treated as resources by default. Why force the developer to explicitly exclude these when an implicit exclusion can work just as well?

1 Like

One thing that’s still not clear to me: is this also true on Darwin platforms? Will all packages with resources (or whose dependencies may have resources or localisation) have to be built with Xcode on macOS?