diff --git a/.swiftlint.yml b/.swiftlint.yml index 8315e41..d455616 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -34,7 +34,6 @@ opt_in_rules: - multiline_parameters - multiline_parameters_brackets - nimble_operator -- no_extension_access_modifier - number_separator - object_literal - operator_usage_whitespace @@ -280,12 +279,6 @@ custom_rules: name: "Remove Where for Negative Filtering" message: "Use `remove(where:)` instead of `filter(where not ...)` for performance." severity: warning - self_conditional_binding: - included: ".*.swift" - regex: '\s+`?\w+`?(?` - New `sum` computed property on `Sequence` types like `Array` - New `average` computed property on `Collection` types with `Int` or `Double` elements like `[Int]` - New `fullRange` and `fullNSRange` computed properties on `String` ### Changed -- Made some APIs available in wider contexts (like `sample` in `RandomAccessCollection` instead of `Array`) +- Made some APIs available in wider contexts (like `sample` in `RandomAccessCollection` instead of `Array`) ### Deprecated - None. ### Removed diff --git a/Frameworks/HandySwift/Extensions/ArrayExtension.swift b/Frameworks/HandySwift/Extensions/ArrayExtension.swift index 30e23bc..3becf2b 100644 --- a/Frameworks/HandySwift/Extensions/ArrayExtension.swift +++ b/Frameworks/HandySwift/Extensions/ArrayExtension.swift @@ -114,27 +114,6 @@ extension RandomAccessCollection where Index == Int { } } -extension Sequence where Element: Numeric { - /// Returns the sum of all elements. - public func sum() -> Element { - return reduce(0, +) - } -} - -extension Collection where Element == Int { - /// Returns the average of all elements as a Double value. - public func average() -> Double { - return reduce(0) { $0 + Double($1) } / Double(count) - } -} - -extension Collection where Element == Double { - /// Returns the average of all elements as a Double value. - public func average() -> Double { - return reduce(0, +) / Double(count) - } -} - extension Array where Element: Comparable { /// Sorts the collection in place by the order specified in the closure. /// diff --git a/Frameworks/HandySwift/Extensions/CollectionExtension.swift b/Frameworks/HandySwift/Extensions/CollectionExtension.swift index b8f377c..4a2abdd 100644 --- a/Frameworks/HandySwift/Extensions/CollectionExtension.swift +++ b/Frameworks/HandySwift/Extensions/CollectionExtension.swift @@ -17,3 +17,24 @@ extension Collection { return indices.contains(index) ? self[index] : nil } } + +extension Sequence where Element: Numeric { + /// Returns the sum of all elements. + public func sum() -> Element { + return reduce(0, +) + } +} + +extension Collection where Element == Int { + /// Returns the average of all elements as a Double value. + public func average() -> Double { + return reduce(0) { $0 + Double($1) } / Double(count) + } +} + +extension Collection where Element == Double { + /// Returns the average of all elements as a Double value. + public func average() -> Double { + return reduce(0, +) / Double(count) + } +} diff --git a/Frameworks/HandySwift/Extensions/DispatchTimeIntervalExtension.swift b/Frameworks/HandySwift/Extensions/DispatchTimeIntervalExtension.swift index 3ba2ea1..5d1326e 100644 --- a/Frameworks/HandySwift/Extensions/DispatchTimeIntervalExtension.swift +++ b/Frameworks/HandySwift/Extensions/DispatchTimeIntervalExtension.swift @@ -23,6 +23,9 @@ extension DispatchTimeInterval { case .never: return TimeInterval.infinity + + @unknown default: + fatalError("Unknown DispatchTimeInterval unit.") } } } diff --git a/Frameworks/HandySwift/Extensions/StringExtension.swift b/Frameworks/HandySwift/Extensions/StringExtension.swift index 2b26a1e..0bb0718 100644 --- a/Frameworks/HandySwift/Extensions/StringExtension.swift +++ b/Frameworks/HandySwift/Extensions/StringExtension.swift @@ -71,15 +71,14 @@ extension String { extension String { /// The type of allowed characters. - /// - /// - Numeric: Allow all numbers from 0 to 9. - /// - Alphabetic: Allow all alphabetic characters ignoring case. - /// - AlphaNumeric: Allow both numbers and alphabetic characters ignoring case. - /// - AllCharactersIn: Allow all characters appearing within the specified String. public enum AllowedCharacters { + /// Allow all numbers from 0 to 9. case numeric + /// Allow all alphabetic characters ignoring case. case alphabetic + /// Allow both numbers and alphabetic characters ignoring case. case alphaNumeric + /// Allow all characters appearing within the specified String. case allCharactersIn(String) } } diff --git a/Frameworks/HandySwift/Protocols/Withable.swift b/Frameworks/HandySwift/Protocols/Withable.swift new file mode 100644 index 0000000..1631969 --- /dev/null +++ b/Frameworks/HandySwift/Protocols/Withable.swift @@ -0,0 +1,25 @@ +// +// Created by Cihat Gündüz on 30.04.19. +// Copyright © 2019 Flinesoft. All rights reserved. +// + +/// Simple protocol to make constructing and modifying objects with multiple properties more pleasant (functional, chainable, point-free). +public protocol Withable { + /// Default initializer without parameters to support Withable protocol. + init() +} + +extension Withable { + /// Construct a new instance, setting an arbitrary subset of properties. + public init(with config: (inout Self) -> Void) { + self.init() + config(&self) + } + + /// Create a copy, overriding an arbitrary subset of properties. + public func with(_ config: (inout Self) -> Void) -> Self { + var copy = self + config(©) + return copy + } +} diff --git a/Frameworks/HandySwift/Structs/Regex.swift b/Frameworks/HandySwift/Structs/Regex.swift index eb9669a..1734b82 100644 --- a/Frameworks/HandySwift/Structs/Regex.swift +++ b/Frameworks/HandySwift/Structs/Regex.swift @@ -101,23 +101,6 @@ public struct Regex { } } -// MARK: - ExpressibleByStringLiteral -extension Regex: ExpressibleByStringLiteral { - @available(*, deprecated, message: "Use `init(_:options:) throws` instead.") - /// Creates a new `Regex` based on a string literal. - /// If the internal initialization fails, the code will crash without any option to handle the error. - /// For safe `Regex` initialization, use the `init(_: String, options: Options) throws` overload instead. - /// - /// - parameter stringLiteral: The pattern string. - public init(stringLiteral value: String) { - do { - try self.init(value) - } catch { - preconditionFailure("Not a valid regex: \(value)") - } - } -} - // MARK: - CustomStringConvertible extension Regex: CustomStringConvertible { /// Returns a string describing the regex using its pattern string. @@ -139,9 +122,9 @@ extension Regex: Equatable { // MARK: - Hashable extension Regex: Hashable { - /// Returns a unique hash value for the `Regex` instance. - public var hashValue: Int { - return regularExpression.hashValue + /// Manages hashing of the `Regex` instance. + public func hash(into hasher: inout Hasher) { + hasher.combine(regularExpression) } } diff --git a/Frameworks/HandySwift/Structs/SortedArray.swift b/Frameworks/HandySwift/Structs/SortedArray.swift index 8ef624a..75d971a 100644 --- a/Frameworks/HandySwift/Structs/SortedArray.swift +++ b/Frameworks/HandySwift/Structs/SortedArray.swift @@ -115,7 +115,7 @@ public struct SortedArray { /// - Parameters: /// - newElement: The new element to be inserted into the array. public mutating func insert(newElement: Element) { - let insertIndex = internalArray.index { $0 >= newElement } ?? internalArray.endIndex + let insertIndex = internalArray.firstIndex { $0 >= newElement } ?? internalArray.endIndex internalArray.insert(newElement, at: insertIndex) } diff --git a/Frameworks/SupportingFiles/Info.plist b/Frameworks/SupportingFiles/Info.plist index 561d538..f6c1ee6 100644 --- a/Frameworks/SupportingFiles/Info.plist +++ b/Frameworks/SupportingFiles/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.8.0 + 3.0.0 CFBundleSignature ???? diff --git a/HandySwift.podspec b/HandySwift.podspec index 9d9dfdb..3fe65e8 100644 --- a/HandySwift.podspec +++ b/HandySwift.podspec @@ -1,8 +1,7 @@ Pod::Spec.new do |s| s.name = "HandySwift" - s.version = "2.8.0 -" + s.version = "3.0.0" s.summary = "Handy Swift features that didn't make it into the Swift standard library" s.description = <<-DESC diff --git a/HandySwift.xcodeproj/project.pbxproj b/HandySwift.xcodeproj/project.pbxproj index fd7e324..8b00342 100644 --- a/HandySwift.xcodeproj/project.pbxproj +++ b/HandySwift.xcodeproj/project.pbxproj @@ -21,6 +21,12 @@ 823B2B3C1C24AAB7007B3CDD /* HandySwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 823B2B311C24AAB6007B3CDD /* HandySwift.framework */; }; 823B2B4D1C24ABA4007B3CDD /* IntExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823B2B4C1C24ABA4007B3CDD /* IntExtension.swift */; }; 823B2B501C24AC00007B3CDD /* IntExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 823B2B4F1C24AC00007B3CDD /* IntExtensionTests.swift */; }; + 8251AA2022786D460022B277 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8251AA1F22786D460022B277 /* Withable.swift */; }; + 8251AA2122786D460022B277 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8251AA1F22786D460022B277 /* Withable.swift */; }; + 8251AA2222786D460022B277 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8251AA1F22786D460022B277 /* Withable.swift */; }; + 8251AA25227875C00022B277 /* WithableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8251AA24227875C00022B277 /* WithableTests.swift */; }; + 8251AA26227875C00022B277 /* WithableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8251AA24227875C00022B277 /* WithableTests.swift */; }; + 8251AA27227875C00022B277 /* WithableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8251AA24227875C00022B277 /* WithableTests.swift */; }; 8258E4561C2E0C140031CBFF /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8258E4551C2E0C140031CBFF /* SortedArray.swift */; }; 8258E4591C2E1ACE0031CBFF /* SortedArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8258E4581C2E1ACE0031CBFF /* SortedArrayTests.swift */; }; 825EFDD41C3333B000558497 /* HandySwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 825EFDCA1C3333B000558497 /* HandySwift.framework */; }; @@ -132,6 +138,8 @@ 823B2B421C24AAB7007B3CDD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 823B2B4C1C24ABA4007B3CDD /* IntExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntExtension.swift; sourceTree = ""; }; 823B2B4F1C24AC00007B3CDD /* IntExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntExtensionTests.swift; sourceTree = ""; }; + 8251AA1F22786D460022B277 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; + 8251AA24227875C00022B277 /* WithableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithableTests.swift; sourceTree = ""; }; 8258E4551C2E0C140031CBFF /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; 8258E4581C2E1ACE0031CBFF /* SortedArrayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArrayTests.swift; sourceTree = ""; }; 825EFDCA1C3333B000558497 /* HandySwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HandySwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -218,6 +226,7 @@ children = ( 82812A9A1D06877B00CD5B6C /* Globals.swift */, 823B2B4B1C24AB7F007B3CDD /* Extensions */, + 8251AA1E22786CF90022B277 /* Protocols */, 8258E4541C2E0BAF0031CBFF /* Structs */, ); path = HandySwift; @@ -228,6 +237,7 @@ children = ( 82812A9E1D06926800CD5B6C /* GlobalsTests.swift */, 823B2B4E1C24ABD8007B3CDD /* Extensions */, + 8251AA23227875A10022B277 /* Protocols */, 8258E4571C2E196B0031CBFF /* Structs */, ); path = HandySwiftTests; @@ -286,6 +296,22 @@ path = Extensions; sourceTree = ""; }; + 8251AA1E22786CF90022B277 /* Protocols */ = { + isa = PBXGroup; + children = ( + 8251AA1F22786D460022B277 /* Withable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + 8251AA23227875A10022B277 /* Protocols */ = { + isa = PBXGroup; + children = ( + 8251AA24227875C00022B277 /* WithableTests.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 8258E4541C2E0BAF0031CBFF /* Structs */ = { isa = PBXGroup; children = ( @@ -507,7 +533,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = Flinesoft; TargetAttributes = { 823B2B301C24AAB6007B3CDD = { @@ -538,10 +564,11 @@ }; buildConfigurationList = 823B2B2B1C24AAB6007B3CDD /* Build configuration list for PBXProject "HandySwift" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 823B2B271C24AAB6007B3CDD; productRefGroup = 823B2B321C24AAB6007B3CDD /* Products */; @@ -661,6 +688,7 @@ 3F95C8D220F22A3C0045AFD0 /* CollectionExtension.swift in Sources */, 823B2B4D1C24ABA4007B3CDD /* IntExtension.swift in Sources */, C5C89B9420B0A0C10048B07C /* Weak.swift in Sources */, + 8251AA2022786D460022B277 /* Withable.swift in Sources */, 82CAE2921C2ED1A200F934A7 /* StringExtension.swift in Sources */, 82CAE2961C2EE64900F934A7 /* ArrayExtension.swift in Sources */, C5CFB6AC20B0A70300830511 /* Unowned.swift in Sources */, @@ -678,6 +706,7 @@ 827599641E520FB800787F99 /* DispatchTimeIntervalExtensionTests.swift in Sources */, 8258E4591C2E1ACE0031CBFF /* SortedArrayTests.swift in Sources */, A11830D71E58A11D00CBE087 /* TimeIntervalExtensionTests.swift in Sources */, + 8251AA25227875C00022B277 /* WithableTests.swift in Sources */, 823B2B501C24AC00007B3CDD /* IntExtensionTests.swift in Sources */, 82CAE2941C2ED5E000F934A7 /* StringExtensionTests.swift in Sources */, 82CAE2981C2EE95200F934A7 /* ArrayExtensionTests.swift in Sources */, @@ -701,6 +730,7 @@ 825EFE051C33358400558497 /* StringExtension.swift in Sources */, 82E21E8E211AF9960061EB1B /* CollectionExtension.swift in Sources */, C5CFB6AF20B0A78F00830511 /* Weak.swift in Sources */, + 8251AA2122786D460022B277 /* Withable.swift in Sources */, A1F221651E3CC05100419B06 /* DispatchTimeIntervalExtension.swift in Sources */, 825EFE021C33358400558497 /* SortedArray.swift in Sources */, 825EFE031C33358400558497 /* IntExtension.swift in Sources */, @@ -718,6 +748,7 @@ 827599651E520FB800787F99 /* DispatchTimeIntervalExtensionTests.swift in Sources */, 825EFE111C3335A400558497 /* StringExtensionTests.swift in Sources */, A11830D81E58A11D00CBE087 /* TimeIntervalExtensionTests.swift in Sources */, + 8251AA26227875C00022B277 /* WithableTests.swift in Sources */, 825EFE0E1C3335A400558497 /* SortedArrayTests.swift in Sources */, 825EFE121C3335A400558497 /* ArrayExtensionTests.swift in Sources */, 825EFE0F1C3335A400558497 /* IntExtensionTests.swift in Sources */, @@ -741,6 +772,7 @@ 825EFE0B1C33358500558497 /* StringExtension.swift in Sources */, 82E21E8F211AF9970061EB1B /* CollectionExtension.swift in Sources */, C5CFB6B020B0A79000830511 /* Weak.swift in Sources */, + 8251AA2222786D460022B277 /* Withable.swift in Sources */, A1F221661E3CC05100419B06 /* DispatchTimeIntervalExtension.swift in Sources */, 825EFE081C33358500558497 /* SortedArray.swift in Sources */, 825EFE091C33358500558497 /* IntExtension.swift in Sources */, @@ -758,6 +790,7 @@ 827599661E520FB800787F99 /* DispatchTimeIntervalExtensionTests.swift in Sources */, 825EFE171C3335A500558497 /* StringExtensionTests.swift in Sources */, A11830D91E58A11D00CBE087 /* TimeIntervalExtensionTests.swift in Sources */, + 8251AA27227875C00022B277 /* WithableTests.swift in Sources */, 825EFE141C3335A500558497 /* SortedArrayTests.swift in Sources */, 825EFE181C3335A500558497 /* ArrayExtensionTests.swift in Sources */, 825EFE151C3335A500558497 /* IntExtensionTests.swift in Sources */, @@ -794,6 +827,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -843,7 +877,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -855,6 +889,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -897,7 +932,7 @@ SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; @@ -1098,6 +1133,7 @@ PRODUCT_MODULE_NAME = HandySwift_tvOS_Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.1; }; name = Debug; @@ -1111,6 +1147,7 @@ PRODUCT_MODULE_NAME = HandySwift_tvOS_Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.1; }; name = Release; diff --git a/HandySwift.xcodeproj/xcshareddata/xcschemes/HandySwift iOS.xcscheme b/HandySwift.xcodeproj/xcshareddata/xcschemes/HandySwift iOS.xcscheme index d47b0d0..34be75d 100644 --- a/HandySwift.xcodeproj/xcshareddata/xcschemes/HandySwift iOS.xcscheme +++ b/HandySwift.xcodeproj/xcshareddata/xcschemes/HandySwift iOS.xcscheme @@ -1,6 +1,6 @@ - Version: 2.8.0
- + Version: 3.0.0 - Swift: 4.2 + Swift: 5.0 Platforms: iOS | tvOS | macOS | Linux @@ -172,7 +170,7 @@ Converting from `NSRange` to `Range` became simple in Swift 4: ``` Swift let string = "Hello World!" let nsRange = NSRange(location: 0, length: 10) -let swiftRange = Range(nsRange, in: string) +let swiftRange = Range(nsRange, in: string) ``` The opposite is now also possible with this extension: @@ -180,7 +178,7 @@ The opposite is now also possible with this extension: ``` Swift let string = "Hello World!" let swiftRange: Range = string.fullRange -let nsRange = NSRange(swiftRange, in: string) +let nsRange = NSRange(swiftRange, in: string) ``` ### ArrayExtension @@ -223,7 +221,7 @@ let structure = ["firstName", "lastName"] let dataEntries = [["Harry", "Potter"], ["Hermione", "Granger"], ["Ron", "Weasley"]] Dictionary(keys: structure, values: dataEntries[0]) // => ["firstName": "Harry", "lastName": "Potter"] -dataEntries.map{ Dictionary(keys: structure, values: $0) } +dataEntries.map { Dictionary(keys: structure, values: $0) } // => [["firstName": "Harry", "lastName": "Potter"], ["firstName": "Hermione", "lastName": "Grange"], ...] Dictionary(keys: [1,2,3], values: [1,2,3,4,5]) // => nil @@ -349,7 +347,7 @@ Returns a random element with frequency-based probability within the array or ni ``` Swift frequencyTable.sample -let randomWord = frequencyTable.sample.map{ $0.word } +let randomWord = frequencyTable.sample.map { $0.word } // => "Harry" ``` @@ -359,7 +357,7 @@ Returns an array with `size` frequency-based random elements or nil if array emp ``` Swift frequencyTable.sample(size: 6) -let randomWords = frequencyTable.sample(size: 6)!.map{ $0.word } +let randomWords = frequencyTable.sample(size: 6)!.map { $0.word } // => ["Harry", "Ronald", "Harry", "Harry", "Hermione", "Hermione"] ``` @@ -504,6 +502,41 @@ testArray[try: 4] // => Optional(20) testArray[try: 20] // => nil ``` +#### .sum() +Returns the sum of all elements. The return type is determined by the numeric elements, e.g. Int for [Int]. +NOTE: Only available for `Numeric` types. +``` swift +[0, 1, 2, 3, 4].sum() // => 10 +[0.5, 1.5, 2.5].sum() // => 4.5 +``` + +#### .average() +Returns the average of all elements as a Double value. +NOTE: Only available for `Int` and `Double` collections. +``` swift +[10, 20, 30, 40].average() // => 25.0 +[10.75, 20.75, 30.25, 40.25].average() // => 25.5 +``` + +### Withable +Simple protocol to make constructing and modifying objects with multiple properties more pleasant (functional, chainable, point-free). + +``` swift +struct Foo: Withable { + var bar: Int = 0 + var baz: Bool = false +} + +// Construct a foo, setting an arbitrary subset of properties +let foo = Foo { $0.bar = 5 } + +// Make a copy of foo, overriding an arbitrary subset of properties +let foo2 = foo.with { $0.bar = 7; $0.baz = true } + +foo.bar // => 5 +foo2.bar // => 7 +``` + ## Contributing diff --git a/Tests/HandySwiftTests/Extensions/ArrayExtensionTests.swift b/Tests/HandySwiftTests/Extensions/ArrayExtensionTests.swift index a6998b5..3f491ec 100644 --- a/Tests/HandySwiftTests/Extensions/ArrayExtensionTests.swift +++ b/Tests/HandySwiftTests/Extensions/ArrayExtensionTests.swift @@ -73,20 +73,4 @@ class ArrayExtensionTests: XCTestCase { XCTAssertEqual(testArray[index], sortedArray[index]) } } - - func testSum() { - let intArray = [1, 2, 3, 4, 5] - XCTAssertEqual(intArray.sum(), 15) - - let doubleArray = [1.0, 2.0, 3.0, 4.0, 5.0] - XCTAssertEqual(doubleArray.sum(), 15.0, accuracy: 0.001) - } - - func testAverage() { - let intArray = [1, 2, 10] - XCTAssertEqual(intArray.average(), 4.333, accuracy: 0.001) - - let doubleArray = [1.0, 2.0, 10.0] - XCTAssertEqual(doubleArray.average(), 4.333, accuracy: 0.001) - } } diff --git a/Tests/HandySwiftTests/Extensions/CollectionExtensionTests.swift b/Tests/HandySwiftTests/Extensions/CollectionExtensionTests.swift index e8cb251..88f2f3d 100644 --- a/Tests/HandySwiftTests/Extensions/CollectionExtensionTests.swift +++ b/Tests/HandySwiftTests/Extensions/CollectionExtensionTests.swift @@ -18,4 +18,20 @@ class CollectionExtensionTests: XCTestCase { let secondTestArray = [Int]() XCTAssertNil(secondTestArray[try: 0]) } + + func testSum() { + let intArray = [1, 2, 3, 4, 5] + XCTAssertEqual(intArray.sum(), 15) + + let doubleArray = [1.0, 2.0, 3.0, 4.0, 5.0] + XCTAssertEqual(doubleArray.sum(), 15.0, accuracy: 0.001) + } + + func testAverage() { + let intArray = [1, 2, 10] + XCTAssertEqual(intArray.average(), 4.333, accuracy: 0.001) + + let doubleArray = [1.0, 2.0, 10.0] + XCTAssertEqual(doubleArray.average(), 4.333, accuracy: 0.001) + } } diff --git a/Tests/HandySwiftTests/Protocols/WithableTests.swift b/Tests/HandySwiftTests/Protocols/WithableTests.swift new file mode 100644 index 0000000..5202bbe --- /dev/null +++ b/Tests/HandySwiftTests/Protocols/WithableTests.swift @@ -0,0 +1,32 @@ +// +// Created by Cihat Gündüz on 30.04.19. +// Copyright © 2019 Flinesoft. All rights reserved. +// + +@testable import HandySwift +import XCTest + +private struct TextFile: Withable { + var contents: String = "" + var linesCount: Int = 0 +} + +class WithableTests: XCTestCase { + func testInitWith() { + let textFile = TextFile { $0.contents = "Text"; $0.linesCount = 5 } + XCTAssertEqual(textFile.contents, "Text") + XCTAssertEqual(textFile.linesCount, 5) + } + + func testWith() { + let textFile = TextFile() + XCTAssertEqual(textFile.contents, "") + XCTAssertEqual(textFile.linesCount, 0) + + let modifiedTextFile = textFile.with { $0.contents = "Text"; $0.linesCount = 5 } + XCTAssertEqual(textFile.contents, "") + XCTAssertEqual(textFile.linesCount, 0) + XCTAssertEqual(modifiedTextFile.contents, "Text") + XCTAssertEqual(modifiedTextFile.linesCount, 5) + } +} diff --git a/Tests/HandySwiftTests/Structs/FrequencyTableTests.swift b/Tests/HandySwiftTests/Structs/FrequencyTableTests.swift index 9cbcd2b..4492355 100644 --- a/Tests/HandySwiftTests/Structs/FrequencyTableTests.swift +++ b/Tests/HandySwiftTests/Structs/FrequencyTableTests.swift @@ -9,7 +9,7 @@ import XCTest class FrequencyTableTests: XCTestCase { func testSample() { let values = ["Harry", "Hermione", "Ronald"] - let frequencyTable = FrequencyTable(values: values) { [5, 10, 1][values.index(of: $0)!] } + let frequencyTable = FrequencyTable(values: values) { [5, 10, 1][values.firstIndex(of: $0)!] } var allSamples: [String] = [] @@ -30,7 +30,7 @@ class FrequencyTableTests: XCTestCase { func testSampleWithSize() { let values = ["Harry", "Hermione", "Ronald"] - let frequencyTable = FrequencyTable(values: values) { [5, 10, 1][values.index(of: $0)!] } + let frequencyTable = FrequencyTable(values: values) { [5, 10, 1][values.firstIndex(of: $0)!] } let allSamples: [String] = frequencyTable.sample(size: 16_000)! diff --git a/Tests/HandySwiftTests/Structs/RegexTests.swift b/Tests/HandySwiftTests/Structs/RegexTests.swift index a222242..9e4d889 100644 --- a/Tests/HandySwiftTests/Structs/RegexTests.swift +++ b/Tests/HandySwiftTests/Structs/RegexTests.swift @@ -90,9 +90,10 @@ class RegexTests: XCTestCase { func testMatchRange() { let regex = try? Regex("[1-9]+") - let firstMatchRange = regex?.firstMatch(in: "abc5def")?.range - XCTAssertEqual(firstMatchRange?.lowerBound.encodedOffset, 3) - XCTAssertEqual(firstMatchRange?.upperBound.encodedOffset, 4) + let text = "abc5def" + let firstMatchRange = regex?.firstMatch(in: text)?.range + XCTAssertEqual(firstMatchRange?.lowerBound.utf16Offset(in: text), 3) + XCTAssertEqual(firstMatchRange?.upperBound.utf16Offset(in: text), 4) } func testMatchCaptures() { diff --git a/UsageExamples.playground/Contents.swift b/UsageExamples.playground/Contents.swift index 3ab7af0..605a5ce 100644 --- a/UsageExamples.playground/Contents.swift +++ b/UsageExamples.playground/Contents.swift @@ -95,6 +95,18 @@ let arrayForTry = [0, 1, 2, 3, 20] arrayForTry[try: 4] arrayForTry[try: 20] +//: ### .sum() +//: Returns the sum of all elements. The return type is determined by the numeric elements, e.g. Int for [Int]. +//: NOTE: Only available for `Numeric` types. +[0, 1, 2, 3, 4].sum() +[0.5, 1.5, 2.5].sum() + +//: ### .average() +//: Returns the average of all elements as a Double value. +//: NOTE: Only available for `Int` and `Double` collections. +[10, 20, 30, 40].average() +[10.75, 20.75, 30.25, 40.25].average() + //: ## DictionaryExtension //: ### init?(keys:values:) //: Initializes a new `Dictionary` and fills it with keys and values arrays or returns nil if count of arrays differ. @@ -165,6 +177,7 @@ unsortedArray // now sorted //: ### .timeInterval //: Returns a `TimeInterval` object from a `DispatchTimeInterval`. +import Dispatch DispatchTimeInterval.milliseconds(500).timeInterval //: ## TimeIntervalExtension @@ -335,6 +348,18 @@ print(weak) var unowned = Unowned(text) print(unowned) -//: ### Accessing inner Reference -//: Access the inner wrapped reference with the `value` property. -print(unowned.value) +//: ## Withable +//: Simple protocol to make constructing and modifying objects with multiple properties more pleasant (functional, chainable, point-free). +struct Foo: Withable { + var bar: Int = 0 + var baz: Bool = false +} + +// Construct a foo, setting an arbitrary subset of properties +let foo = Foo { $0.bar = 5 } + +// Make a copy of foo, overriding an arbitrary subset of properties +let foo2 = foo.with { $0.bar = 7; $0.baz = true } + +foo.bar +foo2.bar