From 34f92f95441f807dfdfb74c017b7b5103564841c Mon Sep 17 00:00:00 2001 From: Frederick Pietschmann <19194800+fredpi@users.noreply.github.com> Date: Wed, 3 Apr 2019 15:30:14 +0200 Subject: [PATCH 1/8] Drop ExpressibleByStringLiteral conformance of Regex --- Frameworks/HandySwift/Structs/Regex.swift | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Frameworks/HandySwift/Structs/Regex.swift b/Frameworks/HandySwift/Structs/Regex.swift index eb9669a..9449bcc 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. From af625706946b7496dc6999b92627d7013ddace70 Mon Sep 17 00:00:00 2001 From: Frederick Pietschmann <19194800+fredpi@users.noreply.github.com> Date: Wed, 3 Apr 2019 15:33:02 +0200 Subject: [PATCH 2/8] =?UTF-8?q?Migrate=20to=20modern=20(=E2=89=A5=20Swift?= =?UTF-8?q?=204.2)=20hashing=20for=20Regex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Frameworks/HandySwift/Structs/Regex.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Frameworks/HandySwift/Structs/Regex.swift b/Frameworks/HandySwift/Structs/Regex.swift index 9449bcc..1734b82 100644 --- a/Frameworks/HandySwift/Structs/Regex.swift +++ b/Frameworks/HandySwift/Structs/Regex.swift @@ -122,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) } } From f3f27531dd7ec17cab7473264a54bb02b8c9ea4c Mon Sep 17 00:00:00 2001 From: Frederick Pietschmann <19194800+fredpi@users.noreply.github.com> Date: Wed, 3 Apr 2019 15:35:04 +0200 Subject: [PATCH 3/8] Add changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f2216d..ca362ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ### Deprecated - None. ### Removed -- None. +- Remove `ExpressibleByStringLiteral` conformance of `Regex` type to only allow initialization via `init(_:options:) throws` interface. ### Fixed - None. ### Security From 887b83211679920cc10f3807b7a4ac14a85ef7b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 30 Apr 2019 13:37:01 +0200 Subject: [PATCH 4/8] Update to Xcode 10.2 & Swift 5 --- .../Extensions/DispatchTimeIntervalExtension.swift | 3 +++ .../HandySwift/Extensions/StringExtension.swift | 9 ++++----- Frameworks/HandySwift/Structs/SortedArray.swift | 2 +- HandySwift.xcodeproj/project.pbxproj | 13 +++++++++---- .../xcshareddata/xcschemes/HandySwift iOS.xcscheme | 2 +- .../xcschemes/HandySwift macOS.xcscheme | 2 +- .../xcshareddata/xcschemes/HandySwift tvOS.xcscheme | 2 +- .../Structs/FrequencyTableTests.swift | 4 ++-- Tests/HandySwiftTests/Structs/RegexTests.swift | 7 ++++--- 9 files changed, 26 insertions(+), 18 deletions(-) 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/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/HandySwift.xcodeproj/project.pbxproj b/HandySwift.xcodeproj/project.pbxproj index fd7e324..d90378f 100644 --- a/HandySwift.xcodeproj/project.pbxproj +++ b/HandySwift.xcodeproj/project.pbxproj @@ -507,7 +507,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = Flinesoft; TargetAttributes = { 823B2B301C24AAB6007B3CDD = { @@ -538,10 +538,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 */; @@ -794,6 +795,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 +845,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 +857,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 +900,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 +1101,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 +1115,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 @@ Date: Tue, 30 Apr 2019 14:00:46 +0200 Subject: [PATCH 5/8] Add Withable protocol for convenient init/copy commands in a single line --- CHANGELOG.md | 4 ++-- .../HandySwift/Protocols/Withable.swift | 24 +++++++++++++++++++ HandySwift.xcodeproj/project.pbxproj | 16 +++++++++++++ README.md | 23 ++++++++++++++++-- UsageExamples.playground/Contents.swift | 18 +++++++++++--- 5 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 Frameworks/HandySwift/Protocols/Withable.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index ca362ef..13af3b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] ### Addedge -- None. +- New `Withable` protocol to init/copy objects and set properties in a convenient way on a single line. ### Changed -- None. +- Upgraded to Swift 5 & Xcode 10.2. ### Deprecated - None. ### Removed diff --git a/Frameworks/HandySwift/Protocols/Withable.swift b/Frameworks/HandySwift/Protocols/Withable.swift new file mode 100644 index 0000000..5ce15df --- /dev/null +++ b/Frameworks/HandySwift/Protocols/Withable.swift @@ -0,0 +1,24 @@ +// +// 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 { + init() +} + +public extension Withable { + /// Construct a new instance, setting an arbitrary subset of properties. + init(with config: (inout Self) -> Void) { + self.init() + config(&self) + } + + /// Create a copy, overriding an arbitrary subset of properties. + func with(_ config: (inout Self) -> Void) -> Self { + var copy = self + config(©) + return copy + } +} diff --git a/HandySwift.xcodeproj/project.pbxproj b/HandySwift.xcodeproj/project.pbxproj index d90378f..d35524a 100644 --- a/HandySwift.xcodeproj/project.pbxproj +++ b/HandySwift.xcodeproj/project.pbxproj @@ -21,6 +21,9 @@ 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 */; }; 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 +135,7 @@ 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 = ""; }; 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 +222,7 @@ children = ( 82812A9A1D06877B00CD5B6C /* Globals.swift */, 823B2B4B1C24AB7F007B3CDD /* Extensions */, + 8251AA1E22786CF90022B277 /* Protocols */, 8258E4541C2E0BAF0031CBFF /* Structs */, ); path = HandySwift; @@ -286,6 +291,14 @@ path = Extensions; sourceTree = ""; }; + 8251AA1E22786CF90022B277 /* Protocols */ = { + isa = PBXGroup; + children = ( + 8251AA1F22786D460022B277 /* Withable.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 8258E4541C2E0BAF0031CBFF /* Structs */ = { isa = PBXGroup; children = ( @@ -662,6 +675,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 */, @@ -702,6 +716,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 */, @@ -742,6 +757,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 */, diff --git a/README.md b/README.md index 3abe2e3..22a791f 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ alt="Version: 2.8.0 "> - Swift: 4.2 + Swift: 5.0 Platforms: iOS | tvOS | macOS | Linux @@ -504,6 +504,25 @@ testArray[try: 4] // => Optional(20) testArray[try: 20] // => nil ``` +### 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/UsageExamples.playground/Contents.swift b/UsageExamples.playground/Contents.swift index 3ab7af0..67e5665 100644 --- a/UsageExamples.playground/Contents.swift +++ b/UsageExamples.playground/Contents.swift @@ -335,6 +335,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 From d91ceb3d9862aecfc0c7729e272cb8bb9b4a8590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 30 Apr 2019 14:33:58 +0200 Subject: [PATCH 6/8] Some refactoring & documentation improvements --- .swiftlint.yml | 7 ---- .../Extensions/ArrayExtension.swift | 21 ------------ .../Extensions/CollectionExtension.swift | 21 ++++++++++++ .../HandySwift/Protocols/Withable.swift | 7 ++-- HandySwift.xcodeproj/project.pbxproj | 16 ++++++++++ README.md | 22 +++++++++++-- .../Extensions/ArrayExtensionTests.swift | 16 ---------- .../Extensions/CollectionExtensionTests.swift | 16 ++++++++++ .../Protocols/WithableTests.swift | 32 +++++++++++++++++++ UsageExamples.playground/Contents.swift | 13 ++++++++ 10 files changed, 121 insertions(+), 50 deletions(-) create mode 100644 Tests/HandySwiftTests/Protocols/WithableTests.swift 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+`?(? 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/Protocols/Withable.swift b/Frameworks/HandySwift/Protocols/Withable.swift index 5ce15df..1631969 100644 --- a/Frameworks/HandySwift/Protocols/Withable.swift +++ b/Frameworks/HandySwift/Protocols/Withable.swift @@ -5,18 +5,19 @@ /// 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() } -public extension Withable { +extension Withable { /// Construct a new instance, setting an arbitrary subset of properties. - init(with config: (inout Self) -> Void) { + public init(with config: (inout Self) -> Void) { self.init() config(&self) } /// Create a copy, overriding an arbitrary subset of properties. - func with(_ config: (inout Self) -> Void) -> Self { + public func with(_ config: (inout Self) -> Void) -> Self { var copy = self config(©) return copy diff --git a/HandySwift.xcodeproj/project.pbxproj b/HandySwift.xcodeproj/project.pbxproj index d35524a..8b00342 100644 --- a/HandySwift.xcodeproj/project.pbxproj +++ b/HandySwift.xcodeproj/project.pbxproj @@ -24,6 +24,9 @@ 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 */; }; @@ -136,6 +139,7 @@ 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; }; @@ -233,6 +237,7 @@ children = ( 82812A9E1D06926800CD5B6C /* GlobalsTests.swift */, 823B2B4E1C24ABD8007B3CDD /* Extensions */, + 8251AA23227875A10022B277 /* Protocols */, 8258E4571C2E196B0031CBFF /* Structs */, ); path = HandySwiftTests; @@ -299,6 +304,14 @@ path = Protocols; sourceTree = ""; }; + 8251AA23227875A10022B277 /* Protocols */ = { + isa = PBXGroup; + children = ( + 8251AA24227875C00022B277 /* WithableTests.swift */, + ); + path = Protocols; + sourceTree = ""; + }; 8258E4541C2E0BAF0031CBFF /* Structs */ = { isa = PBXGroup; children = ( @@ -693,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 */, @@ -734,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 */, @@ -775,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 */, diff --git a/README.md b/README.md index 22a791f..3e3d3bf 100644 --- a/README.md +++ b/README.md @@ -223,7 +223,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 +349,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 +359,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 +504,22 @@ 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). 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/UsageExamples.playground/Contents.swift b/UsageExamples.playground/Contents.swift index 67e5665..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 From 7f8ac8920d9713171d56496b4cbe3781ec8839fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 30 Apr 2019 14:58:41 +0200 Subject: [PATCH 7/8] [README.md] Fix Version badge --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3e3d3bf..c930f20 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,8 @@ alt="Codebeat Status"> - Version: 2.8.0
- + Version: 2.8.0 Swift: 5.0 From 82f7c8de800e89902473dd083cdfda0f1677d70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 30 Apr 2019 15:02:50 +0200 Subject: [PATCH 8/8] Bump version num --- CHANGELOG.md | 20 +++++++++++++++++--- Frameworks/SupportingFiles/Info.plist | 2 +- HandySwift.podspec | 3 +-- README.md | 8 ++++---- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13af3b7..6194fd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] ### Addedge +- None. +### Changed +- None. +### Deprecated +- None. +### Removed +- None. +### Fixed +- None. +### Security +- None. + +## [3.0.0] - 2019-04-30 +### Addedge - New `Withable` protocol to init/copy objects and set properties in a convenient way on a single line. ### Changed -- Upgraded to Swift 5 & Xcode 10.2. +- Upgraded to Swift 5 & Xcode 10.2. ### Deprecated - None. ### Removed @@ -18,14 +32,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - None. -## [2.8.0] +## [2.8.0] - 2019-02-11 ### Added - New `NSRange(_:in:)` initializer for converting from `Range` - 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/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/README.md b/README.md index c930f20..f77b55d 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ alt="Codebeat Status"> - Version: 2.8.0 + Version: 3.0.0 Swift: 5.0 @@ -170,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: @@ -178,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