Skip to content

Commit 0e5651c

Browse files
committed
Separate Serializer.
1 parent 5c364e7 commit 0e5651c

File tree

4 files changed

+123
-127
lines changed

4 files changed

+123
-127
lines changed

Xcode/Xcode/Extensions.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@
88

99
import Foundation
1010

11-
extension Array {
11+
func += <KeyType, ValueType> (inout left: Dictionary<KeyType, ValueType>, right: Dictionary<KeyType, ValueType>) {
12+
for (k, v) in right {
13+
left.updateValue(v, forKey: k)
14+
}
15+
}
16+
17+
extension SequenceType {
1218
func ofType<T>(type: T.Type) -> [T] {
13-
return self.filter { $0 is T }.map { $0 as! T }
19+
return self.flatMap { $0 as? T }
1420
}
1521

16-
func any(pred: Element -> Bool) -> Bool {
22+
func any(pred: Generator.Element -> Bool) -> Bool {
1723
for elem in self {
1824
if pred(elem) {
1925
return true
@@ -22,10 +28,23 @@ extension Array {
2228

2329
return false
2430
}
25-
}
2631

27-
func += <KeyType, ValueType> (inout left: Dictionary<KeyType, ValueType>, right: Dictionary<KeyType, ValueType>) {
28-
for (k, v) in right {
29-
left.updateValue(v, forKey: k)
32+
func groupBy<Key: Hashable>(keySelector: Generator.Element -> Key) -> [Key : [Generator.Element]] {
33+
var groupedBy = Dictionary<Key, [Generator.Element]>()
34+
35+
for element in self {
36+
let key = keySelector(element)
37+
if let group = groupedBy[key] {
38+
groupedBy[key] = group + [element]
39+
} else {
40+
groupedBy[key] = [element]
41+
}
42+
}
43+
44+
return groupedBy
3045
}
31-
}
46+
47+
func sortBy<U: Comparable>(keySelector: Generator.Element -> U) -> [Generator.Element] {
48+
return self.sort { keySelector($0) < keySelector($1) }
49+
}
50+
}

Xcode/Xcode/PBXObject.swift

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,6 @@ import Foundation
1010

1111
typealias JsonObject = [String: AnyObject]
1212

13-
public class AllObjects {
14-
var projectName: String
15-
var dict: [String: PBXObject] = [:]
16-
var fullFilePaths: [String: String] = [:]
17-
18-
init(projectName: String) {
19-
self.projectName = projectName
20-
}
21-
22-
subscript(key: String) -> PBXObject? {
23-
get { return dict[key] }
24-
}
25-
26-
func object<T : PBXObject>(key: String) -> T {
27-
let obj = dict[key]!
28-
if let t = obj as? T {
29-
return t
30-
}
31-
32-
return T(id: key, dict: obj.dict, allObjects: self)
33-
}
34-
}
35-
3613
public /* abstract */ class PBXObject {
3714
let id: String
3815
let dict: JsonObject

Xcode/Xcode/Serialization.swift

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ extension XCProjectFile {
1818
let name = try XCProjectFile.projectName(xcodeprojPath)
1919
let path = xcodeprojPath + "/project.pbxproj"
2020

21-
allObjects.projectName = name
21+
let serializer = Serializer(projectName: name, projectFile: self)
2222

2323
if format == NSPropertyListFormat.OpenStepFormat {
24-
try openStepSerialization.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding)
24+
try serializer.openStepSerialization.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding)
2525
return true
2626
}
2727
else {
@@ -31,10 +31,11 @@ extension XCProjectFile {
3131
}
3232

3333
public func serialize(projectName: String) throws -> NSData {
34-
allObjects.projectName = projectName
34+
35+
let serializer = Serializer(projectName: projectName, projectFile: self)
3536

3637
if format == NSPropertyListFormat.OpenStepFormat {
37-
return openStepSerialization.dataUsingEncoding(NSUTF8StringEncoding)!
38+
return serializer.openStepSerialization.dataUsingEncoding(NSUTF8StringEncoding)!
3839
}
3940
else {
4041
return try NSPropertyListSerialization.dataWithPropertyList(dict, format: format, options: 0)
@@ -51,28 +52,61 @@ let specialRegexes = [
5152
"\\t": try! NSRegularExpression(pattern: "\\t", options: []),
5253
]
5354

54-
extension XCProjectFile {
55+
internal class Serializer {
56+
57+
let projectName: String
58+
let projectFile: XCProjectFile
59+
60+
init(projectName: String, projectFile: XCProjectFile) {
61+
self.projectName = projectName
62+
self.projectFile = projectFile
63+
}
64+
65+
lazy var targetsByConfigId: [String: PBXNativeTarget] = {
66+
var dict: [String: PBXNativeTarget] = [:]
67+
for target in self.projectFile.project.targets {
68+
dict[target.buildConfigurationList.id] = target
69+
}
70+
71+
return dict
72+
}()
73+
74+
lazy var buildPhaseByFileId: [String: PBXBuildPhase] = {
75+
76+
let buildPhases = self.projectFile.allObjects.dict.values.ofType(PBXBuildPhase)
77+
78+
var dict: [String: PBXBuildPhase] = [:]
79+
for buildPhase in buildPhases {
80+
for file in buildPhase.files {
81+
dict[file.id] = buildPhase
82+
}
83+
}
84+
85+
return dict
86+
}()
87+
5588
var openStepSerialization: String {
5689
var lines = [
5790
"// !$*UTF8*$!",
5891
"{",
5992
]
6093

61-
for key in dict.keys.sort() {
62-
let val: AnyObject = dict[key]!
94+
for key in projectFile.dict.keys.sort() {
95+
let val: AnyObject = projectFile.dict[key]!
6396

6497
if key == "objects" {
6598

6699
lines.append("\tobjects = {")
67100

68-
let isas = Set(allObjects.dict.values.map { $0.isa })
101+
let groupedObjects = projectFile.allObjects.dict.values
102+
.groupBy { $0.isa }
103+
.sortBy { $0.0 }
69104

70-
for isa in isas.sort() {
105+
for (isa, objects) in groupedObjects {
71106
lines.append("")
72107
lines.append("/* Begin \(isa) section */")
73-
let objects = allObjects.dict.values.filter { $0.isa == isa }
74108

75-
for object in objects.sort({ $0.id < $1.id }) {
109+
for object in objects.sortBy({ $0.id }) {
76110

77111
let multiline = isa != "PBXBuildFile" && isa != "PBXFileReference"
78112

@@ -110,11 +144,11 @@ extension XCProjectFile {
110144
}
111145

112146
func comment(key: String, verbose: Bool) -> String? {
113-
if key == project.id {
147+
if key == projectFile.project.id {
114148
return "Project object"
115149
}
116150

117-
if let obj = allObjects[key] {
151+
if let obj = projectFile.allObjects.dict[key] {
118152
if let name = obj.dict["name"] as? String {
119153
return name
120154
}
@@ -149,29 +183,18 @@ extension XCProjectFile {
149183
return shellScript.name
150184
}
151185
if let buildFile = obj as? PBXBuildFile {
152-
// TODO: move these lookup tables outside of this function
153-
let objects = Array(allObjects.dict.values)
154-
let buildPhases = objects.ofType(PBXBuildPhase)
155-
let buildPhase = buildPhases.filter { $0.files.any { $0.id == key } }.first
156-
157-
if let buildPhase = buildPhase,
186+
if let buildPhase = buildPhaseByFileId[key],
158187
let fileRef = comment(buildFile.fileRef.id, verbose: verbose),
159188
let group = comment(buildPhase.id, verbose: verbose) {
160189

161-
return "\(fileRef) in \(group)"
190+
return "\(fileRef) in \(group)"
162191
}
163192
}
164193
if obj is XCConfigurationList {
165-
// TODO: move these lookup tables outside of this function
166-
let objects = Array(allObjects.dict.values)
167-
let targets = objects.ofType(PBXTarget)
168-
if let target = targets.filter({ $0.buildConfigurationList.id == key }).first {
194+
if let target = targetsByConfigId[key] {
169195
return "Build configuration list for \(target.isa) \"\(target.name)\""
170196
}
171-
let projects = objects.ofType(PBXProject)
172-
if let project = projects.filter({ $0.buildConfigurationList.id == key }).first {
173-
return "Build configuration list for \(project.isa) \"\(allObjects.projectName)\""
174-
}
197+
return "Build configuration list for PBXProject \"\(projectName)\""
175198
}
176199

177200
return obj.isa
@@ -290,7 +313,6 @@ extension XCProjectFile {
290313
}
291314
}
292315

293-
294316
var objComment = ""
295317
if let c = comment(objKey, verbose: true) {
296318
objComment = " /* \(c) */"

Xcode/Xcode/XCProjectFile.swift

Lines changed: 45 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,36 @@ enum ProjectFileError : ErrorType, CustomStringConvertible {
2525
}
2626
}
2727

28+
public class AllObjects {
29+
var dict: [String: PBXObject] = [:]
30+
var fullFilePaths: [String: String] = [:]
31+
32+
func object<T : PBXObject>(key: String) -> T {
33+
let obj = dict[key]!
34+
if let t = obj as? T {
35+
return t
36+
}
37+
38+
return T(id: key, dict: obj.dict, allObjects: self)
39+
}
40+
}
41+
2842
public class XCProjectFile {
2943
public let project: PBXProject
3044
let dict: JsonObject
3145
let format: NSPropertyListFormat
32-
let allObjects: AllObjects
46+
let allObjects = AllObjects()
3347

3448
public convenience init(xcodeprojPath: String) throws {
3549

3650
guard let data = NSData(contentsOfFile: xcodeprojPath + "/project.pbxproj") else {
3751
throw ProjectFileError.MissingPbxproj
3852
}
3953

40-
let name = try XCProjectFile.projectName(xcodeprojPath)
41-
42-
try self.init(propertyListData: data, projectName: name)
54+
try self.init(propertyListData: data)
4355
}
4456

45-
public convenience init(propertyListData data: NSData, projectName: String) throws {
57+
public convenience init(propertyListData data: NSData) throws {
4658

4759
let options = NSPropertyListReadOptions.Immutable
4860
var format: NSPropertyListFormat = NSPropertyListFormat.BinaryFormat_v1_0
@@ -52,13 +64,12 @@ public class XCProjectFile {
5264
throw ProjectFileError.InvalidData
5365
}
5466

55-
self.init(dict: dict, format: format, projectName: projectName)
67+
self.init(dict: dict, format: format)
5668
}
5769

58-
init(dict: JsonObject, format: NSPropertyListFormat, projectName: String) {
70+
init(dict: JsonObject, format: NSPropertyListFormat) {
5971
self.dict = dict
6072
self.format = format
61-
self.allObjects = AllObjects(projectName: projectName)
6273
let objects = dict["objects"] as! [String: JsonObject]
6374

6475
for (key, obj) in objects {
@@ -88,65 +99,9 @@ public class XCProjectFile {
8899
static func createObject(id: String, dict: JsonObject, allObjects: AllObjects) -> PBXObject {
89100
let isa = dict["isa"] as? String
90101

91-
if isa == "PBXProject" {
92-
return PBXProject(id: id, dict: dict, allObjects: allObjects)
93-
}
94-
if isa == "PBXContainerItemProxy" {
95-
return PBXContainerItemProxy(id: id, dict: dict, allObjects: allObjects)
96-
}
97-
if isa == "PBXBuildFile" {
98-
return PBXBuildFile(id: id, dict: dict, allObjects: allObjects)
99-
}
100-
if isa == "PBXCopyFilesBuildPhase" {
101-
return PBXCopyFilesBuildPhase(id: id, dict: dict, allObjects: allObjects)
102-
}
103-
if isa == "PBXFrameworksBuildPhase" {
104-
return PBXFrameworksBuildPhase(id: id, dict: dict, allObjects: allObjects)
105-
}
106-
if isa == "PBXHeadersBuildPhase" {
107-
return PBXHeadersBuildPhase(id: id, dict: dict, allObjects: allObjects)
108-
}
109-
if isa == "PBXResourcesBuildPhase" {
110-
return PBXResourcesBuildPhase(id: id, dict: dict, allObjects: allObjects)
111-
}
112-
if isa == "PBXShellScriptBuildPhase" {
113-
return PBXShellScriptBuildPhase(id: id, dict: dict, allObjects: allObjects)
114-
}
115-
if isa == "PBXSourcesBuildPhase" {
116-
return PBXSourcesBuildPhase(id: id, dict: dict, allObjects: allObjects)
117-
}
118-
if isa == "PBXBuildStyle" {
119-
return PBXBuildStyle(id: id, dict: dict, allObjects: allObjects)
120-
}
121-
if isa == "XCBuildConfiguration" {
122-
return XCBuildConfiguration(id: id, dict: dict, allObjects: allObjects)
123-
}
124-
if isa == "PBXAggregateTarget" {
125-
return PBXAggregateTarget(id: id, dict: dict, allObjects: allObjects)
126-
}
127-
if isa == "PBXNativeTarget" {
128-
return PBXNativeTarget(id: id, dict: dict, allObjects: allObjects)
129-
}
130-
if isa == "PBXTargetDependency" {
131-
return PBXTargetDependency(id: id, dict: dict, allObjects: allObjects)
132-
}
133-
if isa == "XCConfigurationList" {
134-
return XCConfigurationList(id: id, dict: dict, allObjects: allObjects)
135-
}
136-
if isa == "PBXReference" {
137-
return PBXReference(id: id, dict: dict, allObjects: allObjects)
138-
}
139-
if isa == "PBXFileReference" {
140-
return PBXFileReference(id: id, dict: dict, allObjects: allObjects)
141-
}
142-
if isa == "PBXGroup" {
143-
return PBXGroup(id: id, dict: dict, allObjects: allObjects)
144-
}
145-
if isa == "PBXVariantGroup" {
146-
return PBXVariantGroup(id: id, dict: dict, allObjects: allObjects)
147-
}
148-
if isa == "XCVersionGroup" {
149-
return XCVersionGroup(id: id, dict: dict, allObjects: allObjects)
102+
if let isa = isa,
103+
let type = types[isa] {
104+
return type.init(id: id, dict: dict, allObjects: allObjects)
150105
}
151106

152107
// Fallback
@@ -173,3 +128,26 @@ public class XCProjectFile {
173128
return ps
174129
}
175130
}
131+
132+
let types: [String: PBXObject.Type] = [
133+
"PBXProject": PBXProject.self,
134+
"PBXContainerItemProxy": PBXContainerItemProxy.self,
135+
"PBXBuildFile": PBXBuildFile.self,
136+
"PBXCopyFilesBuildPhase": PBXCopyFilesBuildPhase.self,
137+
"PBXFrameworksBuildPhase": PBXFrameworksBuildPhase.self,
138+
"PBXHeadersBuildPhase": PBXHeadersBuildPhase.self,
139+
"PBXResourcesBuildPhase": PBXResourcesBuildPhase.self,
140+
"PBXShellScriptBuildPhase": PBXShellScriptBuildPhase.self,
141+
"PBXSourcesBuildPhase": PBXSourcesBuildPhase.self,
142+
"PBXBuildStyle": PBXBuildStyle.self,
143+
"XCBuildConfiguration": XCBuildConfiguration.self,
144+
"PBXAggregateTarget": PBXAggregateTarget.self,
145+
"PBXNativeTarget": PBXNativeTarget.self,
146+
"PBXTargetDependency": PBXTargetDependency.self,
147+
"XCConfigurationList": XCConfigurationList.self,
148+
"PBXReference": PBXReference.self,
149+
"PBXFileReference": PBXFileReference.self,
150+
"PBXGroup": PBXGroup.self,
151+
"PBXVariantGroup": PBXVariantGroup.self,
152+
"XCVersionGroup": XCVersionGroup.self
153+
]

0 commit comments

Comments
 (0)