Swift Package Manager + example apps, can you do it?

This is a geek topic for people that care about how they are laying out their Swift projects.

I closely study the decisions AlamoFire makes in terms of project layout and other style decisions because I believe the project is top notch.

This inspires https://github.com/fulldecent/swift5-module-template, which is a template for anybody writing their own libraries. And this works with CocoaPods through their pod-template project.

With the new Xcode, I see File->New->Swift Package. And this works very differently than the past. Specifically, it gives you a straight folder, no Xcode project, and it does not have build targets. You cannot add targets inside it either.

Is it possible to distribute an example app inside a Swift PM module? And, is this the type of coding style that the AlamoFire project would look favorably on?

@mattt may be able to address the last question.

1 Like

The full git repo is downloaded, so anything in there is technically accessible. The complication is the files are locked, so opening an Xcode project/workspace results in numerous "this project is locked" dialogs.

1 Like

I am trying to figure out how the module and the example gets put into the same GitHub repo.

So is the idea to first make the package (File->New->Swift Package) and then go back in to make a project (File->New->Project) and put it in the same folder and then make the project dependent on the package (File->Swift Packages->Add Package Dependency)?

I spent my Apple Developer Incidents on getting that recipe right, and I maintain the file. So I am definitely looking to make this perfect so others can use it.

FYI I can only comment on my understanding of SPM, not what those behind AlamoFire would prefer.

One thing that tripped me up originally with Xcode's SPM integration is that a module is not an project so it it can't have targets. It doesn't build to anything, it's built by things. What Xcode 11 added support for is being able to work with a package using similar tools as a project, with Package.swift being analogous to the .xcproj file. That's what's happening when you do New Swift Package or when you open Package.swift directly.

SPM doesn't know/care about any project files, so as long as Package.swift is in the root directory, you're free to use a project or workspace however you like. Same for example projects and/or Playgrounds. If you're working with a project/workspace I believe you can either import the package locally into your project, or included the files manually.

What things ought to be done/included (readme, playground, example app, etc.) IMO depends on the complexity of the library you're developing. AF will choose a structure that fits their project. I don't know what they would recommend.

You should know that Alamofire's SPM support is just the bare minimum required to support building. We don't do any testing through SPM (since it doesn't support 2/3 of the testable platforms we support) and we don't use SPM as the basis of our project file. Alamofire predates the existence of SPM by over a year, and it was many years more before it could even be built using SPM. Our project layout hasn't changed much since the project was created and is only purposeful so far as supporting various things we've needed over time, like a docs folder for HTML documentation, since that's where GitHub requires it to generate a documentation site. It's unfortunate that SPM downloads all of this, as it's largely a waste of space.

But overall, I think supporting demo apps, documentation, and other meta project details in SPM is a good idea. Feel free to make a pitch for them, but I don't anticipate much overt support from the SPM developers.

I am experimenting with this layout:

28

To achieve this:

  1. File->New->Swift Package, then make your module

  2. File->New->Project, name it "iOS Example" and put in same folder

  3. Set up Git, git tag 1.0.0

  4. In the iOS Example, File->Swift Packages->Add Package Dependency, use file:////Users/path/to/directory

  5. Quit Xcode

  6. Use text editor to edit iOS Example/iOS Example.xcodeproj/project.pbxproj from:

    /* Begin XCRemoteSwiftPackageReference section */
    		D9B967CA235154E300C8DF91 /* XCRemoteSwiftPackageReference "FDSoundActivatedRecorder" */ = {
    			isa = XCRemoteSwiftPackageReference;
    			repositoryURL = "file:///Users/williamentriken/Desktop/FDSoundActivatedRecorder";
    			requirement = {
    				kind = upToNextMajorVersion;
    				minimumVersion = 1.0.0;
    			};
    		};
    /* End XCRemoteSwiftPackageReference section */
    

    to:

    /* Begin XCRemoteSwiftPackageReference section */
    		D9B967CA235154E300C8DF91 /* XCRemoteSwiftPackageReference "FDSoundActivatedRecorder" */ = {
    			isa = XCRemoteSwiftPackageReference;
    			repositoryURL = "../";
    			requirement = {
    				kind = upToNextMajorVersion;
    				minimumVersion = 1.0.0;
    			};
    		};
    /* End XCRemoteSwiftPackageReference section */
    
  7. Open Xcode, open the iOS Example, File->Swift Packages->Reset Package Caches

This seems to work, the iOS project is actually connected to the local copy. There is the unfortunate effect that only released versions of the module (i.e. Git tagged) are seen by the example project and surely this will cause somebody confusion during development of the library.

I would also rename the folder "iOS Example/iOS Example" to "iOS Example/Source" because a believe that is best practice, it is adopted by AlamoFire. There a multiple steps to do that and there is an Xcode bug, all documented in RECIPE.md at the top of the original post.


This is a possible path forward for me, for swift5-module-template, for all my projects, and for CocoaPods.

For AlamoFire and projects with tvOS targets and other advanced build settings it seems the old way (using *.xcworkspace) will be necessary for the foreseeable future.

This brings me to a head. I don't want to adopt this new way as a template if there is not a strong, real-world project like AlamoFire doing it this way ^^^.

Unless you want it referring to tagged versions, having it use the adjacent state is probably simpler. (If you do want to refer to a stable version, you would probably be better of referring to a canonical remote repository, since tags don’t necessarily propagate between clones.)

  1. Start with a valid swift package. ($ swift package init in a directory will create one if you are starting from scratch.)

  2. Create an Xcode project somewhere in the repository. (Or move an existing project there.) I usually collect such things in an Xcode/ subdirectory, so that it won’t interfere with swift package generate-xcodeproj if I need to use it. (There are still a few corner cases where double‐clicking the package manifest doesn’t quite work properly; they will likely be fixed over time though.)

  3. Drag the package directory—not the manifest—from finder into Xcode. Xcode will then contain a reference to the package as a relative path. (If it asks to create a workspace, because none exists yet, make sure you also put that into the repository.)

  4. Xcode targets can then depend on package products under “General → Frameworks, Libraries, and Embedded Content”.

1 Like

When I do this the Swift package inside the Xcode workspace also includes the example project. Is that correct?

I hadn’t noticed that. Under a particular package, Xcode also lists any miscellaneous files or directories irrelevant to the package but still located in its repository (such as LICENSE.md). It is in this section that those files appear. They aren’t used and don’t interfere with anything, but their appearance could be perceived as unwanted noise or as a source of confusion.

Terms of Service

Privacy Policy

Cookie Policy