diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..5186d07 --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +4.0 diff --git a/Example/Fuse.xcodeproj/project.pbxproj b/Example/Fuse.xcodeproj/project.pbxproj index e153481..692efab 100644 --- a/Example/Fuse.xcodeproj/project.pbxproj +++ b/Example/Fuse.xcodeproj/project.pbxproj @@ -216,16 +216,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0920; ORGANIZATIONNAME = CocoaPods; TargetAttributes = { 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; - LastSwiftMigration = 0820; + LastSwiftMigration = 0920; }; 607FACE41AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; - LastSwiftMigration = 0820; + LastSwiftMigration = 0920; TestTargetID = 607FACCF1AFB9204008FA782; }; }; @@ -420,14 +420,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; 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_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -456,6 +462,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; }; name = Debug; }; @@ -467,14 +474,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; 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_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -495,6 +508,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; VALIDATE_PRODUCT = YES; }; name = Release; @@ -509,7 +523,7 @@ MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -523,7 +537,7 @@ MODULE_NAME = ExampleApp; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -543,7 +557,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -559,7 +573,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Example/Fuse.xcodeproj/xcshareddata/xcschemes/Fuse-Example.xcscheme b/Example/Fuse.xcodeproj/xcshareddata/xcschemes/Fuse-Example.xcscheme index 886068d..1f7090d 100644 --- a/Example/Fuse.xcodeproj/xcshareddata/xcschemes/Fuse-Example.xcscheme +++ b/Example/Fuse.xcodeproj/xcshareddata/xcschemes/Fuse-Example.xcscheme @@ -1,6 +1,6 @@ @@ -36,6 +37,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 3a889a5..477d57b 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -67,8 +67,8 @@ class Tests: XCTestCase { func testProtocolWeightedSearch1() { class Book: Fuseable { - dynamic let author: String - dynamic let title: String + @objc dynamic let author: String + @objc dynamic let title: String public init (author: String, title: String) { self.author = author @@ -98,8 +98,8 @@ class Tests: XCTestCase { func testProtocolWeightedSearch2() { class Book: Fuseable { - dynamic let author: String - dynamic let title: String + @objc dynamic let author: String + @objc dynamic let title: String public init (author: String, title: String) { self.author = author @@ -162,4 +162,11 @@ class Tests: XCTestCase { } } } + + func testItHandlesLongInputs() { + let threeHundredCharacters = String(repeating: "abc", count: 100) + let fuse = Fuse() + _ = fuse.search(threeHundredCharacters, in: "placeholder") + XCTAssertTrue(true, "Always pass when reaching here -- expected no exceptions from the long input") + } } diff --git a/Fuse/Classes/Fuse.swift b/Fuse/Classes/Fuse.swift index aecb5d8..6ac36d9 100644 --- a/Fuse/Classes/Fuse.swift +++ b/Fuse/Classes/Fuse.swift @@ -74,7 +74,7 @@ public class Fuse { /// - Returns: A tuple containing pattern metadata public func createPattern (from aString: String) -> Pattern? { let pattern = self.isCaseSensitive ? aString : aString.lowercased() - let len = pattern.characters.count + let len = min(pattern.count, maxPatternLength) if len == 0 { return nil @@ -109,7 +109,7 @@ public class Fuse { text = text.lowercased() } - let textLength = text.characters.count + let textLength = text.count // Exact match if (pattern.text == text) { @@ -122,7 +122,7 @@ public class Fuse { var bestLocation: Int? = { if let index = text.index(of: pattern.text, startingFrom: location) { - return text.characters.distance(from: text.startIndex, to: index) + return text.distance(from: text.startIndex, to: index) } return nil }() @@ -137,7 +137,7 @@ public class Fuse { // What about in the other direction? (speed up) bestLocation = { if let index = text.lastIndexOf(pattern.text, position: location + pattern.len) { - return text.characters.distance(from: text.startIndex, to: index) + return text.distance(from: text.startIndex, to: index) } return nil }() @@ -176,7 +176,7 @@ public class Fuse { // Initialize the bit array var bitArr = [Int](repeating: 0, count: finish + 2) - bitArr[finish + 1] = (1 << i) - 1 + bitArr[finish + 1] = (1 << i) &- 1 for j in (start...finish).reversed() { let currentLocation = j - 1 diff --git a/Fuse/Classes/FuseUtilities.swift b/Fuse/Classes/FuseUtilities.swift index db44a4c..99be1a9 100644 --- a/Fuse/Classes/FuseUtilities.swift +++ b/Fuse/Classes/FuseUtilities.swift @@ -19,7 +19,7 @@ class FuseUtilities { /// - Parameter scoreTextLength: Coerced version of text's length. /// - Returns: Overall score for match (0.0 = good, 1.0 = bad). static func calculateScore(_ pattern: String, e: Int, x: Int, loc: Int, distance: Int) -> Double { - let len = pattern.characters.count + let len = pattern.count let accuracy = Double(e) / Double(len) let proximity = abs(x - loc) if (distance == 0) { @@ -33,13 +33,13 @@ class FuseUtilities { /// - Parameter pattern: The text to encode. /// - Returns: Hash of character locations. static func calculatePatternAlphabet(_ pattern: String) -> [Character: Int] { - let len = pattern.characters.count + let len = pattern.count var mask = [Character: Int]() - for char in pattern.characters { + for char in pattern { mask[char] = 0 } for i in 0...len-1 { - let c = pattern[pattern.characters.index(pattern.startIndex, offsetBy: i)] + let c = pattern[pattern.index(pattern.startIndex, offsetBy: i)] mask[c] = mask[c]! | (1 << (len - i - 1)) } return mask diff --git a/Fuse/Classes/String+Fuse.swift b/Fuse/Classes/String+Fuse.swift index 29be016..e37be47 100644 --- a/Fuse/Classes/String+Fuse.swift +++ b/Fuse/Classes/String+Fuse.swift @@ -14,10 +14,10 @@ extension String { /// - Parameter index: some index /// - Returns: the character at the provided index func char(at index: Int) -> Character? { - if index >= self.characters.count { + if index >= self.count { return nil } - return self[self.characters.index(self.startIndex, offsetBy: index)] + return self[self.index(self.startIndex, offsetBy: index)] } /// Searches and returns the index within the string of the first occurrence of `searchStr`. @@ -27,7 +27,7 @@ extension String { /// /// - Returns: The index within the calling String object of the first occurrence of `searchStr`, starting the search at `position`. Returns `nil` if the value is not found. func index(of aString: String, startingFrom position: Int? = 0) -> String.Index? { - let start: String.Index = self.characters.index(self.startIndex, offsetBy: position!) + let start: String.Index = self.index(self.startIndex, offsetBy: position!) let range: Range = Range(start.. String.Index? { - let len = self.characters.count + let len = self.count let start = min(max(position!, 0), len) - let searchLen = searchStr.characters.count - let r: Range = Range(self.startIndex.. = Range(self.startIndex..