Skip to content

Commit

Permalink
Create PBXProj class from the Data reprentation of a pbxproj file (
Browse files Browse the repository at this point in the history
…#798)

Resolves #793

- Add the ability to instantiate `PBXProj` from `Data` representation
- This complements #787 where projects can now be serialised to `Data` representation rather than to disk

Notes:

- `PBXProj.name` isn't serialised as part of the serialised data representation and will need to be manually updated
  • Loading branch information
Ibrahimhass authored Oct 12, 2023
1 parent e1b2111 commit c3f69fc
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 2 deletions.
3 changes: 3 additions & 0 deletions Sources/XcodeProj/Errors/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ enum PBXProjError: Error, CustomStringConvertible, Equatable {
case pathIsAbsolute(Path)
case multipleLocalPackages(productName: String)
case multipleRemotePackages(productName: String)
case malformed
var description: String {
switch self {
case let .notFound(path):
Expand All @@ -201,6 +202,8 @@ enum PBXProjError: Error, CustomStringConvertible, Equatable {
return "Found multiple top-level packages named \(productName)"
case let .multipleRemotePackages(productName: productName):
return "Can not resolve dependency \(productName) - conflicting version requirements"
case .malformed:
return "The .pbxproj is malformed."
}
}
}
Expand Down
42 changes: 40 additions & 2 deletions Sources/XcodeProj/Objects/Project/PBXProj.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,41 @@ public final class PBXProj: Decodable {
objects: pbxproj.objects
)
}

/// Initializes the project with the data representation of pbxproj file.
///
/// - Parameters:
/// - data: data representation of pbxproj file.
public convenience init(data: Data) throws {
var propertyListFormat = PropertyListSerialization.PropertyListFormat.xml

let serialized = try PropertyListSerialization.propertyList(
from: data,
options: .mutableContainersAndLeaves,
format: &propertyListFormat
)

guard let pbxProjDictionary = serialized as? [String: Any] else {
throw PBXProjError.malformed
}

let context = ProjectDecodingContext(
pbxProjValueReader: { key in
pbxProjDictionary[key]
}
)

let plistDecoder = XcodeprojPropertyListDecoder(context: context)
let pbxproj: PBXProj = try plistDecoder.decode(PBXProj.self, from: data)

self.init(
rootObject: pbxproj.rootObject,
objectVersion: pbxproj.objectVersion,
archiveVersion: pbxproj.archiveVersion,
classes: pbxproj.classes,
objects: pbxproj.objects
)
}

private init(
rootObject: PBXProject? = nil,
Expand Down Expand Up @@ -146,8 +181,11 @@ public final class PBXProj: Decodable {
options: .mutableContainersAndLeaves,
format: &propertyListFormat
)
// swiftlint:disable:next force_cast
let pbxProjDictionary = serialized as! [String: Any]

guard let pbxProjDictionary = serialized as? [String: Any] else {
throw PBXProjError.malformed
}

return (plistXML, pbxProjDictionary)
}
}
Expand Down
14 changes: 14 additions & 0 deletions Tests/XcodeProjTests/Project/XcodeProjTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ final class XcodeProjIntegrationTests: XCTestCase {
initModel: XcodeProj.init(path:))
}

func test_initialize_PBXProj_with_data() throws {
// Given
let pbxprojPath = iosProjectPath + "project.pbxproj"
let pbxprojFromDisk = try PBXProj(path: pbxprojPath)
let pbxprojData = try Data(contentsOf: pbxprojPath.url)

// When
let pbxprojFromData = try PBXProj(data: pbxprojData)
try pbxprojFromData.updateProjectName(path: pbxprojPath)

// Then
XCTAssertEqual(pbxprojFromData, pbxprojFromDisk)
}

func test_write_includes_workspace_settings() throws {
// Define workspace settings that should be written
let workspaceSettings = WorkspaceSettings(buildSystem: .new, derivedDataLocationStyle: .default, autoCreateSchemes: false)
Expand Down

0 comments on commit c3f69fc

Please sign in to comment.