diff --git a/.swiftlint.yml b/.swiftlint.yml index 6cf483b..4af420e 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -8,8 +8,8 @@ excluded: line_length: 220 number_separator: minimum_length: 5 -disabled_rules: -- identifier_name +#disabled_rules: +#- identifier_name opt_in_rules: - file_header - array_init diff --git a/Sources/FirebladeECS/Entity.swift b/Sources/FirebladeECS/Entity.swift index 7603d11..c269940 100644 --- a/Sources/FirebladeECS/Entity.swift +++ b/Sources/FirebladeECS/Entity.swift @@ -5,12 +5,12 @@ // Created by Christian Treffs on 08.10.17. // -public final class Entity: UniqueEntityIdentifiable { +open class Entity: UniqueEntityIdentifiable { internal(set) public var identifier: EntityIdentifier = EntityIdentifier.invalid public var name: String? unowned let nexus: Nexus - init(nexus: Nexus, id: EntityIdentifier, name: String? = nil) { + internal init(nexus: Nexus, id: EntityIdentifier, name: String? = nil) { self.nexus = nexus self.identifier = id self.name = name diff --git a/Sources/FirebladeECS/Family.swift b/Sources/FirebladeECS/Family.swift index 46c0772..9f30b8b 100644 --- a/Sources/FirebladeECS/Family.swift +++ b/Sources/FirebladeECS/Family.swift @@ -34,11 +34,11 @@ public final class Family: Equatable { } public final var memberIds: UniformEntityIdentifiers { - return nexus?.members(of: traits) ?? UniformEntityIdentifiers() + return nexus?.members(withFamilyTraits: traits) ?? UniformEntityIdentifiers() } public final var count: Int { - return nexus?.members(of: traits)?.count ?? 0 + return nexus?.members(withFamilyTraits: traits)?.count ?? 0 } public final func canBecomeMember(_ entity: Entity) -> Bool { diff --git a/Sources/FirebladeECS/Nexus+Component.swift b/Sources/FirebladeECS/Nexus+Component.swift index 98645a5..cc33ddd 100644 --- a/Sources/FirebladeECS/Nexus+Component.swift +++ b/Sources/FirebladeECS/Nexus+Component.swift @@ -8,7 +8,7 @@ public extension Nexus { final var numComponents: Int { - return componentsByType.reduce(0) { return $0 + $1.value.count } + return componentsByType.reduce(0) { $0 + $1.value.count } } final func has(componentId: ComponentIdentifier, entityIdx: EntityIndex) -> Bool { diff --git a/Sources/FirebladeECS/Nexus+Entity.swift b/Sources/FirebladeECS/Nexus+Entity.swift index 698ac04..3b64d14 100644 --- a/Sources/FirebladeECS/Nexus+Entity.swift +++ b/Sources/FirebladeECS/Nexus+Entity.swift @@ -29,7 +29,7 @@ extension Nexus { return entityStorage.count } - public func has(entity entityId: EntityIdentifier) -> Bool { + public func exists(entity entityId: EntityIdentifier) -> Bool { return entityStorage.contains(entityId.index) } diff --git a/Sources/FirebladeECS/Nexus+Family.swift b/Sources/FirebladeECS/Nexus+Family.swift index 17a706d..f6c632f 100644 --- a/Sources/FirebladeECS/Nexus+Family.swift +++ b/Sources/FirebladeECS/Nexus+Family.swift @@ -37,7 +37,7 @@ public extension Nexus { return family.traits.isMatch(components: componentSet) } - func members(of traits: FamilyTraitSet) -> UniformEntityIdentifiers? { + func members(withFamilyTraits traits: FamilyTraitSet) -> UniformEntityIdentifiers? { return familyMembersByTraits[traits] } @@ -46,11 +46,11 @@ public extension Nexus { } func isMember(_ entityId: EntityIdentifier, in family: Family) -> Bool { - return isMember(entityId, in: family.traits) + return isMember(entity: entityId, inFamilyWithTraits: family.traits) } - func isMember(_ entityId: EntityIdentifier, in traits: FamilyTraitSet) -> Bool { - guard let members: UniformEntityIdentifiers = members(of: traits) else { + func isMember(entity entityId: EntityIdentifier, inFamilyWithTraits traits: FamilyTraitSet) -> Bool { + guard let members: UniformEntityIdentifiers = members(withFamilyTraits: traits) else { return false } return members.contains(entityId.index) @@ -63,9 +63,8 @@ extension Nexus { func update(familyMembership entityId: EntityIdentifier) { // FIXME: iterating all families is costly for many families - for (familyTraits, _) in familyMembersByTraits { - update(membership: familyTraits, for: entityId) - } + familyMembersByTraits.forEach { familyTraits, _ in update(membership: familyTraits, for: entityId) } + } enum UpdateState { @@ -76,32 +75,34 @@ extension Nexus { case unchanged(id: EntityIdentifier, traits: FamilyTraitSet) } - @discardableResult - func update(membership traits: FamilyTraitSet, for entityId: EntityIdentifier) -> UpdateState { + func update(membership traits: FamilyTraitSet, for entityId: EntityIdentifier) { let entityIdx: EntityIndex = entityId.index guard let componentIds: SparseComponentIdentifierSet = componentIdsByEntity[entityIdx] else { - return .noComponents(id: entityId, traits: traits) + // no components - so skip + return } - let isMember: Bool = self.isMember(entityId, in: traits) - if !has(entity: entityId) && isMember { - remove(from: traits, entityId: entityId, entityIdx: entityIdx) - return .removedDeleted(id: entityId, traits: traits) + let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits) + if !exists(entity: entityId) && isMember { + remove(entityWithId: entityId, andIndex: entityIdx, fromFamilyWithTraits: traits) + return } + // TODO: get rid of set creation for comparison let componentsSet: ComponentSet = ComponentSet(componentIds) let isMatch: Bool = traits.isMatch(components: componentsSet) + switch (isMatch, isMember) { case (true, false): - add(to: traits, entityId: entityId, entityIdx: entityIdx) + add(entityWithId: entityId, andIndex: entityIdx, toFamilyWithTraits: traits) notify(FamilyMemberAdded(member: entityId, toFamily: traits)) - return .added(id: entityId, traits: traits) + return case (false, true): - remove(from: traits, entityId: entityId, entityIdx: entityIdx) + remove(entityWithId: entityId, andIndex: entityIdx, fromFamilyWithTraits: traits) notify(FamilyMemberRemoved(member: entityId, from: traits)) - return .removed(id: entityId, traits: traits) + return default: - return .unchanged(id: entityId, traits: traits) + return } } @@ -127,28 +128,27 @@ extension Nexus { // MARK: - fileprivate extensions private extension Nexus { - func get(family traits: FamilyTraitSet) -> Family? { + final func get(family traits: FamilyTraitSet) -> Family? { return create(family: traits) } - func create(family traits: FamilyTraitSet) -> Family { + final func create(family traits: FamilyTraitSet) -> Family { let family: Family = Family(self, traits: traits) return family } - func calculateTraitEntityIdHash(traitHash: FamilyTraitSetHash, entityIdx: EntityIndex) -> TraitEntityIdHash { + final func calculateTraitEntityIdHash(traitHash: FamilyTraitSetHash, entityIdx: EntityIndex) -> TraitEntityIdHash { return hash(combine: traitHash, entityIdx) } - func add(to traits: FamilyTraitSet, entityId: EntityIdentifier, entityIdx: EntityIndex) { + final func add(entityWithId entityId: EntityIdentifier, andIndex entityIdx: EntityIndex, toFamilyWithTraits traits: FamilyTraitSet) { if familyMembersByTraits[traits] == nil { familyMembersByTraits[traits] = UniformEntityIdentifiers() } - familyMembersByTraits[traits]?.insert(entityId, at: entityIdx) } - func remove(from traits: FamilyTraitSet, entityId: EntityIdentifier, entityIdx: EntityIndex) { + final func remove(entityWithId entityId: EntityIdentifier, andIndex entityIdx: EntityIndex, fromFamilyWithTraits traits: FamilyTraitSet) { familyMembersByTraits[traits]?.remove(at: entityIdx) } } diff --git a/Sources/FirebladeECS/UnorderedSparseSet.swift b/Sources/FirebladeECS/UnorderedSparseSet.swift index 24d3852..d4809d3 100644 --- a/Sources/FirebladeECS/UnorderedSparseSet.swift +++ b/Sources/FirebladeECS/UnorderedSparseSet.swift @@ -5,7 +5,7 @@ // Created by Christian Treffs on 30.10.17. // -public class UnorderedSparseSet: Sequence { +public class UnorderedSparseSet { public typealias Index = Int public typealias Key = Int @@ -136,6 +136,33 @@ public class UnorderedSparseSet: Sequence { } } +extension UnorderedSparseSet: MutableCollection, RandomAccessCollection { + public func index(after index: Index) -> Int { + return dense.index(after: index) + } + + public subscript(position: Index) -> Element { + get { + guard let element: Element = get(at: position) else { + fatalError("no element at index \(position)") + } + return element + } + set(newValue) { + insert(newValue, at: position) + } + } + + public var startIndex: Index { + return dense.startIndex + } + + public var endIndex: Index { + return dense.endIndex + } + +} + extension UnorderedSparseSet.Entry: Equatable where UnorderedSparseSet.Element: Equatable { public static func == (lhs: UnorderedSparseSet.Entry, rhs: UnorderedSparseSet.Entry) -> Bool { return lhs.element == rhs.element && lhs.key == rhs.key diff --git a/Tests/FirebladeECSTests/Base.swift b/Tests/FirebladeECSTests/Base.swift index 2176ed3..fae6d1e 100644 --- a/Tests/FirebladeECSTests/Base.swift +++ b/Tests/FirebladeECSTests/Base.swift @@ -57,9 +57,8 @@ class ExampleSystem { position.x *= 2 velocity.a *= 2 - } - - + } + } } diff --git a/Tests/FirebladeECSTests/FamilyPerformanceTests.swift b/Tests/FirebladeECSTests/FamilyPerformanceTests.swift index aa0b818..d41f103 100644 --- a/Tests/FirebladeECSTests/FamilyPerformanceTests.swift +++ b/Tests/FirebladeECSTests/FamilyPerformanceTests.swift @@ -5,79 +5,78 @@ // Created by Christian Treffs on 09.05.18. // -import XCTest import FirebladeECS +import XCTest class FamilyPerformanceTests: XCTestCase { - + var nexus: Nexus! - + override func setUp() { super.setUp() nexus = Nexus() } - + override func tearDown() { nexus = nil super.tearDown() } - - + func testMeasureIterateMembers() { - + let number: Int = 10_000 - + for i in 0.. 0 { entity.destroy() @@ -185,14 +183,14 @@ class FamilyTests: XCTestCase { return } } - + XCTAssertEqual(family.memberIds.count, (count / 2)) - + for _ in 0.. = Set.init([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812]) - let b: Set = Set.init([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234]) - let c: Set = Set.init([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543]) + let a: Set = Set([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812]) + let b: Set = Set([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234]) + let c: Set = Set([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543]) let input: ContiguousArray = ContiguousArray(arrayLiteral: a.hashValue, b.hashValue, c.hashValue) measure { @@ -53,9 +53,9 @@ class HashingTests: XCTestCase { } func testMeasureSetOfSetHash() { - let a: Set = Set.init([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812]) - let b: Set = Set.init([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234]) - let c: Set = Set.init([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543]) + let a: Set = Set([14_561_291, 26_451_562, 34_562_182, 488_972_556, 5_128_426_962, 68_211_812]) + let b: Set = Set([1_083_838, 912_312, 83_333, 71_234_555, 4_343_234]) + let c: Set = Set([3_410_346_899_765, 90_000_002, 12_212_321, 71, 6_123_345_676_543]) let input = Set>(arrayLiteral: a, b, c) measure { diff --git a/Tests/FirebladeECSTests/NexusTests.swift b/Tests/FirebladeECSTests/NexusTests.swift index b7bb5ae..133177e 100644 --- a/Tests/FirebladeECSTests/NexusTests.swift +++ b/Tests/FirebladeECSTests/NexusTests.swift @@ -11,7 +11,7 @@ import XCTest class NexusTests: XCTestCase { var nexus: Nexus! - + override func setUp() { super.setUp() nexus = Nexus() @@ -26,44 +26,43 @@ class NexusTests: XCTestCase { XCTAssertEqual(nexus.numEntities, 0) let e0 = nexus.create() - + XCTAssertEqual(e0.identifier.index, 0) XCTAssertEqual(nexus.numEntities, 1) - + let e1 = nexus.create(entity: "Entity 1") XCTAssert(e1.identifier.index == 1) XCTAssert(nexus.numEntities == 2) - + XCTAssertNil(e0.name) XCTAssertEqual(e1.name, "Entity 1") } - + func testEntityDestroy() { testEntityCreate() XCTAssertEqual(nexus.numEntities, 2) - + let e1: Entity = nexus.get(entity: 1)! XCTAssertEqual(e1.identifier.index, 1) - + XCTAssertTrue(nexus.destroy(entity: e1)) XCTAssertFalse(nexus.destroy(entity: e1)) - + XCTAssertEqual(nexus.numEntities, 1) - + let e1Again: Entity? = nexus.get(entity: 1) XCTAssertNil(e1Again) - + XCTAssertEqual(nexus.numEntities, 1) - + nexus.clear() - + XCTAssertEqual(nexus.numEntities, 0) } - func testComponentCreation() { - + XCTAssert(nexus.numEntities == 0) let e0: Entity = nexus.create(entity: "e0") @@ -82,7 +81,7 @@ class NexusTests: XCTestCase { } func testComponentDeletion() { - + let identifier: EntityIdentifier = nexus.create(entity: "e0").identifier let e0 = nexus.get(entity: identifier)! diff --git a/Tests/FirebladeECSTests/SparseSetTests.swift b/Tests/FirebladeECSTests/SparseSetTests.swift index bf9fa1d..403e438 100644 --- a/Tests/FirebladeECSTests/SparseSetTests.swift +++ b/Tests/FirebladeECSTests/SparseSetTests.swift @@ -9,28 +9,28 @@ import XCTest class SparseSetTests: XCTestCase { - + var set: UnorderedSparseSet! - + override func setUp() { super.setUp() set = UnorderedSparseSet() } - + override func tearDown() { set = nil super.tearDown() } - + func testSparseSetAdd() { - + let num: Int = 100 - + for idx in 0.. = [0, 30, 1, 21, 78, 56, 99, 3] - + for idx in indices { let pos = Position(x: idx, y: idx) set.insert(pos, at: idx) } - + XCTAssertEqual(set.count, indices.count) XCTAssertEqual(set.sparse.count, indices.count) XCTAssertEqual(set.dense.count, indices.count) XCTAssertFalse(set.isEmpty) - + XCTAssertEqual(set.get(at: 0)?.x, 0) XCTAssertEqual(set.get(at: 30)?.x, 30) XCTAssertEqual(set.get(at: 1)?.x, 1) @@ -394,17 +384,16 @@ class SparseSetTests: XCTestCase { XCTAssertEqual(set.get(at: 56)?.x, 56) XCTAssertEqual(set.get(at: 99)?.x, 99) XCTAssertEqual(set.get(at: 3)?.x, 3) - - + } - + func testSparseSetRemoveNonPresent() { - + XCTAssertTrue(set.isEmpty) XCTAssertNil(set.remove(at: 100)) XCTAssertTrue(set.isEmpty) } - + func testSparseSetDoubleRemove() { class AClass { } let set = UnorderedSparseSet() @@ -412,115 +401,111 @@ class SparseSetTests: XCTestCase { let b = AClass() set.insert(a, at: 0) set.insert(b, at: 1) - + XCTAssertEqual(set.sparse.count, 2) XCTAssertEqual(set.dense.count, 2) - + XCTAssertEqual(set.count, 2) - + XCTAssertTrue(set.get(at: 0) === a) XCTAssertTrue(set.get(at: 1) === b) - + XCTAssertEqual(set.count, 2) - + XCTAssertNotNil(set.remove(at: 1)) - + XCTAssertEqual(set.count, 1) XCTAssertEqual(set.sparse.count, 1) XCTAssertEqual(set.dense.count, 1) - - + XCTAssertNil(set.remove(at: 1)) - + XCTAssertEqual(set.count, 1) XCTAssertEqual(set.sparse.count, 1) XCTAssertEqual(set.dense.count, 1) - - + XCTAssertTrue(set.get(at: 0) === a) - + XCTAssertEqual(set.count, 1) - + } - + func testSparseSetNonCongiuousData() { - - + var indices: Set = [0, 30, 1, 21, 78, 56, 99, 3] - + for idx in indices { let pos = Position(x: idx, y: idx) set.insert(pos, at: idx) } - + XCTAssertEqual(set.count, indices.count) - + func recurseValueTest() { for idx in indices { XCTAssertEqual(set.get(at: idx)?.x, idx) XCTAssertEqual(set.get(at: idx)?.y, idx) } } - + recurseValueTest() - + while let idx = indices.popFirst() { let entry = set.remove(at: idx)! XCTAssertEqual(entry.key, idx) recurseValueTest() XCTAssertEqual(set.count, indices.count) } - + XCTAssertEqual(set.count, indices.count) XCTAssertEqual(set.count, 0) } - + func testSparseSetClear() { - + let num: Int = 100 - + XCTAssertEqual(set.count, 0) XCTAssertTrue(set.isEmpty) - + for idx in 0..() - - + characters.insert("H", at: 4) characters.insert("e", at: 13) characters.insert("l", at: 44) characters.insert("l", at: 123) characters.insert("o", at: 89) - + characters.insert(" ", at: 66) characters.insert("W", at: 77) characters.insert("o", at: 55) characters.insert("r", at: 90) characters.insert("l", at: 34) characters.insert("d", at: 140) - + XCTAssertEqual(characters.count, 11) - - let string: String = characters.reduce("") { (res, char) in - return res + "\(char)" + + let string: String = characters.reduce("") { res, char in + res + "\(char)" } - + // NOTE: this tests only dense insertion order, this is no guarantee for the real ordering. XCTAssertEqual(string, "Hello World") - + } } diff --git a/Tests/FirebladeECSTests/SystemsTests.swift b/Tests/FirebladeECSTests/SystemsTests.swift index 5274cfd..b1ef50b 100644 --- a/Tests/FirebladeECSTests/SystemsTests.swift +++ b/Tests/FirebladeECSTests/SystemsTests.swift @@ -5,106 +5,105 @@ // Created by Christian Treffs on 10.05.18. // -import XCTest @testable import FirebladeECS +import XCTest class SystemsTests: XCTestCase { - + var nexus: Nexus! var colorSystem: ColorSystem! var positionSystem: PositionSystem! - + override func setUp() { super.setUp() nexus = Nexus() colorSystem = ColorSystem(nexus: nexus) positionSystem = PositionSystem(nexus: nexus) } - + override func tearDown() { colorSystem = nil positionSystem = nil nexus = nil super.tearDown() - + } - + func testSystemsUpdate() { - + let num: Int = 10_000 - + colorSystem.update() positionSystem.update() - + let posTraits = positionSystem.family.traits - + XCTAssertEqual(nexus.numEntities, 0) XCTAssertEqual(colorSystem.family.memberIds.count, 0) XCTAssertEqual(positionSystem.family.memberIds.count, 0) XCTAssertEqual(nexus.freeEntities.count, 0) XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, 0) - + batchCreateEntities(count: num) - + XCTAssertEqual(nexus.numEntities, num) XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num) XCTAssertEqual(colorSystem.family.memberIds.count, num) XCTAssertEqual(positionSystem.family.memberIds.count, num) XCTAssertEqual(nexus.freeEntities.count, 0) - + colorSystem.update() positionSystem.update() - + XCTAssertEqual(nexus.numEntities, num) XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num) XCTAssertEqual(colorSystem.family.memberIds.count, num) XCTAssertEqual(positionSystem.family.memberIds.count, num) XCTAssertEqual(nexus.freeEntities.count, 0) - + batchCreateEntities(count: num) - + XCTAssertEqual(nexus.numEntities, num * 2) XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2) XCTAssertEqual(colorSystem.family.memberIds.count, num * 2) XCTAssertEqual(positionSystem.family.memberIds.count, num * 2) XCTAssertEqual(nexus.freeEntities.count, 0) - + colorSystem.update() positionSystem.update() - + XCTAssertEqual(nexus.numEntities, num * 2) XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2) XCTAssertEqual(colorSystem.family.memberIds.count, num * 2) XCTAssertEqual(positionSystem.family.memberIds.count, num * 2) XCTAssertEqual(nexus.freeEntities.count, 0) - + batchDestroyEntities(count: num) - + XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num) XCTAssertEqual(nexus.freeEntities.count, num) XCTAssertEqual(nexus.numEntities, num) XCTAssertEqual(colorSystem.family.memberIds.count, num) XCTAssertEqual(positionSystem.family.memberIds.count, num) - - + colorSystem.update() positionSystem.update() - + XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num) XCTAssertEqual(nexus.numEntities, num) XCTAssertEqual(colorSystem.family.memberIds.count, num) XCTAssertEqual(positionSystem.family.memberIds.count, num) XCTAssertEqual(nexus.freeEntities.count, num) - + batchCreateEntities(count: num) - + XCTAssertEqual(nexus.familyMembersByTraits[posTraits]?.count, num * 2) XCTAssertEqual(nexus.numEntities, num * 2) XCTAssertEqual(colorSystem.family.memberIds.count, num * 2) XCTAssertEqual(positionSystem.family.memberIds.count, num * 2) XCTAssertEqual(nexus.freeEntities.count, 0) } - + func createDefaultEntity(name: String?) { let e = nexus.create(entity: name) e.assign(Position(x: 1, y: 2)) @@ -116,11 +115,11 @@ class SystemsTests: XCTestCase { createDefaultEntity(name: nil) } } - + func batchDestroyEntities(count: Int) { let family = nexus.family(requiresAll: [Position.self], excludesAll: []) var currentCount = count - + family.iterate { (entity: Entity!) in if currentCount > 0 { entity.destroy() @@ -130,19 +129,19 @@ class SystemsTests: XCTestCase { return } } - + } - + } class ColorSystem { - + let family: Family - + init(nexus: Nexus) { family = nexus.family(requiresAll: [Color.self], excludesAll: []) } - + func update() { family.iterate { (color: Color!) in color.r = 1 @@ -152,32 +151,31 @@ class ColorSystem { } } - class PositionSystem { let family: Family - + var velocity: Double = 4.0 - + init(nexus: Nexus) { family = nexus.family(requiresAll: [Position.self], excludesAll: []) } - + func randNorm() -> Double { return 4.0 } - + func update() { family.iterate { [unowned self](pos: Position!) in - - let deltaX: Double = self.velocity*((self.randNorm() * 2) - 1) - let deltaY: Double = self.velocity*((self.randNorm() * 2) - 1) + + let deltaX: Double = self.velocity * ((self.randNorm() * 2) - 1) + let deltaY: Double = self.velocity * ((self.randNorm() * 2) - 1) let x = pos.x + Int(deltaX) let y = pos.y + Int(deltaY) - + pos.x = x pos.y = y } } - + } diff --git a/generateXcodeProject.sh b/generateXcodeProject.sh index e9a52ce..d417b56 100755 --- a/generateXcodeProject.sh +++ b/generateXcodeProject.sh @@ -11,7 +11,7 @@ bundle install swift package update # generate project -swift package generate-xcodeproj #--xcconfig-overrides settings.xcconfig +swift package generate-xcodeproj --enable-code-coverage #--xcconfig-overrides settings.xcconfig # add project specialities bundle exec ./prepareXcodeProject.rb