Last active
June 1, 2024 20:26
-
-
Save AlexKobachiJP/7b6737fdb2ae3c8cb751f0f7a45404cd to your computer and use it in GitHub Desktop.
Sort file and package references in an Xcode project.
This file contains 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
// Copyright © 2023 Alex Kovács. All rights reserved. | |
import ArgumentParser | |
import Foundation | |
import PathKit | |
import XcodeProj | |
// See below for `MainCommand` skeleton. | |
extension MainCommand { | |
struct XcodeCommand: ParsableCommand { | |
static var configuration = CommandConfiguration( | |
commandName: "xcode", | |
abstract: "Process Xcode project files.", | |
subcommands: [ | |
SortCommand.self | |
] | |
) | |
} | |
} | |
extension MainCommand.XcodeCommand { | |
struct SortCommand: ParsableCommand { | |
static var configuration = CommandConfiguration( | |
commandName: "sort", | |
abstract: "Sorts file and package references in the project.", | |
discussion: """ | |
- Files in the navigator are sorted by filename, with groups always appearing first. | |
- Files in target build phases are sorted by filename. | |
- Packages in the project's package dependency list and in target framework build phases lower-case the name for sorting. | |
""" | |
) | |
@Argument(help: "The path to the Xcode project to process.") | |
var project: String | |
func run() throws { | |
let path = Path(project) | |
let xcodeproj = try XcodeProj(path: path) | |
// Let the framework handle what it does. | |
let settings = PBXOutputSettings( | |
projNavigatorFileOrder: .byFilenameGroupsFirst, | |
projBuildPhaseFileOrder: .byFilename | |
) | |
try xcodeproj.write(path: path, override: true, outputSettings: settings) | |
// Now massage the parts the framework doesn't handle according to our needs and save again. | |
xcodeproj.pbxproj.sortPackageDependencies() | |
xcodeproj.pbxproj.sortFrameworksBuildPhases() | |
try xcodeproj.write(path: path, override: true) | |
} | |
} | |
} | |
extension PBXProj { | |
/// Sorts the list of packages in the project's Package Dependencies tab alphabetically. | |
func sortPackageDependencies() { | |
if let packages = rootObject?.packages { | |
rootObject?.packages = packages.sorted { | |
// Without lower-casing, "SomePackage" will come before "a-package". | |
if let lhs = $0.name?.lowercased(), let rhs = $1.name?.lowercased() { | |
return lhs < rhs | |
} | |
return true | |
} | |
} | |
} | |
/// Sorts the list of packages in a Link Binary with Libraries build phase. | |
func sortFrameworksBuildPhases() { | |
for phase in frameworksBuildPhases { | |
if let files = phase.files { | |
phase.files = files.sorted { | |
// Without lower-casing, "SomePackage" will come before "a-package". | |
if let lhs = $0.product?.productName.lowercased(), let rhs = $1.product?.productName.lowercased() { | |
return lhs < rhs | |
} | |
return true | |
} | |
} | |
} | |
} | |
} | |
struct MainCommand: AsyncParsableCommand { | |
static var configuration = CommandConfiguration( | |
commandName: "...", | |
abstract: "....", | |
version: "...", | |
subcommands: [ | |
XcodeCommand.self | |
], | |
helpNames: [ .short, .long ] | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment