Skip to content

Handy Swift features that didn't make it into the Swift standard library.

License

Notifications You must be signed in to change notification settings

FlineDev/HandySwift

Repository files navigation

Build Status codebeat badge Version: 2.0.0 Swift: 3.0 Platforms: iOS | tvOS | OS X License: MIT

InstallationUsageIssuesContributingLicense

HandySwift

The goal of this library is to provide handy features that didn't make it to the Swift standard library (yet) due to many different reasons. Those could be that the Swift community wants to keep the standard library clean and manageable or simply hasn't finished discussion on a specific feature yet.

If you like this, please also checkout HandyUIKit for handy UI features that we feel should have been part of the UIKit frameworks in the first place.

If you are upgrading from a previous major version of HandySwift (e.g. 1.x to 2.x) then checkout the releases section on GitHub and look out for the release notes of the last major releas(es) (e.g. 2.0.0) for an overview of the changes made. It'll save you time as hints are on how best to migrate are included there.

Installation

Currently the recommended way of installing this library is via Carthage. Cocoapods is supported, too. Swift Package Manager was targeted but didn't work in my tests.

You can of course also just include this framework manually into your project by downloading it or by using git submodules.

Carthage

Place one of the following lines to your Cartfile:

github "Flinesoft/HandySwift"

Now run carthage update. Then drag & drop the HandySwift.framework in the Carthage/build folder to your project. Now you can import HandySwift in each class you want to use its features. Refer to the Carthage README for detailed / updated instructions.

CocoaPods

Add the line pod 'HandySwift' to your target in your Podfile and make sure to include use_frameworks! at the top. The result might look similar to this:

platform :ios, '8.0'
use_frameworks!

target 'MyAppTarget' do
    pod 'HandySwift', '~> 1.0'
end

Now close your project and run pod install from the command line. Then open the .xcworkspace from within your project folder. Build your project once (with Cmd+B) to update the frameworks known to Xcode. Now you can import HandySwift in each class you want to use its features. Refer to CocoaPods.org for detailed / updates instructions.

Usage

Please have a look at the UsageExamples.playground for a complete list of features provided. Open the Playground from within the .xcworkspace in order for it to work.

Globals

Some global helpers.

delay(bySeconds:) { ... }

Runs a given closure after a delay given in seconds. Dispatch queue can be set optionally, defaults to Main thread.

var date = NSDate() // Without delay: 2016-06-07 05:38:03 +0000
delay(bySeconds: 1.5) { // Runs in Main thread by default
    date = NSDate() // Delayed by 1.5 seconds: 2016-06-07 05:38:05 +0000
}
delay(bySeconds: 5, dispatchLevel: .userInteractive) {
    date = NSDate() // Delayed by 5 seconds: 2016-06-07 05:38:08 +0000
}

IntExtension

init(randomBelow:)

Initialize random Int value below given positive value.

Int(randomBelow: 50)! // => 26
Int(randomBelow: 1_000_000)! // => 208041

IntegerTypeExtension

.times

Repeat some code block a given number of times.

3.times { array.append("Hello World!") }
// => ["Hello World!", "Hello World!", "Hello World!"]

5.times {
  let randomInt = Int(randomBelow: 1_000)!
  intArray.append(randomInt)
}
// => [481, 16, 680, 87, 912]

StringExtension

.stripped()

Returns string with whitespace characters stripped from start and end.

" \n\t BB-8 likes Rey \t\n ".stripped()
// => "BB-8 likes Rey"

.isBlank

Checks if String contains any characters other than whitespace characters.

"  \t  ".isBlank
// => true

init(randomWithLength:allowedCharactersType:)

Get random numeric/alphabetic/alphanumeric String of given length.

String(randomWithLength: 4, allowedCharactersType: .numeric) // => "8503"
String(randomWithLength: 6, allowedCharactersType: .alphabetic) // => "ysTUzU"
String(randomWithLength: 8, allowedCharactersType: .alphaNumeric) // => "2TgM5sUG"
String(randomWithLength: 10, allowedCharactersType: .allCharactersIn("?!🐲🍏✈️🎎🍜"))
// => "!🍏🐲✈️🎎🐲🍜??🍜"

ArrayExtension

.sample

