diff --git a/.gitignore b/.gitignore index b7f1399..0cc0832 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.build \ No newline at end of file +.build +.swiftpm \ No newline at end of file diff --git a/Sources/OpenStepSerializer.swift b/Sources/OpenStepSerializer.swift index 622821d..a827cc2 100644 --- a/Sources/OpenStepSerializer.swift +++ b/Sources/OpenStepSerializer.swift @@ -14,9 +14,9 @@ class OpenStepSerializer { let projectFile: XcodeProj let lineEnding: String - init(projectName: String, lineEnding: String = "\r\n", projectFile: XcodeProj) { + init(projectName: String, lineEnding: String? = "\r\n", projectFile: XcodeProj) { self.projectName = projectName - self.lineEnding = lineEnding + self.lineEnding = lineEnding ?? "\r\n" self.projectFile = projectFile } @@ -67,7 +67,7 @@ class OpenStepSerializer { } #if os(Linux) let row: String - if let dict = val as? [AnyHashable: Any], dict.isEmpty { + if let dict = val as? [AnyHashable: Any], dict.isEmpty { row = "\(key) = {\n}\(comment);" // print on linux give [:] ... } else { row = "\(key) = \(val)\(comment);" diff --git a/Sources/PropertyList.swift b/Sources/PropertyList.swift new file mode 100644 index 0000000..66494f9 --- /dev/null +++ b/Sources/PropertyList.swift @@ -0,0 +1,145 @@ +// +// PropertyList.swift +// +// +// Created by emarchand on 22/10/2022. +// + +import Foundation + +open class PropertyList { + + public enum Format { + case binary + case xml + case json + case openStep + + public func toPropertyListformat() -> PropertyListSerialization.PropertyListFormat? { + switch self { + case .binary: + return .binary + case .xml: + return .xml + case .openStep: + return .openStep + case .json: + return nil + } + } + + public init(_ format: PropertyListSerialization.PropertyListFormat) { + switch format { + case .binary: + self = .binary + case .xml: + self = .xml + case .openStep: + self = .openStep + @unknown default: + fatalError("unknown format") + } + } + + public init?(_ format: PropertyListSerialization.PropertyListFormat?) { + if let format = format { + self.init(format) + } else { + return nil + } + } + + public init?(string: String) { + switch string.lowercased() { + case "openstep": + self = .openStep + case "xml", "xml1": + self = .xml + case "json": + self = .json + case "binary", "binary1": + self = .binary + default: + return nil + } + } + + } + + public let dict: PBXObject.Fields + public let format: Format + + public init(dict: PBXObject.Fields, format: Format) throws { + self.dict = dict + self.format = format + } + + public convenience init(propertyListData data: Data) throws { + let format: Format + let obj: Any + if data.first == 123 { // start with { + obj = try JSONSerialization.jsonObject(with: data) + format = .json + } else { + var propertyListFormat: PropertyListSerialization.PropertyListFormat = .binary + obj = try PropertyListSerialization.propertyList(from: data, options: [], format: &propertyListFormat) + format = .init(propertyListFormat) + } + + guard let dict = obj as? PBXObject.Fields else { + throw XcodeProjError.invalidData(object: obj) + } + + try self.init(dict: dict, format: format) + } + + public convenience init(url: URL) throws { + assert(url.isFileURL) + do { + let data = try Data(contentsOf: url) + try self.init(propertyListData: data) + } catch let error as XcodeProjError { + throw error + } catch { + throw XcodeProjError.failedToReadFile(error: error) + } + } + + // MARK: - Write + public func write(to url: URL, + format: Format, + atomic: Bool = true) throws { + if format == .openStep { + throw XcodeProjError.notSupported + } else if let propertyListformat = format.toPropertyListformat() { + let data = try PropertyListSerialization.data( + fromPropertyList: dict, + format: propertyListformat, + options: 0) +#if os(Linux) + try data.write(to: url, options: []) // error no attomic on linux +#else + try data.write(to: url, options: atomic ? [.atomicWrite] : []) +#endif + } else if format == .json { + let data = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) +#if os(Linux) + try data.write(to: url, options: []) // error no attomic on linux +#else + try data.write(to: url, options: atomic ? [.atomicWrite] : []) +#endif + } + } + + public func data() throws -> Data { + if format == .openStep { + throw XcodeProjError.notSupported + } else if let propertyListformat = format.toPropertyListformat() { + return try PropertyListSerialization.data(fromPropertyList: dict, format: propertyListformat, options: 0) + } else if format == .json { + return try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) + } + return Data() + } + +} diff --git a/Sources/XcodeProj+Serialization.swift b/Sources/XcodeProj+Serialization.swift index 529d292..0965d79 100755 --- a/Sources/XcodeProj+Serialization.swift +++ b/Sources/XcodeProj+Serialization.swift @@ -10,8 +10,11 @@ import Foundation extension XcodeProj { - public func write(to url: URL, format: Format? = nil, - projectName: String? = nil, lineEnding: String? = nil, atomic: Bool = true) throws { + public func write(to url: URL, + format: Format? = nil, + projectName: String? = nil, + lineEnding: String? = nil, + atomic: Bool = true) throws { let pbxprojURL: URL let name: String if url.isDirectoryURL { @@ -36,16 +39,17 @@ extension XcodeProj { if format == .openStep { let serializer = OpenStepSerializer(projectName: name, lineEnding: lineEnding, projectFile: self) try serializer.serialize().write(to: pbxprojURL, atomically: atomic, encoding: .utf8) - } - else if let propertyListformat = format.toPropertyListformat() { - let data = try PropertyListSerialization.data(fromPropertyList: dict, format: propertyListformat, options: 0) + } else if let propertyListformat = format.toPropertyListformat() { + let data = try PropertyListSerialization.data( + fromPropertyList: dict, + format: propertyListformat, + options: 0) #if os(Linux) try data.write(to: pbxprojURL, options: []) // error no attomic on linux #else try data.write(to: pbxprojURL, options: atomic ? [.atomicWrite] : []) #endif - } - else if format == .json { + } else if format == .json { let data = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) #if os(Linux) try data.write(to: pbxprojURL, options: []) // error no attomic on linux @@ -62,8 +66,7 @@ extension XcodeProj { return try serializer.serialize().data(using: .utf8) ?? Data() } else if let propertyListformat = format.toPropertyListformat() { return try PropertyListSerialization.data(fromPropertyList: dict, format: propertyListformat, options: 0) - } - else if format == .json { + } else if format == .json { return try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) } return Data() // must not occurs diff --git a/Sources/XcodeProj.swift b/Sources/XcodeProj.swift index bd93527..8db8ddf 100755 --- a/Sources/XcodeProj.swift +++ b/Sources/XcodeProj.swift @@ -8,88 +8,6 @@ import Foundation -open class PropertyList { - - public enum Format { - case binary - case xml - case json - case openStep - - public func toPropertyListformat() -> PropertyListSerialization.PropertyListFormat? { - switch self { - case .binary: - return .binary - case .xml: - return .xml - case .openStep: - return .openStep - case .json: - return nil - } - } - - public init(_ format: PropertyListSerialization.PropertyListFormat) { - switch format { - case .binary: - self = .binary - case .xml: - self = .xml - case .openStep: - self = .openStep - } - } - - public init?(_ format: PropertyListSerialization.PropertyListFormat?) { - if let format = format { - self.init(format) - } else { - return nil - } - } - } - - public let dict: PBXObject.Fields - public let format: Format - - public init(dict: PBXObject.Fields, format: Format) throws { - self.dict = dict - self.format = format - } - - public convenience init(propertyListData data: Data) throws { - let format: Format - let obj: Any - if data.first == 123 { // start with { - obj = try JSONSerialization.jsonObject(with: data) - format = .json - } else { - var propertyListFormat: PropertyListSerialization.PropertyListFormat = .binary - obj = try PropertyListSerialization.propertyList(from: data, options: [], format: &propertyListFormat) - format = .init(propertyListFormat) - } - - guard let dict = obj as? PBXObject.Fields else { - throw XcodeProjError.invalidData(object: obj) - } - - try self.init(dict: dict, format: format) - } - - public convenience init(url: URL) throws { - assert(url.isFileURL) - do { - let data = try Data(contentsOf: url) - try self.init(propertyListData: data) - } catch let error as XcodeProjError { - throw error - } catch { - throw XcodeProjError.failedToReadFile(error: error) - } - } - -} - public class XcodeProj: PropertyList { public static let pbxprojFileExtension = "pbxproj" @@ -150,7 +68,6 @@ public class XcodeProj: PropertyList { } - // MARK: init public convenience init(url: URL) throws { assert(url.isFileURL) diff --git a/Sources/XcodeProjError.swift b/Sources/XcodeProjError.swift index d45b1b5..dc5d1f7 100644 --- a/Sources/XcodeProjError.swift +++ b/Sources/XcodeProjError.swift @@ -22,4 +22,7 @@ public enum XcodeProjError: Error { // object missing case objectMissing(key: String, expectedType: Isa?) + // not supported + case notSupported + }