diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..9f55b2c --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +3.0 diff --git a/ArrayDiff.xcodeproj/project.pbxproj b/ArrayDiff.xcodeproj/project.pbxproj index 967ed74..1a62cad 100644 --- a/ArrayDiff.xcodeproj/project.pbxproj +++ b/ArrayDiff.xcodeproj/project.pbxproj @@ -160,9 +160,11 @@ TargetAttributes = { CC71C1521BBDD4B400772EDB = { CreatedOnToolsVersion = 7.0.1; + LastSwiftMigration = 0800; }; CC71C15C1BBDD4B500772EDB = { CreatedOnToolsVersion = 7.0.1; + LastSwiftMigration = 0800; }; }; }; @@ -337,6 +339,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -355,6 +358,7 @@ PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiff; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -365,6 +369,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiffTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -375,6 +380,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiffTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/ArrayDiff/ArrayDiff.swift b/ArrayDiff/ArrayDiff.swift index 7dea737..369c91e 100644 --- a/ArrayDiff/ArrayDiff.swift +++ b/ArrayDiff/ArrayDiff.swift @@ -5,28 +5,28 @@ public struct ArrayDiff { static var debugLogging = false /// The indexes in the old array of the items that were kept - public let commonIndexes: NSIndexSet + public let commonIndexes: IndexSet /// The indexes in the old array of the items that were removed - public let removedIndexes: NSIndexSet + public let removedIndexes: IndexSet /// The indexes in the new array of the items that were inserted - public let insertedIndexes: NSIndexSet + public let insertedIndexes: IndexSet /// Returns nil if the item was inserted - public func oldIndexForNewIndex(index: Int) -> Int? { - if insertedIndexes.containsIndex(index) { return nil } + public func oldIndexForNewIndex(_ index: Int) -> Int? { + if insertedIndexes.contains(index) { return nil } var result = index - result -= insertedIndexes.countOfIndexesInRange(NSMakeRange(0, index)) - result += removedIndexes.countOfIndexesInRange(NSMakeRange(0, result + 1)) + result -= insertedIndexes.count(in: NSMakeRange(0, index).toRange()!) + result += removedIndexes.count(in: NSMakeRange(0, result + 1).toRange()!) return result } /// Returns nil if the item was deleted - public func newIndexForOldIndex(index: Int) -> Int? { - if removedIndexes.containsIndex(index) { return nil } + public func newIndexForOldIndex(_ index: Int) -> Int? { + if removedIndexes.contains(index) { return nil } var result = index - let deletedBefore = removedIndexes.countOfIndexesInRange(NSMakeRange(0, index)) + let deletedBefore = removedIndexes.count(in: NSMakeRange(0, index).toRange()!) result -= deletedBefore var insertedAtOrBefore = 0 for i in insertedIndexes { @@ -54,31 +54,31 @@ public struct ArrayDiff { public extension Array { - public func diff(other: Array, elementsAreEqual: ((Element, Element) -> Bool)) -> ArrayDiff { + public func diff(_ other: Array, elementsAreEqual: ((Element, Element) -> Bool)) -> ArrayDiff { var lengths: [[Int]] = Array>( - count: count + 1, - repeatedValue: Array( - count: other.count + 1, - repeatedValue: 0) + repeating: Array( + repeating: 0, + count: other.count + 1), + count: count + 1 ) - for i in (0...count).reverse() { - for j in (0...other.count).reverse() { + for i in (0...count).reversed() { + for j in (0...other.count).reversed() { if i == count || j == other.count { lengths[i][j] = 0 } else if elementsAreEqual(self[i], other[j]) { lengths[i][j] = 1 + lengths[i+1][j+1] } else { - lengths[i][j] = max(lengths[i+1][j], lengths[i][j+1]) + lengths[i][j] = Swift.max(lengths[i+1][j], lengths[i][j+1]) } } } - let commonIndexes = NSMutableIndexSet() + var commonIndexes = IndexSet() var i = 0, j = 0 while i < count && j < other.count { if elementsAreEqual(self[i], other[j]) { - commonIndexes.addIndex(i) + commonIndexes.insert(i) i += 1 j += 1 } else if lengths[i+1][j] >= lengths[i][j+1] { @@ -87,12 +87,14 @@ public extension Array { j += 1 } } - - let removedIndexes = NSMutableIndexSet(indexesInRange: NSMakeRange(0, count)) - removedIndexes.removeIndexes(commonIndexes) + + var removedIndexes = IndexSet(integersIn: 0..) -> ArrayDiff { + public func diff(_ other: Array) -> ArrayDiff { return self.diff(other, elementsAreEqual: { $0 == $1 }) } } diff --git a/ArrayDiff/FoundationExtensions.swift b/ArrayDiff/FoundationExtensions.swift index e4bc3ff..f5f34a8 100644 --- a/ArrayDiff/FoundationExtensions.swift +++ b/ArrayDiff/FoundationExtensions.swift @@ -4,31 +4,38 @@ import Foundation // MARK: NSRange <-> Range conversion public extension NSRange { - public var range: Range { + public var range: CountableRange { return location..) { - location = range.startIndex - length = range.endIndex - range.startIndex + location = range.lowerBound + length = range.upperBound - range.lowerBound } } // MARK: NSIndexSet -> [NSIndexPath] conversion -public extension NSIndexSet { +public extension IndexSet { /** Returns an array of NSIndexPaths that correspond to these indexes in the given section. When reporting changes to table/collection view, you can improve performance by sorting deletes in descending order and inserts in ascending order. */ - public func indexPathsInSection(section: Int, ascending: Bool = true) -> [NSIndexPath] { - var result: [NSIndexPath] = [] + public func indexPathsInSection(_ section: Int, ascending: Bool = true) -> [IndexPath] { + var result: [IndexPath] = [] result.reserveCapacity(count) - enumerateIndexesWithOptions(ascending ? [] : .Reverse) { index, _ in - result.append(NSIndexPath(indexes: [section, index], length: 2)) - } + + if ascending { + forEach { index in + result.append(IndexPath(indexes: [section, index])) + } + } else { + reversed().forEach { index in + result.append(IndexPath(indexes: [section, index])) + } + } return result } } @@ -37,26 +44,29 @@ public extension NSIndexSet { public extension Array { - public subscript (indexes: NSIndexSet) -> [Element] { + public subscript (indexes: IndexSet) -> [Element] { var result: [Element] = [] result.reserveCapacity(indexes.count) - indexes.enumerateRangesUsingBlock { nsRange, _ in + + (indexes as NSIndexSet).enumerateRanges(options: []) { nsRange, _ in result += self[nsRange.range] } return result } - public mutating func removeAtIndexes(indexSet: NSIndexSet) { - indexSet.enumerateRangesWithOptions(.Reverse) { nsRange, _ in - self.removeRange(nsRange.range) + public mutating func removeAtIndexes(_ indexSet: IndexSet) { + (indexSet as NSIndexSet).enumerateRanges(options: .reverse) { nsRange, _ in + self.removeSubrange(nsRange.range) } } - public mutating func insertElements(newElements: [Element], atIndexes indexes: NSIndexSet) { + public mutating func insertElements(_ newElements: [Element], atIndexes indexes: IndexSet) { assert(indexes.count == newElements.count) var i = 0 - indexes.enumerateRangesUsingBlock { range, _ in - self.insertContentsOf(newElements[i.." */ public var nestedDescription: String { - let countsStr = enumerate().map { "[\($0): \($1.items.count)]" }.joinWithSeparator(", ") + let countsStr = enumerated().map { "[\($0): \($1.items.count)]" }.joined(separator: ", ") return "" } /** Attempt to retrieve the item at the given index path. Returns nil if the index is out of bounds. */ - public subscript (indexPath: NSIndexPath) -> Element.Item? { - let sectionIndex = indexPath.indexAtPosition(0) + public subscript (indexPath: IndexPath) -> Element.Item? { + let sectionIndex = indexPath[0] guard indices.contains(sectionIndex) else { return nil } let section = self[sectionIndex] - let itemIndex = indexPath.indexAtPosition(1) + let itemIndex = indexPath[1] guard section.items.indices.contains(itemIndex) else { return nil } return section.items[itemIndex] @@ -60,11 +60,11 @@ public struct NestedDiff { Determine the new index path, if any, for the given old index path. - Returns: The index path after the update, or nil if the item was removed */ - public func newIndexPathForOldIndexPath(indexPath: NSIndexPath) -> NSIndexPath? { - let oldSection = indexPath.indexAtPosition(0) + public func newIndexPathForOldIndexPath(_ indexPath: IndexPath) -> IndexPath? { + let oldSection = indexPath[0] if let newSection = sectionsDiff.newIndexForOldIndex(oldSection), - newItem = itemDiffs[oldSection]?.newIndexForOldIndex(indexPath.indexAtPosition(1)) { - return NSIndexPath(indexes: [newSection, newItem], length: 2) + let newItem = itemDiffs[oldSection]?.newIndexForOldIndex(indexPath[1]) { + return (NSIndexPath(indexes: [newSection, newItem], length: 2) as IndexPath) } else { return nil } @@ -74,10 +74,10 @@ public struct NestedDiff { Determine the new index path, if any, for the given old index path. - Returns: The index path before the update, or nil if the item was inserted */ - public func oldIndexPathForNewIndexPath(newIndexPath: NSIndexPath) -> NSIndexPath? { - if let oldSection = sectionsDiff.oldIndexForNewIndex(newIndexPath.indexAtPosition(0)), - oldItem = itemDiffs[oldSection]?.oldIndexForNewIndex(newIndexPath.indexAtPosition(1)) { - return NSIndexPath(indexes: [oldSection, oldItem], length: 2) + public func oldIndexPathForNewIndexPath(_ newIndexPath: IndexPath) -> IndexPath? { + if let oldSection = sectionsDiff.oldIndexForNewIndex(newIndexPath[0]), + let oldItem = itemDiffs[oldSection]?.oldIndexForNewIndex(newIndexPath[1]) { + return (NSIndexPath(indexes: [oldSection, oldItem], length: 2) as IndexPath) } else { return nil } @@ -109,11 +109,11 @@ public extension Array where Element: SectionType { itemDiffs is indexed based on the _old_ section indexes. */ - public func diffNested(newData: Array) -> NestedDiff { + public func diffNested(_ newData: Array) -> NestedDiff { let sectionDiff = diff(newData) // diffs will exist for all sections that weren't deleted or inserted - let itemDiffs: [ArrayDiff?] = self.enumerate().map { oldSectionIndex, oldSectionInfo in + let itemDiffs: [ArrayDiff?] = self.enumerated().map { oldSectionIndex, oldSectionInfo in if let newSection = sectionDiff.newIndexForOldIndex(oldSectionIndex) { assert(newData[newSection] == oldSectionInfo, "Diffing for the wrong section!") return oldSectionInfo.items.diff(newData[newSection].items) diff --git a/ArrayDiff/UIKitIntegration.swift b/ArrayDiff/UIKitIntegration.swift index 372a25a..e16483a 100644 --- a/ArrayDiff/UIKitIntegration.swift +++ b/ArrayDiff/UIKitIntegration.swift @@ -14,13 +14,13 @@ public extension ArrayDiff { This should be called on the main thread inside collectionView.performBatchUpdates */ - public func applyToItemsInCollectionView(collectionView: UICollectionView, section: Int) { - assert(NSThread.isMainThread()) + public func applyToItemsInCollectionView(_ collectionView: UICollectionView, section: Int) { + assert(Thread.isMainThread) // Apply updates in safe order for good measure. // Deletes, descending // Inserts, ascending - collectionView.deleteItemsAtIndexPaths(removedIndexes.indexPathsInSection(section, ascending: false)) - collectionView.insertItemsAtIndexPaths(insertedIndexes.indexPathsInSection(section)) + collectionView.deleteItems(at: removedIndexes.indexPathsInSection(section, ascending: false)) + collectionView.insertItems(at: insertedIndexes.indexPathsInSection(section)) } /** @@ -28,13 +28,13 @@ public extension ArrayDiff { This should be called on the main thread between tableView.beginUpdates and tableView.endUpdates */ - public func applyToRowsInTableView(tableView: UITableView, section: Int, rowAnimation: UITableViewRowAnimation) { - assert(NSThread.isMainThread()) + public func applyToRowsInTableView(_ tableView: UITableView, section: Int, rowAnimation: UITableViewRowAnimation) { + assert(Thread.isMainThread) // Apply updates in safe order for good measure. // Deletes, descending // Inserts, ascending - tableView.deleteRowsAtIndexPaths(removedIndexes.indexPathsInSection(section, ascending: false), withRowAnimation: rowAnimation) - tableView.insertRowsAtIndexPaths(insertedIndexes.indexPathsInSection(section), withRowAnimation: rowAnimation) + tableView.deleteRows(at: removedIndexes.indexPathsInSection(section, ascending: false), with: rowAnimation) + tableView.insertRows(at: insertedIndexes.indexPathsInSection(section), with: rowAnimation) } /** @@ -42,16 +42,16 @@ public extension ArrayDiff { This should be called on the main thread between tableView.beginUpdates and tableView.endUpdates */ - public func applyToSectionsInTableView(tableView: UITableView, rowAnimation: UITableViewRowAnimation) { - assert(NSThread.isMainThread()) + public func applyToSectionsInTableView(_ tableView: UITableView, rowAnimation: UITableViewRowAnimation) { + assert(Thread.isMainThread) // Apply updates in safe order for good measure. // Deletes, descending // Inserts, ascending if removedIndexes.count > 0 { - tableView.deleteSections(removedIndexes, withRowAnimation: rowAnimation) + tableView.deleteSections(removedIndexes as IndexSet, with: rowAnimation) } if insertedIndexes.count > 0 { - tableView.insertSections(insertedIndexes, withRowAnimation: rowAnimation) + tableView.insertSections(insertedIndexes as IndexSet, with: rowAnimation) } } @@ -60,16 +60,16 @@ public extension ArrayDiff { This should be called on the main thread inside collectionView.performBatchUpdates */ - public func applyToSectionsInCollectionView(collectionView: UICollectionView) { - assert(NSThread.isMainThread()) + public func applyToSectionsInCollectionView(_ collectionView: UICollectionView) { + assert(Thread.isMainThread) // Apply updates in safe order for good measure. // Deletes, descending // Inserts, ascending if removedIndexes.count > 0 { - collectionView.deleteSections(removedIndexes) + collectionView.deleteSections(removedIndexes as IndexSet) } if insertedIndexes.count > 0 { - collectionView.insertSections(insertedIndexes) + collectionView.insertSections(insertedIndexes as IndexSet) } } } @@ -80,23 +80,23 @@ public extension NestedDiff { This should be called on the main thread between tableView.beginUpdates and tableView.endUpdates */ - public func applyToTableView(tableView: UITableView, rowAnimation: UITableViewRowAnimation) { - assert(NSThread.isMainThread()) + public func applyToTableView(_ tableView: UITableView, rowAnimation: UITableViewRowAnimation) { + assert(Thread.isMainThread) // Apply updates in safe order for good measure. // Item deletes, descending // Section deletes // Section inserts // Item inserts, ascending - for (oldSection, diffOrNil) in itemDiffs.enumerate() { + for (oldSection, diffOrNil) in itemDiffs.enumerated() { if let diff = diffOrNil { - tableView.deleteRowsAtIndexPaths(diff.removedIndexes.indexPathsInSection(oldSection, ascending: false), withRowAnimation: rowAnimation) + tableView.deleteRows(at: diff.removedIndexes.indexPathsInSection(oldSection, ascending: false), with: rowAnimation) } } sectionsDiff.applyToSectionsInTableView(tableView, rowAnimation: rowAnimation) - for (oldSection, diffOrNil) in itemDiffs.enumerate() { + for (oldSection, diffOrNil) in itemDiffs.enumerated() { if let diff = diffOrNil { if let newSection = sectionsDiff.newIndexForOldIndex(oldSection) { - tableView.insertRowsAtIndexPaths(diff.insertedIndexes.indexPathsInSection(newSection), withRowAnimation: rowAnimation) + tableView.insertRows(at: diff.insertedIndexes.indexPathsInSection(newSection), with: rowAnimation) } else { assertionFailure("Found an item diff for a section that was removed. Wat.") } @@ -109,23 +109,23 @@ public extension NestedDiff { This should be called on the main thread inside collectionView.performBatchUpdates */ - public func applyToCollectionView(collectionView: UICollectionView) { - assert(NSThread.isMainThread()) + public func applyToCollectionView(_ collectionView: UICollectionView) { + assert(Thread.isMainThread) // Apply updates in safe order for good measure. // Item deletes, descending // Section deletes // Section inserts // Item inserts, ascending - for (oldSection, diffOrNil) in itemDiffs.enumerate() { + for (oldSection, diffOrNil) in itemDiffs.enumerated() { if let diff = diffOrNil { - collectionView.deleteItemsAtIndexPaths(diff.removedIndexes.indexPathsInSection(oldSection, ascending: false)) + collectionView.deleteItems(at: diff.removedIndexes.indexPathsInSection(oldSection, ascending: false)) } } sectionsDiff.applyToSectionsInCollectionView(collectionView) - for (oldSection, diffOrNil) in itemDiffs.enumerate() { + for (oldSection, diffOrNil) in itemDiffs.enumerated() { if let diff = diffOrNil { if let newSection = sectionsDiff.newIndexForOldIndex(oldSection) { - collectionView.insertItemsAtIndexPaths(diff.insertedIndexes.indexPathsInSection(newSection)) + collectionView.insertItems(at: diff.insertedIndexes.indexPathsInSection(newSection)) } else { assertionFailure("Found an item diff for a section that was removed. Wat.") } diff --git a/ArrayDiffTests/ArrayDiffTests.swift b/ArrayDiffTests/ArrayDiffTests.swift index 5365db9..d8da031 100644 --- a/ArrayDiffTests/ArrayDiffTests.swift +++ b/ArrayDiffTests/ArrayDiffTests.swift @@ -12,20 +12,20 @@ import XCTest class ArrayDiffTests: XCTestCase { func testACommonCase() { - let old = "a b c d e".componentsSeparatedByString(" ") - let new = "m a b f".componentsSeparatedByString(" ") + let old = "a b c d e".components(separatedBy: " ") + let new = "m a b f".components(separatedBy: " ") - let allFirstIndexes = NSIndexSet(indexesInRange: NSMakeRange(0, old.count)) + let allFirstIndexes = IndexSet(integersIn: NSMakeRange(0, old.count).toRange()!) - let expectedRemoves = NSMutableIndexSet() - expectedRemoves.addIndexesInRange(NSMakeRange(2, 3)) + var expectedRemoves = IndexSet() + expectedRemoves.add(in: 2..<2+3) - let expectedInserts = NSMutableIndexSet() - expectedInserts.addIndex(0) - expectedInserts.addIndex(3) + var expectedInserts = IndexSet() + expectedInserts.insert(0) + expectedInserts.insert(3) - let expectedCommonObjects = "a b".componentsSeparatedByString(" ") + let expectedCommonObjects = "a b".components(separatedBy: " ") let diff = old.diff(new) @@ -33,7 +33,7 @@ class ArrayDiffTests: XCTestCase { XCTAssertEqual(expectedRemoves, diff.removedIndexes) XCTAssertEqual(expectedCommonObjects, old[diff.commonIndexes]) - let removedPlusCommon = NSMutableIndexSet(indexSet: diff.removedIndexes) + var removedPlusCommon = IndexSet(indexSet: diff.removedIndexes) removedPlusCommon.addIndexes(diff.commonIndexes) XCTAssertEqual(removedPlusCommon, allFirstIndexes) @@ -44,8 +44,8 @@ class ArrayDiffTests: XCTestCase { } func testNewIndexForOldIndex() { - let old = "a b c d e".componentsSeparatedByString(" ") - let new = "m a b f".componentsSeparatedByString(" ") + let old = "a b c d e".components(separatedBy: " ") + let new = "m a b f".components(separatedBy: " ") let diff = old.diff(new) let newIndexes: [Int?] = (0.. Bool { + static func customEqual(_ t0: TestType, t1: TestType) -> Bool { return t0.value == t1.value } -} \ No newline at end of file +} diff --git a/Example/ArrayDiffExample.xcodeproj/project.pbxproj b/Example/ArrayDiffExample.xcodeproj/project.pbxproj index b6cafd0..ad9db4a 100644 --- a/Example/ArrayDiffExample.xcodeproj/project.pbxproj +++ b/Example/ArrayDiffExample.xcodeproj/project.pbxproj @@ -7,8 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 7A7A468D1F63E95868197ACD /* Pods_ArrayDiffExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1034EA96DBA00DD036314DA4 /* Pods_ArrayDiffExample.framework */; }; B99123EA9EDC92D672B36C4C /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8C5C4CE1B849F4DD20BD864 /* Pods.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - CC642FB81BC0620E0024AD39 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC642FB71BC0620E0024AD39 /* Utilities.swift */; settings = {ASSET_TAGS = (); }; }; + CC642FB81BC0620E0024AD39 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC642FB71BC0620E0024AD39 /* Utilities.swift */; }; CC71C17E1BBE16DA00772EDB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC71C17D1BBE16DA00772EDB /* AppDelegate.swift */; }; CC71C1801BBE16DA00772EDB /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC71C17F1BBE16DA00772EDB /* ViewController.swift */; }; CC71C1831BBE16DA00772EDB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CC71C1811BBE16DA00772EDB /* Main.storyboard */; }; @@ -17,6 +18,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 00F0F3804BA28FCFFDBB225B /* Pods-ArrayDiffExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ArrayDiffExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-ArrayDiffExample/Pods-ArrayDiffExample.release.xcconfig"; sourceTree = ""; }; + 1034EA96DBA00DD036314DA4 /* Pods_ArrayDiffExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ArrayDiffExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A0BE2AF2FAE498767E2B19BA /* Pods-ArrayDiffExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ArrayDiffExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ArrayDiffExample/Pods-ArrayDiffExample.debug.xcconfig"; sourceTree = ""; }; A8C5C4CE1B849F4DD20BD864 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC500C041BBF684900F0D46A /* ArrayDiffExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ArrayDiffExample-Bridging-Header.h"; sourceTree = ""; }; CC642FB71BC0620E0024AD39 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; @@ -37,6 +41,7 @@ buildActionMask = 2147483647; files = ( B99123EA9EDC92D672B36C4C /* Pods.framework in Frameworks */, + 7A7A468D1F63E95868197ACD /* Pods_ArrayDiffExample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -48,6 +53,8 @@ children = ( FA1BF45B04DCE2098602C724 /* Pods.debug.xcconfig */, E3A5B455C1F7070368012B13 /* Pods.release.xcconfig */, + A0BE2AF2FAE498767E2B19BA /* Pods-ArrayDiffExample.debug.xcconfig */, + 00F0F3804BA28FCFFDBB225B /* Pods-ArrayDiffExample.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -89,6 +96,7 @@ isa = PBXGroup; children = ( A8C5C4CE1B849F4DD20BD864 /* Pods.framework */, + 1034EA96DBA00DD036314DA4 /* Pods_ArrayDiffExample.framework */, ); name = Frameworks; sourceTree = ""; @@ -100,12 +108,12 @@ isa = PBXNativeTarget; buildConfigurationList = CC71C18C1BBE16DA00772EDB /* Build configuration list for PBXNativeTarget "ArrayDiffExample" */; buildPhases = ( - 0F51EC56221C4A84DB7112CA /* Check Pods Manifest.lock */, + 0F51EC56221C4A84DB7112CA /* [CP] Check Pods Manifest.lock */, CC71C1761BBE16DA00772EDB /* Sources */, CC71C1771BBE16DA00772EDB /* Frameworks */, CC71C1781BBE16DA00772EDB /* Resources */, - 3BDFB1C01D6681F5EE2CB90B /* Embed Pods Frameworks */, - 7A9FDAB7EC115F34BEE62E15 /* Copy Pods Resources */, + 3BDFB1C01D6681F5EE2CB90B /* [CP] Embed Pods Frameworks */, + 7A9FDAB7EC115F34BEE62E15 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -123,11 +131,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Adlai Holler"; TargetAttributes = { CC71C1791BBE16DA00772EDB = { CreatedOnToolsVersion = 7.0.1; + LastSwiftMigration = 0800; }; }; }; @@ -163,49 +172,49 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0F51EC56221C4A84DB7112CA /* Check Pods Manifest.lock */ = { + 0F51EC56221C4A84DB7112CA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - 3BDFB1C01D6681F5EE2CB90B /* Embed Pods Frameworks */ = { + 3BDFB1C01D6681F5EE2CB90B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "[CP] Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ArrayDiffExample/Pods-ArrayDiffExample-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 7A9FDAB7EC115F34BEE62E15 /* Copy Pods Resources */ = { + 7A9FDAB7EC115F34BEE62E15 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "[CP] Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ArrayDiffExample/Pods-ArrayDiffExample-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -256,8 +265,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -300,8 +311,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -320,14 +333,16 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; }; name = Release; }; CC71C18D1BBE16DA00772EDB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FA1BF45B04DCE2098602C724 /* Pods.debug.xcconfig */; + baseConfigurationReference = A0BE2AF2FAE498767E2B19BA /* Pods-ArrayDiffExample.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; GCC_OPTIMIZATION_LEVEL = s; @@ -337,13 +352,15 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ArrayDiffExample/ArrayDiffExample-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 3.0; }; name = Debug; }; CC71C18E1BBE16DA00772EDB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E3A5B455C1F7070368012B13 /* Pods.release.xcconfig */; + baseConfigurationReference = 00F0F3804BA28FCFFDBB225B /* Pods-ArrayDiffExample.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = ArrayDiffExample/Info.plist; @@ -351,6 +368,7 @@ PRODUCT_BUNDLE_IDENTIFIER = adlai.ArrayDiffExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "ArrayDiffExample/ArrayDiffExample-Bridging-Header.h"; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Example/ArrayDiffExample/AppDelegate.swift b/Example/ArrayDiffExample/AppDelegate.swift index c2925b7..4026923 100644 --- a/Example/ArrayDiffExample/AppDelegate.swift +++ b/Example/ArrayDiffExample/AppDelegate.swift @@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/Example/ArrayDiffExample/Utilities.swift b/Example/ArrayDiffExample/Utilities.swift index b3f9d36..685110f 100644 --- a/Example/ArrayDiffExample/Utilities.swift +++ b/Example/ArrayDiffExample/Utilities.swift @@ -1,34 +1,34 @@ import UIKit -extension NSIndexSet { +extension IndexSet { // Get a random index set in a range - static func randomIndexesInRange(range: Range, probability: Float) -> NSIndexSet { - let result = NSMutableIndexSet() - for i in range { + static func randomIndexesInRange(_ range: Range, probability: Float) -> IndexSet { + var result = IndexSet() + for i in range.lowerBound ..< range.upperBound { if Bool.random(probability) { - result.addIndex(i) + result.insert(i) } } - return result + return result as IndexSet } } extension Bool { static var trueCount = 0 static var totalCount = 0 - static func random(probability: Float) -> Bool { + static func random(_ probability: Float) -> Bool { let result = arc4random_uniform(100) < UInt32(probability * 100) if result { - trueCount++ + trueCount += 1 } - totalCount++ + totalCount += 1 return result } } extension String { static func random() -> String { - return NSUUID().UUIDString + return UUID().uuidString } } diff --git a/Example/ArrayDiffExample/ViewController.swift b/Example/ArrayDiffExample/ViewController.swift index 7e5ccdf..971f26c 100644 --- a/Example/ArrayDiffExample/ViewController.swift +++ b/Example/ArrayDiffExample/ViewController.swift @@ -17,12 +17,12 @@ class ViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() title = "ArrayDiff Demo" - navigationItem.rightBarButtonItem = UIBarButtonItem(title: "+100", style: .Plain, target: self, action: "updateTapped") + navigationItem.rightBarButtonItem = UIBarButtonItem(title: "+100", style: .plain, target: self, action: #selector(ViewController.updateTapped)) dataSource.registerReusableViewsWithTableView(tableView) tableView?.dataSource = dataSource } - @objc private func updateTapped() { + @objc fileprivate func updateTapped() { for _ in 0..<100 { dataSource.enqueueRandomUpdate(tableView, completion: { dataSource in let operationCount = dataSource.updateQueue.operationCount @@ -33,15 +33,15 @@ class ViewController: UITableViewController { } -private func createRandomSections(count: Int) -> [BasicSection] { +private func createRandomSections(_ count: Int) -> [BasicSection] { return (0.. BasicSection { +private func createRandomSection(_ count: Int) -> BasicSection { return BasicSection(name: .random(), items: createRandomItems(count)) } -private func createRandomItems(count: Int) -> [String] { +private func createRandomItems(_ count: Int) -> [String] { return (0..] static var updateLogging = false - let updateQueue: NSOperationQueue + let updateQueue: OperationQueue // The probability of each incremental update. var fickleness: Float = 0.1 override init() { - updateQueue = NSOperationQueue() + updateQueue = OperationQueue() updateQueue.maxConcurrentOperationCount = 1 - updateQueue.qualityOfService = .UserInitiated + updateQueue.qualityOfService = .userInitiated let initialSectionCount = 5 data = createRandomSections(initialSectionCount) @@ -66,16 +66,16 @@ final class ThrashingDataSource: NSObject, UITableViewDataSource { updateQueue.name = "\(self).updateQueue" } - func enqueueRandomUpdate(tableView: UITableView, completion: (ThrashingDataSource -> Void)) { - updateQueue.addOperationWithBlock { + func enqueueRandomUpdate(_ tableView: UITableView, completion: @escaping ((ThrashingDataSource) -> Void)) { + updateQueue.addOperation { self.executeRandomUpdate(tableView) - NSOperationQueue.mainQueue().addOperationWithBlock { + OperationQueue.main.addOperation { completion(self) } } } - private func executeRandomUpdate(tableView: UITableView) { + fileprivate func executeRandomUpdate(_ tableView: UITableView) { if ThrashingDataSource.updateLogging { print("Data before update: \(data.nestedDescription)") } @@ -85,33 +85,33 @@ final class ThrashingDataSource: NSObject, UITableViewDataSource { let minimumSectionCount = 3 let minimumItemCount = 5 - let _deletedItems: [NSIndexSet] = newData.enumerate().map { sectionIndex, sectionInfo in + let _deletedItems: [IndexSet] = newData.enumerated().map { sectionIndex, sectionInfo in if sectionInfo.items.count >= minimumItemCount { - let indexSet = NSIndexSet.randomIndexesInRange(0..= minimumSectionCount { - _deletedSections = NSIndexSet.randomIndexesInRange(0.. String? { + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return data[section].name } - func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCellWithIdentifier(cellID, forIndexPath: indexPath) + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) cell.textLabel?.text = data[indexPath.section].items[indexPath.item] return cell } - func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data[section].items.count } - func numberOfSectionsInTableView(tableView: UITableView) -> Int { + func numberOfSections(in tableView: UITableView) -> Int { return data.count } } diff --git a/Example/Podfile b/Example/Podfile index cc3b45f..0c1aee4 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -2,4 +2,12 @@ platform :ios, '8.0' use_frameworks! -pod 'ArrayDiff', :path => '../' +pod 'ArrayDiff', path: '../.' +target 'ArrayDiffExample' +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['SWIFT_VERSION'] = '3.0' + end + end +end