Returns a random element within the array or nil if array empty.

[1, 2, 3, 4, 5].sample // => 4
([] as [Int]).sample // => nil

.sample(size:)

Returns an array with size random elements or nil if array empty.

[1, 2, 3, 4, 5].sample(size: 3) // => [2, 1, 4]
[1, 2, 3, 4, 5].sample(size: 8) // => [1, 4, 2, 4, 3, 4, 1, 5]
([] as [Int]).sample(size: 3) // => nil

.combinations(with:)

Combines each element with each element of a given other array.

[1, 2, 3].combinations(with: ["A", "B"])
// => [(1, "A"), (1, "B"), (2, "A"), (2, "B"), (3, "A"), (3, "B")]

DictionaryExtension

init?(keys:values:)

Initializes a new Dictionary and fills it with keys and values arrays or returns nil if count of arrays differ.

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) }
// => [["firstName": "Harry", "lastName": "Potter"], ["firstName": "Hermione", "lastName": "Grange"], ...]

Dictionary(keys: [1,2,3], values: [1,2,3,4,5]) // => nil

.merge(Dictionary)

Merges a given Dictionary into an existing Dictionary overriding existing values for matching keys.

var dict = ["A": "A value", "B": "Old B value"]
dict.merge(["B": "New B value", "C": "C value"])
dict // => ["A": "A value", "B": "New B value", "C": "C value"]

.merged(with: Dictionary)

Create new merged Dictionary with the given Dictionary merged into a Dictionary overriding existing values for matching keys.

let immutableDict = ["A": "A value", "B": "Old B value"]
immutableDict.merged(with: ["B": "New B value", "C": "C value"])
// => ["A": "A value", "B": "New B value", "C": "C value"]

SortedArray

The main purpose of this wrapper is to provide speed improvements for specific actions on sorted arrays.

init(array:) & .array

let unsortedArray = [5, 2, 1, 3, 0, 4]
let sortedArray = SortedArray(unsortedArray)
sortedArray.array   // => [0, 1, 2, 3, 4, 5]

.index

Finds the lowest index matching the given predicate using binary search for an improved performance (O(log n)).

SortedArray([5, 2, 1, 3, 0, 4]).index { $0 > 1 }
// => 2

.prefix(upTo:) / .prefix(through:)

SortedArray([5, 2, 1, 3, 0, 4]).prefix(upTo: 2)
// => [0, 1]

.suffix(from:)

SortedArray([5, 2, 1, 3, 0, 4]).suffix(from: 2)
// => [2, 3, 4, 5]

FrequencyTable

FrequencyTable(values: valuesArray) { valueToFrequencyClosure }

Initialize with values and closure.

struct WordFrequency {
    let word: String; let frequency: Int
    init(word: String, frequency: Int) { self.word = word; self.frequency = frequency }
}
let wordFrequencies = [
    WordFrequency(word: "Harry", frequency: 10),
    WordFrequency(word: "Hermione", frequency: 4),
    WordFrequency(word: "Ronald", frequency: 1)
]

let frequencyTable = FrequencyTable(values: wordFrequencies) { $0.frequency }
// => HandySwift.FrequencyTable<WordFrequency>

.sample

Returns a random element with frequency-based probability within the array or nil if array empty.

frequencyTable.sample
let randomWord = frequencyTable.sample.map{ $0.word }
// => "Harry"

.sample(size:)

Returns an array with size frequency-based random elements or nil if array empty.

frequencyTable.sample(size: 6)
let randomWords = frequencyTable.sample(size: 6)!.map{ $0.word }
// => ["Harry", "Ronald", "Harry", "Harry", "Hermione", "Hermione"]

Contributing

Contributions are welcome. Please just open an Issue on GitHub to discuss a point or request a feature or send a Pull Request with your suggestion. If there's a related discussion on the Swift Evolution mailing list, please also post the thread name with a link.

Pull requests with new features will only be accepted when the following are given:

  • The feature is handy but not (yet) part of the Swift standard library.
  • Tests for the new feature exist and all tests pass successfully.
  • Usage examples of the new feature are given in the Playgrounds.

Please also try to follow the same syntax and semantic in your commit messages (see rationale here).

License

This library is released under the MIT License. See LICENSE for details.