Skip to content

Commit e25a5b8

Browse files
feat: add library product to Swift Package (#8)
1 parent 61de2fc commit e25a5b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+289
-201
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
EXECUTABLE=xcp
2-
BUILD_PRODUCT=XcodeProjectCLI
2+
BUILD_PRODUCT=xcp
33

44
build:
55
swift build
@@ -45,4 +45,4 @@ make uninstall:
4545
clean:
4646
rm -rf .bin .release .build
4747
swift package clean
48-
rm ~/.local/bin/$(EXECUTABLE)
48+
rm ~/.local/bin/$(EXECUTABLE) || true

Package.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ let package = Package(
88
platforms: [
99
.macOS(.v13)
1010
],
11+
products: [
12+
.library(name: "XcodeProject", targets: ["XcodeProject"]),
13+
.executable(name: "xcp", targets: ["XcodeProjectCLI"])
14+
],
1115
dependencies: [
1216
.package(url: "https://github.com/tuist/XcodeProj.git", .upToNextMajor(from: "8.12.0")),
1317
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0")
@@ -16,13 +20,24 @@ let package = Package(
1620
.executableTarget(
1721
name: "XcodeProjectCLI",
1822
dependencies: [
19-
"XcodeProj",
23+
"XcodeProjectCommands",
24+
.product(name: "ArgumentParser", package: "swift-argument-parser")
25+
]
26+
),
27+
.target(
28+
name: "XcodeProject",
29+
dependencies: ["XcodeProj"]
30+
),
31+
.target(
32+
name: "XcodeProjectCommands",
33+
dependencies: [
34+
"XcodeProject",
2035
.product(name: "ArgumentParser", package: "swift-argument-parser")
2136
]
2237
),
2338
.testTarget(
24-
name: "XcodeProjectCLITests",
25-
dependencies: ["XcodeProjectCLI"]
39+
name: "XcodeProjectCommandsTests",
40+
dependencies: ["XcodeProjectCommands"]
2641
)
2742
]
2843
)

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,21 @@ brew install wojciech-kulik/tap/xcp
6767
xcp --version
6868
```
6969

70+
## ⚙️ Use as a Library
71+
72+
You can also use XcodeProjectCLI as a library in your Swift projects for macOS. Here's a simple example of how to integrate it:
73+
74+
```swift
75+
import XcodeProject
76+
77+
let projectPath = "/path/to/YourProject.xcodeproj"
78+
let xcodeProject = try Project(xcodeProjectPath: projectPath)
79+
80+
// Sample operation:
81+
try project.groups.addGroup("path/relative/to/xcodeproj/NewGroup".asInputPath)
82+
try project.save()
83+
```
84+
7085
## 🤓 My Other Projects
7186

7287
- [Snippety](https://snippety.app) - Snippets manager for macOS & iOS

Sources/XcodeProjectCLI/Core/Project.swift renamed to Sources/XcodeProject/Core/Project.swift

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
import Foundation
22
import XcodeProj
33

4-
final class Project {
4+
public final class Project {
55
private(set) static var projectRoot = ""
66

7-
let targets: ProjectTargets
8-
let groups: ProjectGroups
9-
let files: ProjectFiles
7+
public let targets: ProjectTargets
8+
public let groups: ProjectGroups
9+
public let files: ProjectFiles
1010

1111
private let project: XcodeProj
1212

13-
init(projectPath: String?) throws {
14-
var projectPath = projectPath
13+
public init(xcodeProjectPath: String?) throws {
14+
var projectPath = xcodeProjectPath
15+
let pwd = ProcessInfo.processInfo.environment["PWD"]
16+
var currentDir = pwd
1517

1618
#if DEBUG
17-
let currentDir: String? = ProcessInfo.processInfo.environment["PWD"] ?? #filePath
18-
.components(separatedBy: "/")
19-
.dropLast(4)
20-
.joined(separator: "/")
21-
#else
22-
let currentDir = ProcessInfo.processInfo.environment["PWD"]
19+
if pwd == nil || pwd == "/tmp" {
20+
currentDir = #filePath
21+
.components(separatedBy: "/")
22+
.dropLast(4)
23+
.joined(separator: "/")
24+
}
2325
#endif
2426

2527
if projectPath == nil, let currentDir {
@@ -33,7 +35,7 @@ final class Project {
3335
projectPath = (projectPath as NSString?)?.expandingTildeInPath
3436

3537
guard let projectPath else {
36-
throw CLIError.xcodeProjectNotFound
38+
throw XcodeProjectError.xcodeProjectNotFound
3739
}
3840

3941
Self.projectRoot = (projectPath as NSString).deletingLastPathComponent
@@ -44,7 +46,7 @@ final class Project {
4446
self.files = ProjectFiles(project: project)
4547
}
4648

47-
func save() throws {
49+
public func save() throws {
4850
if let path = project.path {
4951
try project.write(path: path, override: true)
5052
}

Sources/XcodeProjectCLI/Core/ProjectFiles.swift renamed to Sources/XcodeProject/Core/ProjectFiles.swift

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22
import XcodeProj
33

4-
final class ProjectFiles {
4+
public final class ProjectFiles {
55
private let project: XcodeProj
66
private lazy var projectGroups = ProjectGroups(project: project)
77
private lazy var projectTargets = ProjectTargets(project: project)
@@ -10,13 +10,17 @@ final class ProjectFiles {
1010
self.project = project
1111
}
1212

13-
func findFile(_ filePath: InputPath) -> PBXFileReference? {
13+
public init(xcodeProjectPath: String) throws {
14+
self.project = try XcodeProj(pathString: xcodeProjectPath)
15+
}
16+
17+
public func findFile(_ filePath: InputPath) -> PBXFileReference? {
1418
project.pbxproj.fileReferences
1519
.first { $0.fullPath == filePath }
1620
}
1721

1822
@discardableResult
19-
func addFile(
23+
public func addFile(
2024
_ filePath: InputPath,
2125
toTargets targets: [String],
2226
guessTarget: Bool,
@@ -28,7 +32,7 @@ final class ProjectFiles {
2832

2933
// Validate group
3034
if group == nil, !createGroups {
31-
throw CLIError.groupNotFoundInProject(groupPath)
35+
throw XcodeProjectError.groupNotFoundInProject(groupPath)
3236
}
3337

3438
// Create group if needed
@@ -59,9 +63,9 @@ final class ProjectFiles {
5963
return targets
6064
}
6165

62-
func removeFile(_ filePath: InputPath) throws {
66+
public func removeFile(_ filePath: InputPath) throws {
6367
guard let fileRef = findFile(filePath) else {
64-
throw CLIError.fileNotFoundInProject(filePath)
68+
throw XcodeProjectError.fileNotFoundInProject(filePath)
6569
}
6670

6771
// Remove from build phases
@@ -80,7 +84,7 @@ final class ProjectFiles {
8084
project.pbxproj.delete(object: fileRef)
8185
}
8286

83-
func removeFile(_ filePath: InputPath, from target: String) throws {
87+
public func removeFile(_ filePath: InputPath, from target: String) throws {
8488
guard let target = project.pbxproj.targets(named: target).first else {
8589
return
8690
}
@@ -90,11 +94,11 @@ final class ProjectFiles {
9094
}
9195

9296
@discardableResult
93-
func moveFile(_ filePath: InputPath, to newPath: InputPath) throws -> [TargetName] {
97+
public func moveFile(_ filePath: InputPath, to newPath: InputPath) throws -> [TargetName] {
9498
let targets = try projectTargets.findTargets(for: filePath)
9599

96100
guard let fileRef = findFile(filePath) else {
97-
throw CLIError.fileNotFoundInProject(filePath)
101+
throw XcodeProjectError.fileNotFoundInProject(filePath)
98102
}
99103

100104
// Find or create destination group
@@ -117,9 +121,9 @@ final class ProjectFiles {
117121
return targets.map(\.name)
118122
}
119123

120-
func renameFile(_ filePath: InputPath, newName: String) throws {
124+
public func renameFile(_ filePath: InputPath, newName: String) throws {
121125
guard let file = findFile(filePath) else {
122-
throw CLIError.fileNotFoundInProject(filePath)
126+
throw XcodeProjectError.fileNotFoundInProject(filePath)
123127
}
124128

125129
file.name = nil

Sources/XcodeProjectCLI/Core/ProjectGroups.swift renamed to Sources/XcodeProject/Core/ProjectGroups.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
import Foundation
22
import XcodeProj
33

4-
final class ProjectGroups {
4+
public final class ProjectGroups {
55
private let project: XcodeProj
66
private lazy var projectFiles = ProjectFiles(project: project)
77

88
init(project: XcodeProj) {
99
self.project = project
1010
}
1111

12-
func findGroup(_ groupPath: InputPath) throws -> PBXGroup? {
12+
public init(xcodeProjectPath: String) throws {
13+
self.project = try XcodeProj(pathString: xcodeProjectPath)
14+
}
15+
16+
public func findGroup(_ groupPath: InputPath) throws -> PBXGroup? {
1317
try listAllGroups()
1418
.first { $1 == groupPath }?
1519
.group
1620
}
1721

18-
func createGroupHierarchy(at groupPath: InputPath) throws -> PBXGroup {
22+
public func createGroupHierarchy(at groupPath: InputPath) throws -> PBXGroup {
1923
guard let rootGroup = try project.pbxproj.rootGroup() else {
20-
throw CLIError.rootGroupNotFound
24+
throw XcodeProjectError.rootGroupNotFound
2125
}
2226

2327
let pathComponents = groupPath.relativePathComponents
@@ -34,7 +38,7 @@ final class ProjectGroups {
3438
return currentGroup
3539
}
3640

37-
func addGroup(_ groupPath: InputPath) throws {
41+
public func addGroup(_ groupPath: InputPath) throws {
3842
if try findGroup(groupPath) != nil {
3943
print("Warning: Group already exists in the project: \(groupPath)")
4044
return
@@ -43,19 +47,19 @@ final class ProjectGroups {
4347
_ = try createGroupHierarchy(at: groupPath)
4448
}
4549

46-
func renameGroup(_ groupPath: InputPath, newName: String) throws {
50+
public func renameGroup(_ groupPath: InputPath, newName: String) throws {
4751
guard let group = try findGroup(groupPath) else {
48-
throw CLIError.groupNotFoundInProject(groupPath)
52+
throw XcodeProjectError.groupNotFoundInProject(groupPath)
4953
}
5054

5155
group.name = nil
5256
group.path = newName
5357
group.setGroupSourceTree()
5458
}
5559

56-
func deleteGroup(_ groupPath: InputPath) throws {
60+
public func deleteGroup(_ groupPath: InputPath) throws {
5761
guard let group = try findGroup(groupPath) else {
58-
throw CLIError.groupNotFoundInProject(groupPath)
62+
throw XcodeProjectError.groupNotFoundInProject(groupPath)
5963
}
6064

6165
if let parent = group.parent as? PBXGroup {
@@ -65,9 +69,9 @@ final class ProjectGroups {
6569
try removeGroupRecursively(group)
6670
}
6771

68-
func moveGroup(_ groupPath: InputPath, to destination: InputPath) throws {
72+
public func moveGroup(_ groupPath: InputPath, to destination: InputPath) throws {
6973
guard let group = try findGroup(groupPath) else {
70-
throw CLIError.groupNotFoundInProject(groupPath)
74+
throw XcodeProjectError.groupNotFoundInProject(groupPath)
7175
}
7276

7377
if let parent = group.parent as? PBXGroup {
@@ -139,7 +143,7 @@ final class ProjectGroups {
139143

140144
private func listAllGroups() throws -> [(group: PBXGroup, path: InputPath)] {
141145
guard let rootGroup = try project.pbxproj.rootGroup() else {
142-
throw CLIError.rootGroupNotFound
146+
throw XcodeProjectError.rootGroupNotFound
143147
}
144148

145149
return try rootGroup

0 commit comments

Comments
 (0)