= subcount && sign(self[j - subcount], temp) {
23 | self[j] = self[j - subcount]
24 | j -= subcount
25 | }
26 | self[j] = temp
27 | }
28 | subcount /= 2
29 | }
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIBezierPath/UIBezierPath+RandomPoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIBezierPath+RandomPoint.swift
3 | // extensions_kit
4 | //
5 | // Created by Astemir Eleev on 09/05/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import CoreGraphics
10 | import class UIKit.UIBezierPath
11 |
12 | extension UIBezierPath {
13 |
14 | func randomPoint(inset: CGFloat) -> CGPoint {
15 | var position: CGPoint!
16 | repeat {
17 | let x = CGFloat.random(in: bounds.origin.x + inset ... bounds.origin.x + bounds.size.width - inset)
18 | let y = CGFloat.random(in: bounds.origin.y + inset ... bounds.origin.y + bounds.size.height - inset)
19 | position = CGPoint(x: x, y: y)
20 | } while (!contains(position))
21 | return position
22 | }
23 |
24 | func randomPointNormalized(inset: CGFloat) -> CGPoint {
25 | let point = randomPoint(inset: inset)
26 | return CGPoint(x: point.x - bounds.origin.x - bounds.size.width / 2, y: point.y - bounds.origin.y - bounds.size.height / 2)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Int/Int+Roman.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int+Roman.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Int {
12 |
13 | public func romanNumeral() -> String? {
14 | guard self > 0 else {
15 | return nil
16 | }
17 | let romanValues = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
18 | let arabicValues = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
19 |
20 | var romanValue = ""
21 | var startingValue = self
22 |
23 | for (index, romanChar) in romanValues.enumerated() {
24 | let arabicValue = arabicValues[index]
25 | let div = startingValue / arabicValue
26 | if div > 0 {
27 | for _ in 0.. 0 {
28 | isDone = false
29 | }
30 | }
31 | digits *= base
32 | self = buckets.flatMap { $0 }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationDataStructures/DoublyLinkedList/DoublyNode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoublyNode.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 01/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class DoublyNode: CustomStringConvertible, CustomDebugStringConvertible {
12 |
13 | // MARK: - Conformances to CustomStringConvertible & CustomDebugStringConvertible protocols
14 |
15 | var description: String {
16 | return representation()
17 | }
18 |
19 | var debugDescription: String {
20 | return representation()
21 | }
22 |
23 | private func representation() -> String {
24 | return "\(String(describing: data))"
25 | }
26 |
27 | // MARK: - Properties
28 |
29 | var next: DoublyNode?
30 | var data: T
31 | var previous: DoublyNode?
32 |
33 | // MARK: - Initailziers
34 |
35 | init(data: T, next: DoublyNode?, previous: DoublyNode?) {
36 | self.data = data
37 | self.next = next
38 | self.previous = previous
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Dictionary/Dictionary+JSON.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dictionary+JSON.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Dictionary {
12 |
13 | public func jsonData(prettify: Bool = false) -> Data? {
14 | guard JSONSerialization.isValidJSONObject(self) else {
15 | return nil
16 | }
17 | let options = (prettify == true) ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization.WritingOptions()
18 | return try? JSONSerialization.data(withJSONObject: self, options: options)
19 | }
20 |
21 | public func jsonString(prettify: Bool = false) -> String? {
22 | guard JSONSerialization.isValidJSONObject(self) else { return nil }
23 | let options = (prettify == true) ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization.WritingOptions()
24 | guard let jsonData = try? JSONSerialization.data(withJSONObject: self, options: options) else { return nil }
25 | return String(data: jsonData, encoding: .utf8)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIImage/UIImage+SolidColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+SolidColor.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 30/04/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit.UIImage
10 |
11 | // MARK: - Extension for creating UIImage from a UIColor
12 | extension UIImage {
13 |
14 | public convenience init?(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) {
15 | let rect = CGRect(origin: .zero, size: size)
16 | UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
17 | color.setFill()
18 | UIRectFill(rect)
19 | let image = UIGraphicsGetImageFromCurrentImageContext()
20 | UIGraphicsEndImageContext()
21 |
22 | guard let cgImage = image?.cgImage else { return nil }
23 | self.init(cgImage: cgImage)
24 | }
25 |
26 | func isImageWhite() -> Bool? {
27 | guard let whiteImage = UIImage(color: .white, size: self.size), let whiteImageData = whiteImage.jpegData(compressionQuality: 1.0), let imageData = self.jpegData(compressionQuality: 1.0) else {
28 | return nil
29 | }
30 | return whiteImageData == imageData
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIImageView/UIImageView+DownloadFromURL.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageView+DownloadFromURL.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 01/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit.UIImageView
10 |
11 | extension UIImageView {
12 |
13 | func downloadImageFrom(url: String, with defaultImage: UIImage? = nil, completion: @escaping(_ image: UIImage)->Void) {
14 | guard let url = URL(string: url) else { return }
15 | URLSession.shared.dataTask(with: url, completionHandler: { data, response, error -> Void in
16 |
17 | guard let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
18 | let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
19 | let data = data, error == nil,
20 | let image = UIImage(data: data)
21 | else {
22 | if let defaultImage = defaultImage {
23 | completion(defaultImage)
24 | }
25 | return
26 | }
27 |
28 | completion(image)
29 | }).resume()
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Date/Date+FirstLast.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date+FirstLast.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 28/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Date {
12 |
13 | public var firstDayOfWeek: Date {
14 | var beginningOfWeek = Date()
15 | var interval = TimeInterval()
16 |
17 | _ = Calendar.current.dateInterval(of: .weekOfYear, start: &beginningOfWeek, interval: &interval, for: self)
18 | return beginningOfWeek
19 | }
20 |
21 | public var startOfDay: Date {
22 | return Calendar.current.startOfDay(for: self)
23 | }
24 |
25 | public var endOfDay: Date {
26 | let cal = Calendar.current
27 | var components = DateComponents()
28 | components.day = 1
29 | return cal.date(byAdding: components, to: self.startOfDay)!.addingTimeInterval(-1)
30 | }
31 |
32 | public var numberOfDaysInMonth: Int {
33 | let calendar = Calendar.current
34 | let dayRange = (calendar as NSCalendar).range(of: NSCalendar.Unit.day, in: NSCalendar.Unit.month, for: self)
35 |
36 | return dayRange.length
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/BoolTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BoolTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 14/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class BoolTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testInt", testInt)
16 | ]
17 |
18 | override func setUp() {
19 | super.setUp()
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | override func tearDown() {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | super.tearDown()
26 | }
27 |
28 | func testInt() {
29 | let falseBool = false
30 | XCTAssert(falseBool.int == 0)
31 |
32 | let trueBool = true
33 | XCTAssert(trueBool.int == 1)
34 | }
35 |
36 | func testPerformanceExample() {
37 | // This is an example of a performance test case.
38 | self.measure {
39 | // Put the code you want to measure the time of here.
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/URL/URL+QRImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+QRImage.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 08/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit.UIImage
11 | import CoreImage.CIImage
12 | import ExtendedCoreImage
13 |
14 | extension URL {
15 |
16 | /// Creates a QR image from the `absoluteString` of the `URL`
17 | ///
18 | /// - Parameters:
19 | /// - scale: is a `CGPoint` that specifies the scale affine transformation of the final QR image
20 | /// - color: is a `UIColor` that specifies the tint color (if any e.g. non `nil`) of the final QR image
21 | /// - Returns: optional `UIImage` instance holding the QR image or `nil` if `CIImage` could not be created
22 | public func qrImage(scaledBy scale: CGPoint = CGPoint(x: 10, y: 10), tint color: UIColor? = nil) -> UIImage? {
23 | var ciImage: CIImage? = CIImage.qrImage(from: absoluteString, scaledBy: scale)
24 |
25 | if let someColor = color {
26 | ciImage = ciImage?.tinted(by: someColor)
27 | }
28 |
29 | guard let image = ciImage else { return nil }
30 | return UIImage(ciImage: image)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/Array+UnionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+UnionTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 06/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | final class ArrayUnionTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testUnion", testUnion)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testUnion() {
27 | let firstArray = [1,2,4,5,6,7,8]
28 | let secondArry = [8,9,10]
29 |
30 | let unionedIntArray = firstArray.union(values: secondArry)
31 | XCTAssert(unionedIntArray == [1, 2, 4, 5, 6, 7, 8, 9, 10])
32 | }
33 |
34 | func testPerformanceExample() {
35 | // This is an example of a performance test case.
36 | self.measure {
37 | // Put the code you want to measure the time of here.
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UICollectionView/UICollectionView+CustomCellRegistration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+CustomCellRegistration.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 31/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UICollectionView {
12 |
13 | /// Registers custom UICollectionViewCell for a UICollectionView instance. UICollectionViewCell needs to be located in current Bundle
14 | ///
15 | /// Usage example:
16 | /// let collectionView = UICollectionView()
17 | /// collectionView.register(cellNamed: UICollectionViewCell.self, reuseableIdentifier: "item-cell")
18 | ///
19 | /// - Parameters:
20 | /// - name: is a name of a UICollectionView that is going to be registered (hint: use UICollectionView.self as a parameter in order to avoid any misspellings)
21 | /// - id: is a reusable identifier for a UICollectionView cell
22 | public func register(cell name: UICollectionViewCell.Type, reusableId id: String) {
23 | let nibName = String(describing: name)
24 | let bundle = Bundle(identifier: nibName)
25 | let cellNib = UINib(nibName: nibName, bundle: bundle)
26 | self.register(cellNib, forCellWithReuseIdentifier: id)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIImage/UIImage+LandscapeCameraOrientationFix.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+LandscapeCameraOrientationFix.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 30/04/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit.UIImage
10 |
11 | // MARK: - This extension for UIImage fixes image orientation for cases when the iamge was captured using AVFoundation in landscape interface orientation
12 | extension UIImage {
13 |
14 | func landscapeCameraCaptureOrientationFix(for interfaceOrinetation: UIInterfaceOrientation) -> UIImage? {
15 | var imageOrientation: UIImage.Orientation!
16 | var shouldOrient: Bool = false
17 |
18 | if interfaceOrinetation == .landscapeRight {
19 | imageOrientation = UIImage.Orientation.left
20 | shouldOrient = !shouldOrient
21 | } else if interfaceOrinetation == .landscapeLeft {
22 | imageOrientation = UIImage.Orientation.right
23 | shouldOrient = !shouldOrient
24 | }
25 | if shouldOrient, let cgimage = self.cgImage {
26 | let image = UIImage(cgImage: cgimage, scale: self.scale, orientation: imageOrientation)
27 | return image
28 | }
29 |
30 | return self
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/ExtendedCoreImage/CIImage+Tinted.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CIImage+Tinted.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 08/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import CoreImage.CIImage
10 | import UIKit.UIColor
11 |
12 | public extension CIImage {
13 |
14 | /// Applies the `color` as a tint color
15 | ///
16 | /// - Parameter color: is a UIColor instance that is used as a tint color
17 | /// - Returns: new optional CIImage that was tinted or nil if `CIMultiplyCompositing` or/and `CIConstantColorGenerator` filter(s) could not be created
18 | func tinted(by color: UIColor) -> CIImage? {
19 | guard
20 | let transparentQRImage = transparent,
21 | let filter = CIFilter(name: "CIMultiplyCompositing"),
22 | let colorFilter = CIFilter(name: "CIConstantColorGenerator") else { return nil }
23 |
24 | let ciColor = CIColor(color: color)
25 | colorFilter.setValue(ciColor, forKey: kCIInputColorKey)
26 | let colorImage = colorFilter.outputImage
27 |
28 | filter.setValue(colorImage, forKey: kCIInputImageKey)
29 | filter.setValue(transparentQRImage, forKey: kCIInputBackgroundImageKey)
30 |
31 | return filter.outputImage!
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/Array+ContainsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+ContainsTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 28/12/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | final class ArrayContainsTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testContains", testContains)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testContains() {
27 | let testArray = [1,2,4,5,6,7,8,9,10]
28 | var result = testArray.contains(elements: 1,2,4,5)
29 | XCTAssert(result == true)
30 |
31 | result = testArray.contains(elements: 9,10,11,12)
32 | XCTAssert(result == false)
33 | }
34 |
35 | func testPerformanceExample() {
36 | // This is an example of a performance test case.
37 | self.measure {
38 | // Put the code you want to measure the time of here.
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Array/Array+Intersection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+Intersection.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 07/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Array where Element: Equatable {
12 |
13 | /// Computes intersection of self and the input values
14 | ///
15 | /// - Parameter values: vararg of arrays to intersect
16 | /// - Returns: an array that contains unique elements in all the input elements and self
17 | public func intersection(values: [Element]...) -> Array {
18 | var result = self
19 | var intersection = Array()
20 |
21 | for (index, value) in values.enumerated() {
22 | // By intersecting n and n + 1 we compute the intersection
23 | if (index > 0) {
24 | result = intersection
25 | intersection = Array()
26 | }
27 |
28 | // Searches for the common elements and saves them in the intersection Array, in order to intersect in the next iteration
29 | for elementValue in value where result.contains(elementValue) {
30 | intersection.append(elementValue)
31 | }
32 | }
33 |
34 | return intersection
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/String/String+Subscript.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Subscript.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 06/12/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - Adds conformances to CoutableClosedRnage, CountableRange, PartialRangeThrough and PartialRangeFrom protocols support in a form of subscripts
12 | extension String {
13 |
14 | public subscript(value: CountableClosedRange) -> Substring {
15 | return self[index(startIndex, offsetBy: value.lowerBound)...index(startIndex, offsetBy: value.upperBound)]
16 | }
17 |
18 | public subscript(value: CountableRange) -> Substring {
19 | return self[index(startIndex, offsetBy: value.lowerBound)..) -> Substring {
23 | return self[..) -> Substring {
27 | return self[...index(startIndex, offsetBy: value.upperBound)]
28 | }
29 |
30 | public subscript(value: PartialRangeFrom) -> Substring {
31 | return self[index(startIndex, offsetBy: value.lowerBound)...]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/Array+DifferenceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+DifferenceTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 28/12/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | final class ArrayDifferenceTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testDiff", testDiff)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testDiff() {
27 | let testA = [1,2,3,4,5]
28 | let testB = [4,5,7,8,9]
29 |
30 | var result = testA.difference(elements: testB)
31 | XCTAssert(result == [1,2,3])
32 |
33 | result = testB.difference(elements: testA)
34 | XCTAssert(result == [7,8,9])
35 | }
36 |
37 | func testPerformanceExample() {
38 | // This is an example of a performance test case.
39 | self.measure {
40 | // Put the code you want to measure the time of here.
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Int/Int+DecimalToBinary.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Int+DecimalToBinary.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 09/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Int {
12 |
13 | /// Converts a decimal number to a binary number using the conventional algorithm.
14 | ///
15 | /// - Returns: a binary representation of 'self'
16 | public func decimalToBinary() -> Int {
17 | var decimal = 0, binary = 0, c = -1
18 | var selfCopy = self
19 |
20 | while selfCopy != 0 {
21 | decimal = selfCopy % 2
22 | c += 1
23 | binary += decimal * Int(truncating: pow(10, c) as NSDecimalNumber)
24 | selfCopy /= 2
25 | }
26 | return binary
27 | }
28 |
29 | /// Converts a binary number to a decimal number using the conventional algorithm.
30 | ///
31 | /// - Returns: a decimal representation of `self`
32 | public func binaryToDecimal() -> Int {
33 | var k = self, d = 0, s = 0, c = -1
34 |
35 | while k != 0 {
36 | d = k % 10
37 | c += 1
38 | s += d * Int(truncating: pow(2, c) as NSDecimalNumber)
39 | k /= 10
40 | }
41 | return s
42 | }
43 | }
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationSorting/Array+QuickSortLomutoScheme.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+QuickSortLomutoScheme.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 29/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Array where Element: Comparable {
12 |
13 | public static func quickSortLomuto(array: inout [Element], lowest: Int, highest: Int) {
14 | if lowest < highest {
15 | let pivot = Array.partitionLomuto(array: &array, lowest: lowest, highest: highest)
16 |
17 | Array.quickSortLomuto(array: &array, lowest: lowest, highest: pivot - 1)
18 | Array.quickSortLomuto(array: &array, lowest: pivot + 1, highest: highest)
19 | } else {
20 | debugPrint(#function + " lowest param is bigger than highest: ", lowest, highest)
21 | }
22 | }
23 |
24 | private static func partitionLomuto(array: inout [Element], lowest: Int, highest: Int) -> Int {
25 | let pivot = array[highest]
26 | var i = lowest
27 |
28 | for j in lowest.. CGFloat {
14 | let window = getFirstSharedAppsWindow()
15 | let bottomPadding = window?.safeAreaInsets.bottom ?? 0.0
16 | return bottomPadding
17 | }
18 |
19 | public class func safeAreaTop() -> CGFloat {
20 | let window = getFirstSharedAppsWindow()
21 | let bottomPadding = window?.safeAreaInsets.top ?? 0.0
22 | return bottomPadding
23 | }
24 |
25 | public class func safeAreaLeft() -> CGFloat {
26 | let window = getFirstSharedAppsWindow()
27 | let leftPadding = window?.safeAreaInsets.left ?? 0.0
28 | return leftPadding
29 | }
30 |
31 | public class func safeAreaRight() -> CGFloat {
32 | let window = getFirstSharedAppsWindow()
33 | let rightPadding = window?.safeAreaInsets.right ?? 0.0
34 | return rightPadding
35 | }
36 |
37 | }
38 |
39 | private extension UIApplication {
40 |
41 | private class func getFirstSharedAppsWindow() -> UIWindow? {
42 | return UIApplication.shared.keyWindow ?? UIApplication.shared.windows.first
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/Sources/ExtendedCoreGraphics/CGPoint/CGPoint+Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint+Utils.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 20/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import CoreGraphics
10 |
11 | extension CGPoint {
12 |
13 | public func length() -> CGFloat {
14 | return sqrt(x*x + y*y)
15 | }
16 |
17 | public func lengthSquared() -> CGFloat {
18 | return x*x + y*y
19 | }
20 |
21 | func normalized() -> CGPoint {
22 | let len = length()
23 | return len > 0 ? self / len : CGPoint.zero
24 | }
25 |
26 | public mutating func normalize() -> CGPoint {
27 | self = normalized()
28 | return self
29 | }
30 |
31 | /// Computes the distance between the current [`self`] and the `other` point
32 | public func distance(to other: CGPoint) -> CGFloat {
33 | return sqrt(pow(x - other.x, 2) + pow(y - other.y, 2))
34 | }
35 |
36 | /// Computes an angle in radians between the current [`self`] and the argument parameter `point`
37 | public func angle(to point: CGPoint) -> CGFloat {
38 | return atan2(point.y - y, point.x - x)
39 | }
40 |
41 | /// Returns the angle in radians of the vector described by the CGPoint. The range of the angle is -π to π; an angle of 0 points to the right.
42 | public var angle: CGFloat {
43 | return atan2(y, x)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/NSObjectTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSObjectTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 02/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class NSObjectTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testObjectName", testObjectName)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testObjectName() {
27 | let _ = Foo()
28 | let _ = Bar()
29 |
30 | let fooClassName = Foo.nameOfClass
31 | let barClassName = Bar.nameOfClass
32 |
33 | debugPrint("foo : ", fooClassName)
34 | debugPrint("bar : ", barClassName)
35 | XCTAssert(fooClassName == "Foo")
36 | XCTAssert(barClassName == "Bar")
37 | }
38 |
39 | func testPerformanceExample() {
40 | // This is an example of a performance test case.
41 | self.measure {
42 | // Put the code you want to measure the time of here.
43 | }
44 | }
45 |
46 | }
47 |
48 | class Foo: NSObject { }
49 | class Bar: NSObject { }
50 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Decodable/Decodable+DecodeFromFile.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Decodable+DecodeFromFile.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 24/12/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Decodable {
12 |
13 | /// Decodes a file into a type
14 | ///
15 | /// - Parameters:
16 | /// - fileName: the target file name that will be used to decode into a type
17 | /// - fileExtension: a file's extension. The default value is ".json"
18 | /// - bundle: the app's bundle, The default value is ".main"
19 | /// - Returns: an instantiated object of type Self
20 | /// - Throws: may throw an error of type DecodableError, more specifically .missingFile
21 | public static func decodeFromFile(
22 | named fileName: String,
23 | with fileExtension: String = "json",
24 | in bundle: Bundle = .main
25 | ) throws -> Self {
26 | guard let url = bundle.url(forResource: fileName,
27 | withExtension: fileExtension) else {
28 | throw DecodableError.missingFile
29 | }
30 |
31 | let data = try Data(contentsOf: url)
32 | let decoder = JSONDecoder()
33 |
34 | return try decoder.decode(self, from: data)
35 | }
36 | }
37 |
38 |
39 | public enum DecodableError: Error {
40 | case missingFile
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationSorting/Array+QuickSortHoareScheme.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+QuickSortHoareScheme.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 29/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public extension Array where Element: Comparable {
12 |
13 | static func quickSortHoare(array: inout [Element], lowest: Int, highest: Int) {
14 |
15 | if lowest < highest {
16 | let pivot = Array.partitionHoare(array: &array, lowest: lowest, highest: highest)
17 |
18 | Array.quickSortHoare(array: &array, lowest: lowest, highest: pivot)
19 | Array.quickSortHoare(array: &array, lowest: pivot + 1, highest: highest)
20 | } else {
21 | debugPrint(#function + " lowest param is bigger than highest: ", lowest, highest)
22 | }
23 | }
24 |
25 | private static func partitionHoare(array: inout [Element], lowest: Int, highest: Int) -> Int {
26 | let pivot = array[lowest]
27 | var i = lowest - 1
28 | var j = highest + 1
29 |
30 | while true {
31 | i += 1
32 |
33 | while array[i] < pivot { i += 1 }
34 | j -= 1
35 |
36 | while array[j] > pivot {j -= 1 }
37 | if i >= j { return j }
38 |
39 | array.swapAt(i, j)
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Array/Array+Filtering.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+Filtering.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 05/06/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Array {
12 |
13 | /// Skipps the n number of elements and returns the remainig of the array
14 | ///
15 | /// - Parameter n: number of elements to skip
16 | /// - Returns: new array with changed number of elements
17 | public func skip(_ n: Int) -> Array {
18 | let result: [Element] = []
19 | return n > count ? result : Array(self[Int(n).. Bool) -> Bool {
27 | return filter(condition).count == count
28 | }
29 |
30 | /// Checks if any element in the given array satisfies a given condition
31 | ///
32 | /// - Parameter condition: a closure with one input element and bool as a return value
33 | /// - Returns: a bool value that indicates whether the condition was satisfied at least once
34 | public func any(condition: (Element) -> Bool) -> Bool {
35 | return filter(condition).count > 0
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIColor/UIColor+ColorComponents.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+ColorComponents.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 06/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // MARK: - The extension adds support for missing color components properties for various use cases
12 | extension UIColor {
13 |
14 | // MARK: - Properties
15 |
16 | public var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
17 | var red: CGFloat = 0
18 | var green: CGFloat = 0
19 | var blue: CGFloat = 0
20 | var alpha: CGFloat = 0
21 |
22 | getRed(&red, green: &green, blue: &blue, alpha: &alpha)
23 | return (red, green, blue, alpha)
24 |
25 | }
26 |
27 | public var hsba: (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) {
28 | var hue: CGFloat = 0.0
29 | var saturation: CGFloat = 0.0
30 | var brightness: CGFloat = 0.0
31 | var alpha: CGFloat = 0.0
32 |
33 | getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
34 | return (hue, saturation, brightness, alpha)
35 | }
36 |
37 | public var grayscale: (white: CGFloat, alpha: CGFloat) {
38 | var (white, alpha) = (CGFloat(0.0), CGFloat(0.0))
39 |
40 | getWhite(&white, alpha: &alpha)
41 | return (white, alpha)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/ExtendedCoreGraphics/CGSize/CGSize+Operators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize+Operators.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 05/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import CoreGraphics
10 |
11 | extension CGSize {
12 |
13 | public static func +(left: CGSize, right: CGSize) -> CGSize {
14 | return CGSize(width: left.width + right.width, height: right.height + right.height)
15 | }
16 |
17 | public static func +=(left: inout CGSize, right: CGSize){
18 | left = left + right
19 | }
20 |
21 | public static func -(left: CGSize, right: CGSize) -> CGSize {
22 | return CGSize(width: left.width - right.width, height: right.height - right.height)
23 | }
24 |
25 | public static func -=(left: inout CGSize, right: CGSize){
26 | left = left - right
27 | }
28 |
29 | public static func *(left: CGSize, right: CGSize) -> CGSize {
30 | return CGSize(width: left.width * right.width, height: right.height * right.height)
31 | }
32 |
33 | public static func *=(left: inout CGSize, right: CGSize) {
34 | left = left * right
35 | }
36 |
37 | public static func /(left: CGSize, right: CGSize) -> CGSize {
38 | return CGSize(width: left.width / right.width, height: right.height / right.height)
39 | }
40 |
41 | public static func /=(left: inout CGSize, right: CGSize) {
42 | left = left / right
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIBezierPath/UIBezierPath+Convenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIBezierPath+Convenience.swift
3 | // extensions_kit
4 | //
5 | // Created by Astemir Eleev on 09/05/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import CoreGraphics
10 | import class UIKit.UIBezierPath
11 |
12 | public extension UIBezierPath {
13 |
14 | // MARK: - Properties
15 |
16 | var center: CGPoint {
17 | return CGPoint(x: bounds.midX, y: bounds.midY)
18 | }
19 |
20 | var points: [CGPoint] {
21 | var points = [CGPoint]()
22 |
23 | withUnsafeMutablePointer(to: &points) { pointsPointer in
24 | cgPath.apply(info: pointsPointer) { userInfo, nextElementPointer in
25 | let element = nextElementPointer.pointee
26 | var point = CGPoint.zero
27 | switch element.type {
28 | case .moveToPoint: point = element.points[0]
29 | case .addLineToPoint: point = element.points[0]
30 | default: break
31 | }
32 | let elementsPointer = userInfo!.assumingMemoryBound(to: [CGPoint].self)
33 | elementsPointer.pointee.append(point)
34 | }
35 | }
36 | return points
37 | }
38 |
39 | // MARK: - Initializers
40 |
41 | convenience init(ovalOf size: CGSize) {
42 | self.init(ovalIn: CGRect(origin: CGPoint(x: -size.width / 2, y: -size.height / 2), size: size))
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/Array+IntersectionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+IntersectionTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 28/12/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | final class ArrayIntersectionTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testIntersection", testIntersection)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testIntersection() {
27 | let testA = [1,2,3,4,5]
28 | let testB = [4,5,6,7]
29 |
30 | var result = testA.intersection(values: testB)
31 | XCTAssert(result == [4,5])
32 |
33 | result = testB.intersection(values: testA)
34 | XCTAssert(result == [4,5])
35 |
36 | let wrongTestB = [6,7,8,9]
37 | result = testA.intersection(values: wrongTestB)
38 | XCTAssert(result == [])
39 | }
40 |
41 | func testPerformanceExample() {
42 | // This is an example of a performance test case.
43 | self.measure {
44 | // Put the code you want to measure the time of here.
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIViewController/UIViewController+ContainerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+ContainerViewController.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/02/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public extension UIViewController {
12 |
13 | /// Loads a UIViewController from a Storyboard file
14 | func loadViewController(named name: String, storyboard named: String, bundle: Bundle = .main) -> UIViewController {
15 | let storyboard = UIStoryboard(name: named, bundle: bundle)
16 | let viewController = storyboard.instantiateViewController(withIdentifier: name)
17 | return viewController
18 | }
19 |
20 | /// Adds a generic `T` which is `UIViewController` as a child container view controller to the specified generic `V` which is `UIView`. The T is embedded into `V` as where `V` is used as a container view for the view controller of type `T`
21 | func add(viewController: T, to view: V) {
22 | addChild(viewController)
23 | viewController.view.frame = view.bounds
24 | view.addSubview(viewController.view)
25 | viewController.didMove(toParent: self)
26 | }
27 |
28 | /// Removes the generic `T` which is `UIViewController`
29 | func remove(viewController: T) {
30 | willMove(toParent: nil)
31 | viewController.removeFromParent()
32 | removeFromParent()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIWindow/UIWindow+Instantiate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIWindow+Instantiate.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 24/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIWindow {
12 |
13 | /// Creates a new `UIWindow` instance with the given root view controller, frame and a `WindowOption` if any
14 | public static func create(with rootViewController: T, frame: CGRect = UIScreen.main.bounds, option: WindowOption = .none) -> UIWindow {
15 | let window = UIWindow(frame: frame)
16 | window.rootViewController = rootViewController
17 | let windowsOptionFunction = option.convert(for: window)
18 | windowsOptionFunction()
19 |
20 | return window
21 | }
22 | }
23 |
24 | /// Is a wrapper enum type that simplifies the instantiation of `UIWindow` class
25 | ///
26 | /// - none: no options applied
27 | /// - key: makes the `UIWindow` instance key but not visible
28 | /// - keyAndVisible: makes the `UIWindow` instnace key and visible
29 | public enum WindowOption {
30 | case none
31 | case key
32 | case keyAndVisible
33 | }
34 |
35 | public extension WindowOption {
36 | func convert(for window: UIWindow) -> () -> Void {
37 | switch self {
38 | case .none:
39 | return { /* empty function that will does nothing or none */ }
40 | case .key:
41 | return window.makeKey
42 | case .keyAndVisible:
43 | return window.makeKeyAndVisible
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/DecodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 24/12/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class DecodableTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testFileDecoding", testFileDecoding)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testFileDecoding() {
27 | // // We need this type of bundle in order to be able to "reach" the .json file inside the test target folder
28 | // let bundle = Bundle(for: type(of: self))
29 | //
30 | // guard let decodedUser = try? User.decodeFromFile(named: "User", in: bundle) else {
31 | // XCTAssert(false)
32 | // return
33 | // }
34 | // print("decoded user: ", decodedUser)
35 | //
36 | // XCTAssert(decodedUser.name == "Willy")
37 | // XCTAssert(decodedUser.age == 30)
38 | }
39 |
40 | func testPerformanceExample() {
41 | // This is an example of a performance test case.
42 | self.measure {
43 | // Put the code you want to measure the time of here.
44 | }
45 | }
46 |
47 | }
48 |
49 |
50 | struct User: Codable {
51 | var name: String
52 | var age: Int
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/ExtendedSceneKit/SCNAction+MoveAlong.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SCNAction+MoveAlong.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 09/05/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import class SceneKit.SCNAction
10 | import struct SceneKit.SCNVector3
11 | import CoreGraphics
12 | import class UIKit.UIBezierPath
13 | import ExtendedUIKit
14 | import ExtendedCoreGraphics
15 |
16 | extension SCNAction {
17 |
18 | public class func moveAlong(path: UIBezierPath, z: CGFloat, speed: Double) -> SCNAction {
19 | let points = path.points
20 | var lastPoint = points.first!
21 | var lastAngle = lastPoint.angle(to: points[1])
22 | var actions = [SCNAction.rotateTo(x: 0, y: 0, z: lastAngle, duration: 0), SCNAction.move(to: SCNVector3(lastPoint.x, lastPoint.y, z), duration: 0)]
23 |
24 | for point in points[1...] {
25 | let duration = Double(point.distance(to: lastPoint)) / speed
26 | var angle = lastPoint.angle(to: point)
27 | if abs(angle - lastAngle) > .pi {
28 | if angle > 0 {
29 | angle -= 2 * .pi
30 | } else {
31 | angle += 2 * .pi
32 | }
33 | }
34 | actions.append(SCNAction.group([
35 | SCNAction.move(to: SCNVector3(point.x, point.y, z), duration: duration),
36 | SCNAction.rotateTo(x: 0, y: 0, z: angle, duration: duration / 3)
37 | ]))
38 | lastPoint = point
39 | lastAngle = angle
40 | }
41 | return SCNAction.sequence(actions)
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/RandomAccessCollection/RandomAccessCollection+BinarySearch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RandomAccessCollection+BinarySearch.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 25/11/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension RandomAccessCollection {
12 |
13 | /// Binary Search algorithm for a given value with the closure that specified the order of the elements
14 | public func binarySearch(for value: Iterator.Element,
15 | isOrderIncreasing: (Iterator.Element, Iterator.Element) -> Bool = { _, _ in true })
16 | -> Index? {
17 | guard !isEmpty else { return nil }
18 | var left = startIndex
19 | var right = index(before: endIndex)
20 |
21 | while left <= right {
22 | let dist = distance(from: left, to: right)
23 | let mid = index(left, offsetBy: dist/2)
24 | let candidate = self[mid]
25 |
26 | if isOrderIncreasing(candidate, value) { left = index(after: mid)
27 | } else if isOrderIncreasing(value, candidate) { right = index(before: mid)
28 | } else {
29 | // The elements are equal, do nothing
30 | }
31 | }
32 | // Element has not been found
33 | return nil
34 | }
35 |
36 | }
37 |
38 | extension RandomAccessCollection where Iterator.Element: Comparable {
39 |
40 | public func binarySearch(for value: Iterator.Element) -> Index? {
41 | return binarySearch(for: value, isOrderIncreasing: <)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/ExtendedCoreImage/Filters/HighlightFilter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HighlightFilter.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 07/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import CoreImage
10 |
11 | /// The filter is originally designed for highlighting 3D objects but can be used to add this effect to images and sprites
12 | public class HighlightFilter: CIFilter {
13 |
14 | // MARK: - Properties
15 |
16 | public static let filterName = "highlightFilter"
17 |
18 | @objc dynamic public var inputImage: CIImage?
19 | @objc dynamic public var inputIntensity: NSNumber?
20 | @objc dynamic public var inputRadius: NSNumber?
21 |
22 | // MARK: - Overrides
23 |
24 | public override var outputImage: CIImage? {
25 | guard let inputImage = inputImage else {
26 | return nil
27 | }
28 | let bloomFilter = prepareBloomFilter(for: inputImage)
29 |
30 | let sourceOverCompositing = CIFilter(name:"CISourceOverCompositing")!
31 | sourceOverCompositing.setValue(inputImage, forKey: "inputImage")
32 | sourceOverCompositing.setValue(bloomFilter.outputImage, forKey: "inputBackgroundImage")
33 |
34 | return sourceOverCompositing.outputImage
35 | }
36 |
37 | // MARK: - Private helpers
38 |
39 | private func prepareBloomFilter(for inputImage: CIImage) -> CIFilter {
40 | let bloomFilter = CIFilter(name:"CIBloom")!
41 | bloomFilter.setValue(inputImage, forKey: kCIInputImageKey)
42 | bloomFilter.setValue(inputIntensity, forKey: "inputIntensity")
43 | bloomFilter.setValue(inputRadius, forKey: "inputRadius")
44 | return bloomFilter
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/ExtendedSpriteKit/SKScene/SKScene+SerialSpriteLoading.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SKScene+SerialSpriteLoading.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import SpriteKit.SKScene
10 | import SpriteKit.SKNode
11 |
12 | public extension SKScene {
13 |
14 | /// Uploads a set of scene graph nodes with a specific pattern
15 | ///
16 | /// - Parameters:
17 | /// - key: is a String instnace that describes name of child nodes that will be uploaded
18 | /// - pattern: is a closure that accepts string and int (as key and index) and returns string that decribes naming pattern
19 | /// - indices: is an instnace of ClosedRange type that specifies index boundaries of uploading nodes (for instnace you want to upload a set of nodes that describe sky and are called "cloud" - there are 3 clouds: "cloud-1", "cloud-2", "cloud-3" - the method helps to upload them using a singe function)
20 | /// - Returns: an array containing Node types (Node is any type derived from SKNode class)s
21 | func upload(for key: String, with pattern: (_ key: String, _ index: Int)->String, inRange indices: ClosedRange) -> [Node] where Node: SKNode {
22 |
23 | var foundNodes = [Node]()
24 |
25 | for index in indices.lowerBound...indices.upperBound {
26 | let childName = pattern(key, index)
27 | guard let node = self.childNode(withName: childName) as? Node else {
28 | debugPrint(#function + " could not find child with the following name: ", childName)
29 | continue
30 | }
31 | foundNodes.append(node)
32 | }
33 |
34 | return foundNodes
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/DoubleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DoubleTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 27/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class DoubleTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testCurrecyShortcuts", testCurrecyShortcuts),
16 | ("testRounded", testRounded)
17 | ]
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 | }
23 |
24 | override func tearDown() {
25 | // Put teardown code here. This method is called after the invocation of each test method in the class.
26 | super.tearDown()
27 | }
28 |
29 | func testCurrecyShortcuts() {
30 | let usd = 350.00.usd
31 | XCTAssert(usd == "$ 350.00")
32 |
33 | let usdDecimal = 450.34.usd
34 | XCTAssert(usdDecimal == "$ 450.34")
35 |
36 | let rub = 120.00.ruble
37 | XCTAssert(rub == "120,00 ₽")
38 | }
39 |
40 | func testRounded() {
41 | let rounded3 = 23.3434.rounded(toPlaces: 3)
42 | XCTAssert(rounded3 == 23.343)
43 |
44 | let rounded2 = 3.527.rounded(toPlaces: 2)
45 | XCTAssert(rounded2 == 3.53)
46 |
47 | let rounded1 = 7.32.rounded(toPlaces: 1)
48 | XCTAssert(rounded1 == 7.3)
49 | }
50 |
51 | func testPerformanceExample() {
52 | // This is an example of a performance test case.
53 | self.measure {
54 | // Put the code you want to measure the time of here.
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/OptionSetTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OptionSetTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 29/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class OptionSetTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testOperations", testOperations)
16 | ]
17 |
18 | override func setUp() {
19 | super.setUp()
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | override func tearDown() {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | super.tearDown()
26 | }
27 |
28 | func testOperations() {
29 | let caseInsensitive = String.CompareOptions.caseInsensitive
30 |
31 | let backwards = String.CompareOptions.backwards
32 | let caseInsensitivebackwards = caseInsensitive.inserting(new: backwards)
33 |
34 | let result = String.CompareOptions(rawValue: caseInsensitivebackwards.rawValue)
35 |
36 | XCTAssertTrue(caseInsensitivebackwards == result)
37 |
38 |
39 | let removedCaseInsensitive = backwards.removing(caseInsensitive)
40 | let removedResult = String.CompareOptions(rawValue: removedCaseInsensitive.rawValue)
41 |
42 | XCTAssertTrue(removedResult == backwards)
43 | }
44 |
45 | func testPerformanceExample() {
46 | // This is an example of a performance test case.
47 | self.measure {
48 | // Put the code you want to measure the time of here.
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/UnicodeOutputStreamTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnicodeOutputStreamTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 29/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class UnicodeOutputStreamTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("test", test)
16 | ]
17 |
18 | override func setUp() {
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func test() {
27 | let tastables = ["0: 👨 U+1F468\tMAN", "1: U+200D\tZERO WIDTH JOINER", "2: 👩 U+1F469\tWOMAN", "3: U+200D\tZERO WIDTH JOINER", "4: 👧 U+1F467\tGIRL", "5: U+200D\tZERO WIDTH JOINER", "6: 👧 U+1F467\tGIRL"]
28 |
29 | var unicodeOutputStream = UnicodeOutputStream()
30 | unicodeOutputStream.outputCompletion = {
31 | equalsAtLeastOne(from: tastables, target: $0)
32 | }
33 | print("👨👩👧👧", to: &unicodeOutputStream)
34 |
35 | func equalsAtLeastOne(from collection: [T], target: T) {
36 | for item in collection where item == target {
37 | XCTAssert(true)
38 | return
39 | }
40 | XCTAssert(false)
41 | }
42 | }
43 |
44 | func testPerformanceExample() {
45 | // This is an example of a performance test case.
46 | self.measure {
47 | // Put the code you want to measure the time of here.
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UICollectionView/UICollectionView+Operations.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+Operations.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 04/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UICollectionView {
12 |
13 | // MARK: - Typealiases
14 |
15 | public typealias UICollectionViewCompletion = (Bool) -> ()
16 |
17 | // MARK: - Methods
18 |
19 | /// Deletes items from the collection view in the specified section
20 | public func delete(indices: [Int], in section: Int = 0, completion: @escaping UICollectionViewCompletion = { _ in }) {
21 | guard !indices.isEmpty else { return }
22 |
23 | let indexPaths = indices.map { IndexPath(row: $0, section: section) }
24 | performBatchUpdates({ self.deleteItems(at: indexPaths) }, completion: completion)
25 | }
26 |
27 | /// Inserts new items to the collection view to the specified section
28 | public func insert(indices: [Int], in section: Int = 0, completion: @escaping UICollectionViewCompletion = { _ in }) {
29 | guard !indices.isEmpty else { return }
30 |
31 | let indexPaths = indices.map { IndexPath(row: $0, section: section) }
32 | performBatchUpdates({ self.insertItems(at: indexPaths) } , completion: completion)
33 | }
34 |
35 | /// Reloads items in the collection view
36 | public func reload(indices: [Int], in section: Int = 0, completion: @escaping UICollectionViewCompletion = { _ in }) {
37 | guard !indices.isEmpty else { return }
38 |
39 | let indexPaths = indices.map { IndexPath(row: $0, section: section) }
40 | performBatchUpdates({ self.reloadItems(at: indexPaths) } , completion: completion)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Extendedos/OSLog/OSLog+LogLevels.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OSLog+LogLevels.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 25/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import os
10 | import Foundation
11 |
12 | public enum SubsystemError: Error {
13 | case missingBundleIdentifier(String)
14 | }
15 |
16 | @available(iOS 12.0, *)
17 | extension OSLog {
18 |
19 | // MARK: Static properties
20 |
21 | public static var ui = OSLog(subsystem: try! subsystem(), category: "UI")
22 | public static var network = OSLog(subsystem: try! subsystem(), category: "Network")
23 | public static var `default` = OSLog(subsystem: try! subsystem(), category: "Default")
24 | public static var info = OSLog(subsystem: try! subsystem(), category: "Info")
25 | public static var debug = OSLog(subsystem: try! subsystem(), category: "Debug")
26 | public static var error = OSLog(subsystem: try! subsystem(), category: "Error")
27 | public static var fault = OSLog(subsystem: try! subsystem(), category: "Fault")
28 |
29 | // MARK: - Private helpers
30 |
31 | private static func subsystem() throws -> String {
32 | guard let subsystemIdentifier = Bundle.main.bundleIdentifier else { throw SubsystemError.missingBundleIdentifier("Could not get bundle identifier from the main bundle") }
33 | return subsystemIdentifier
34 | }
35 | }
36 |
37 | // MARK: - Global functions
38 |
39 | /// Sends a message to the logging system, optionally specifying a custom log object that specifies the subsystem with log category.
40 | ///
41 | /// The method is a convenience wrapper around `os.os_log` method, but with fewer parameters
42 | public func os_log(_ message: StaticString, log: OSLog) {
43 | os.os_log(message, log: log)
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UITableView/UITableView+ScrollingUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITableView+ScrollingUtils.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UITableView {
12 |
13 | /// Scrolls to bottom of the UITalbeView
14 | ///
15 | /// - Parameters:
16 | /// - animated: is a boolean parameter indicating whether the scrolling should be performed with animation
17 | public func scrollToBottom(animated: Bool = true) {
18 | let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height)
19 | setContentOffset(bottomOffset, animated: animated)
20 | }
21 |
22 | /// Scrolls to the top of the UITableView
23 | ///
24 | /// - Parameters:
25 | /// - animation: is a boolean parameter indicating whether the scrolling should be performed with animation
26 | public func scrollToTop(animated: Bool = true) {
27 | setContentOffset(CGPoint.zero, animated: animated)
28 | }
29 |
30 | /// Wrapper that safely scrolls to the speficied index path
31 | ///
32 | /// - Parameters:
33 | /// - indexPath: is an IndexPath type that specifies the target scrolling position
34 | /// - scrollPosition: is the target scroll position of the specified index path
35 | /// - animated: is a boolean value indicating whether the scrolling should be animated or not
36 | public func safeScrollToRow(at indexPath: IndexPath, at scrollPosition: UITableView.ScrollPosition, animated: Bool = true) {
37 | guard indexPath.section < numberOfSections, indexPath.row < numberOfRows(inSection: indexPath.section) else { return }
38 | scrollToRow(at: indexPath, at: scrollPosition, animated: animated)
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UICollectionView/UICollectionView+ScrollingUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+ScrollingUtils.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UICollectionView {
12 |
13 | /// Scrolls to the bottom of the UICollectionView
14 | ///
15 | /// - Parameters:
16 | /// - animated: is a boolean parameter indicating whether the scrolling should be performed with animation
17 | public func scrollToBottom(animated: Bool = true) {
18 | let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height)
19 | setContentOffset(bottomOffset, animated: animated)
20 | }
21 |
22 | /// Scrolls to the top of the UICollectionView
23 | ///
24 | /// - Parameters:
25 | /// - animated: is a boolean parameter indicating wherher the scrolling should be performed with animation
26 | public func scrollToTop(animated: Bool = true) {
27 | setContentOffset(.zero, animated: animated)
28 | }
29 |
30 | /// Wrapper that safely scrolls to the speficied index path
31 | ///
32 | /// - Parameters:
33 | /// - indexPath: is an IndexPath type that specifies the target scrolling position
34 | /// - scrollPosition: is the target scroll position of the specified index path
35 | /// - animated: is a boolean value indicating whether the scrolling should be animated or not
36 | public func safeScrollingToCell(at indexPath: IndexPath, at scrollPosition: UICollectionView.ScrollPosition, animated: Bool = true) {
37 | guard indexPath.section < numberOfSections, indexPath.row < numberOfItems(inSection: indexPath.section) else { return }
38 | scrollToItem(at: indexPath, at: scrollPosition, animated: animated)
39 | }
40 | }
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIImage/UIImage+Resize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Resize.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 30/04/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit.UIImage
10 |
11 | // MARK: - Class-level extension for UIImage that allow to resize input image based on expected image width or/and height
12 | public extension UIImage {
13 |
14 | class func resize(_ image: UIImage, newWidth: CGFloat) -> UIImage? {
15 |
16 | let scale = newWidth / image.size.width
17 | let newHeight = image.size.height * scale
18 | UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
19 | image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
20 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
21 | UIGraphicsEndImageContext()
22 |
23 | return newImage
24 | }
25 |
26 | class func resize(_ image: UIImage, newHeight: CGFloat) -> UIImage? {
27 |
28 | let scale = newHeight / image.size.height
29 | let newWidth = image.size.width * scale
30 | UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
31 | image.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
32 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
33 | UIGraphicsEndImageContext()
34 |
35 | return newImage
36 | }
37 |
38 | class func resize(_ image: UIImage, newSize: CGSize) -> UIImage? {
39 |
40 | UIGraphicsBeginImageContext(newSize)
41 | image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
42 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
43 | UIGraphicsEndImageContext()
44 |
45 | return newImage
46 | }
47 |
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Custom TextOutputStream/UnicodeOutputStream.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnicodeOutputStream.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 28/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Prints all the `Unicode` characters with the following scheme:
12 | ///
13 | /// Index: UnicodeScalar CodePoint (tabulation) UnicodeScalarName
14 | ///
15 | /// Can be attached the output completion handler in order to be able to debug the output stream
16 | public struct UnicodeOutputStream: TextOutputStream {
17 |
18 | // MARK: - Properties
19 |
20 | private var output: String = "" {
21 | didSet {
22 | outputCompletion?(output)
23 | }
24 | }
25 | internal var outputCompletion: ((String)->Void)?
26 |
27 | // MARK: - Conformance to TextOutputStream protocol
28 |
29 | public mutating func write(_ string: String) {
30 | guard !string.isEmpty, string != "\n" else { return }
31 |
32 | for (index, scalar) in string.unicodeScalars.lazy.enumerated() {
33 | let name = scalar.name ?? ""
34 | let codePoint = String(format: "U+%04X", scalar.value)
35 |
36 | output = "\(index): \(scalar) \(codePoint)\t\(name)"
37 | print(output)
38 | }
39 | }
40 | }
41 |
42 | // MARK: Internal extension for the Unicode.Scalar name
43 |
44 | extension Unicode.Scalar {
45 | var name: String? {
46 | guard var escapedName =
47 | "\(self)".applyingTransform(.toUnicodeName,
48 | reverse: false)
49 | else {
50 | return nil
51 | }
52 |
53 | escapedName.removeFirst(3) // remove "\\N{"
54 | escapedName.removeLast(1) // remove "}"
55 |
56 | return escapedName
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/ExtendedSpriteKit/SKTextureAtlas/SKTextureAtlas+FramesLoader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SKTextureAtlas+FramesLoader.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import SpriteKit.SKTextureAtlas
10 |
11 | extension SKTextureAtlas {
12 |
13 | /// Uploads an animation sequence from a texture atlas and returns an array of textures that can be futher used
14 | ///
15 | /// - Parameters:
16 | /// - named: is a texture atlas name
17 | /// - beginIndex: is a begin index that differentiates frames (for instnace the very first frame can named "player-0" or "player-1", the index helps in pattern matching)
18 | /// - pattern: is a closure that accepts name of a frame and index (index is incremented internally) and returns a string instnace that is used as texture atlas naming pattern
19 | /// - Returns: an array of SKTexture instances, each containing a singe frame of key-frame animation
20 | /// - Throws: an instnace of NSError with exit code 1, no user-related info and domain-specific error explanation
21 | class func upload(named name: String, beginIndex: Int = 1, pattern: (_ name: String, _ index: Int) -> String) throws -> [SKTexture] {
22 |
23 | let atlas = SKTextureAtlas(named: name)
24 | var frames = [SKTexture]()
25 | let count = atlas.textureNames.count
26 |
27 | if beginIndex > count {
28 | throw NSError(domain: "Begin index is grather than the number of texture in a the texture atlas named: \(name)", code: 1, userInfo: nil)
29 | }
30 |
31 | for index in beginIndex...count {
32 | let namePattern = pattern(name, index)
33 | let texture = atlas.textureNamed(namePattern)
34 | frames.append(texture)
35 | }
36 |
37 | return frames
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/ExtendedSpriteKit/SKTexture/SKTexture+LinearGradient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SKTexture+LinearGradient.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 06/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import SpriteKit.SKTexture
10 |
11 | extension SKTexture {
12 |
13 | enum GradientDirection {
14 | case up
15 | case left
16 | case upLeft
17 | case upRight
18 | }
19 |
20 | convenience init(size: CGSize, startColor: SKColor, endColor: SKColor, direction: GradientDirection = .up) {
21 | let context = CIContext(options: nil)
22 | let filter = CIFilter(name: "CILinearGradient")!
23 | let startVector: CIVector
24 | let endVector: CIVector
25 |
26 | filter.setDefaults()
27 |
28 | switch direction {
29 | case .up:
30 | startVector = CIVector(x: size.width/2, y: 0)
31 | endVector = CIVector(x: size.width/2, y: size.height)
32 | case .left:
33 | startVector = CIVector(x: size.width, y: size.height/2)
34 | endVector = CIVector(x: 0, y: size.height/2)
35 | case .upLeft:
36 | startVector = CIVector(x: size.width, y: 0)
37 | endVector = CIVector(x: 0, y: size.height)
38 | case .upRight:
39 | startVector = CIVector(x: 0, y: 0)
40 | endVector = CIVector(x: size.width, y: size.height)
41 | }
42 |
43 | filter.setValue(startVector, forKey: "inputPoint0")
44 | filter.setValue(endVector, forKey: "inputPoint1")
45 | filter.setValue(CIColor(color: startColor), forKey: "inputColor0")
46 | filter.setValue(CIColor(color: endColor), forKey: "inputColor1")
47 |
48 | let image = context.createCGImage(filter.outputImage!, from: CGRect(origin: .zero, size: size))
49 |
50 | self.init(cgImage: image!)
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/Sources/ExtendedPhotoKit/PHAsset/PHAsset+URL.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PHAsset+URL.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 13/06/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Photos
10 |
11 | public extension PHAsset {
12 |
13 | // MARK: - Enums
14 |
15 | enum PHAssetError: Error {
16 | case requestCannotBePerformed(String)
17 | }
18 |
19 | // MARK: - Methods
20 |
21 | /// Provides possibility to get URL for image and video media types.
22 | ///
23 | /// - Parameter completionHandler: is an escaping closure that returns a URL for a requested media type
24 | /// - Throws: PHAssetError with String description
25 | func url(completionHandler : @escaping ((_ responseURL : URL?) -> Void)) throws {
26 | switch mediaType {
27 | case .image :
28 | let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
29 | options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
30 | return true
31 | }
32 | self.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput, info) in
33 | completionHandler(contentEditingInput!.fullSizeImageURL)
34 | })
35 | case .video:
36 | let options: PHVideoRequestOptions = PHVideoRequestOptions()
37 | options.version = .original
38 | PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: { (asset, audioMix, info) in
39 | if let urlAsset = asset as? AVURLAsset {
40 | let localVideoUrl = urlAsset.url
41 | completionHandler(localVideoUrl)
42 | } else {
43 | completionHandler(nil)
44 | }
45 | })
46 | default:
47 | throw PHAssetError.requestCannotBePerformed("The requested media type is not supported")
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIViewController/UIViewController+Storyboard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Storyboard.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 29/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ExtendedFoundation
11 |
12 | extension UIViewController {
13 |
14 | // MARK: - Public methods
15 |
16 | /// Instantiates a UIViewController instance from a Storyboard using the UIViewController's name as a reference name of the Storyboard file
17 | ///
18 | /// - bundle: is an optional instance of Bundle class. By default it's nil, which means that the defailt bundle will be used
19 | /// - returns: instantiated UIViewController instance
20 | public class func instantiateController(from bundle: Bundle? = nil) -> Self? {
21 | return instantiateFromStoryboard(from: bundle)
22 | }
23 |
24 | /// Instantiates a UIViewController instance from the specified Storyboard and a String identifier
25 | ///
26 | /// - Returns: instantiated UIViewController instance
27 | public class func instantiateController(from storyboard: UIStoryboard, identifier: String) -> Self {
28 | return instantiate(from: storyboard, identifier: identifier)
29 | }
30 |
31 | /// Instantiates a UIView controller instnace from a Storyboard using class name as an identifier
32 | ///
33 | /// - Returns: instantiated UIViewController instnace
34 | public class func instantiateController(from storyboard: UIStoryboard) -> Self {
35 | return instantiate(from: storyboard, identifier: nameOfClass)
36 | }
37 |
38 | // MARK: - Private methods
39 |
40 | private class func instantiateFromStoryboard(from bundle: Bundle? = nil) -> T? {
41 | let storyboard = UIStoryboard(name: String(describing: self), bundle: bundle)
42 | return storyboard.instantiateInitialViewController() as? T
43 | }
44 |
45 | private class func instantiate(from storyboard: UIStoryboard, identifier: String) -> T {
46 | return storyboard.instantiateViewController(withIdentifier: identifier) as! T
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIAlertController/UIAlertController+Presentation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIAlertController+Presentation.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 03/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit.UIAlertController
10 |
11 | extension UIAlertController {
12 |
13 | /// Presents a UIAlertController with the given title, message, tintColor and alert actions
14 | ///
15 | /// - Parameters:
16 | /// - title: is an optional String title that will appear at the top of the alert controller
17 | /// - message: is an optional String message that will appear at the center of the alert controller
18 | /// - tintColor: is an optional UIColor that will color the view of the alert controller
19 | /// - controller: is a target UIViewController that will be used to present the alert. If none was specified then the UIApplication's root view controller will be used to present the alert
20 | /// - animated: is a Bool value that specified whether the presentation of the alert should be animated or not
21 | /// - alertActions: is a closure that is used to inject varying number of UIAlertAction objects
22 | public class func present(with title: String?,
23 | and message: String?,
24 | tintColor: UIColor? = nil,
25 | from controller: UIViewController? = nil,
26 | shouldAnimate animated: Bool = true,
27 | alertActions: ()->[UIAlertAction]) {
28 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
29 |
30 | let actions = alertActions()
31 | actions.forEach { action in
32 | alertController.addAction(action)
33 | }
34 |
35 | if let tintColor = tintColor {
36 | alertController.view.tintColor = tintColor
37 | }
38 |
39 | let unwrappedController = controller ?? UIApplication.shared.keyWindow?.rootViewController
40 | unwrappedController?.present(alertController, animated: animated, completion: nil)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationDataStructures/PriorityQueue/PriorityQueue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PriorityQueue.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 01/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Implementation of Priority Queue based on Heap data structure.
12 | public struct PriorityQueue where T: Comparable {
13 |
14 | // MARK: - Propertioes
15 |
16 | fileprivate var heap: Heap
17 |
18 | // MARK: - Initializers
19 |
20 | public init(order: @escaping (T, T) -> Bool) {
21 | heap = Heap(order: order)
22 | }
23 |
24 | public init(elements: [T], order: @escaping (T, T) -> Bool) {
25 | heap = Heap(array: elements, order: order)
26 | }
27 |
28 | // MARK: - Methods
29 |
30 | public func isEmpty() -> Bool {
31 | return heap.isEmpty
32 | }
33 |
34 | public func count() -> Int {
35 | return heap.count
36 | }
37 |
38 | public func peek() -> T? {
39 | return heap.peek()
40 | }
41 |
42 | public mutating func enqueue(_ element: T) {
43 | heap.insert(node: element)
44 | }
45 |
46 | public mutating func dequeue() -> T? {
47 | return heap.remove()
48 | }
49 |
50 | public mutating func changePriority(index i: Int, value: T) {
51 | return heap.replace(elementAt: i, with: value)
52 | }
53 | }
54 |
55 | extension PriorityQueue where T: Equatable {
56 |
57 | // MARK: - Methods
58 |
59 | public func index(of element: T) -> Int? {
60 | return heap.index(of: element)
61 | }
62 | }
63 |
64 | extension PriorityQueue: ExpressibleByArrayLiteral {
65 |
66 | // MARK: - Initializers
67 |
68 | public init(arrayLiteral elements: T...) {
69 | self.init(elements: elements, order: >)
70 | }
71 | }
72 |
73 | extension PriorityQueue: CustomStringConvertible, CustomDebugStringConvertible {
74 |
75 | // MARK: - Properties
76 |
77 | public var description: String {
78 | return heap.description
79 | }
80 |
81 | public var debugDescription: String {
82 | return heap.debugDescription
83 | }
84 |
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationDataStructures/Stack/Stack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stack.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 30/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Stack {
12 |
13 | // MARK: - Private properties
14 |
15 | private var elements: [T]
16 |
17 | // MARK: - Public computed properties
18 |
19 | public var isEmpty: Bool {
20 | return elements.isEmpty
21 | }
22 |
23 | public var count: Int {
24 | return elements.count
25 | }
26 |
27 | // MARK: - Initializers
28 |
29 | public init() {
30 | elements = [T]()
31 | }
32 |
33 | public init(stack: S) where S.Iterator.Element == T {
34 | self.elements = Array(stack.reversed())
35 | }
36 |
37 | // MARK: Methods
38 |
39 | public mutating func push(element: T) {
40 | elements.append(element)
41 | }
42 |
43 | public mutating func peek() -> T? {
44 | return elements.last
45 | }
46 |
47 | public mutating func pop() -> T? {
48 | return elements.popLast()
49 | }
50 |
51 | }
52 |
53 | /// Extension that adds conformances to CustomStringConvertible & CustomDebugStringConvertible protocols
54 | extension Stack: CustomStringConvertible, CustomDebugStringConvertible {
55 |
56 | public var description: String {
57 | return elements.description
58 | }
59 |
60 | public var debugDescription: String {
61 | return elements.description
62 | }
63 | }
64 |
65 | /// Adds conformance to ExpressibleByArrayLiteral protocol - Stack can be initialized by an array literal
66 | extension Stack: ExpressibleByArrayLiteral {
67 |
68 | public init(arrayLiteral elements: T...) {
69 | self.init()
70 |
71 | elements.forEach { element in
72 | self.elements.append(element)
73 | }
74 | }
75 | }
76 |
77 | /// Adds conformance to Sequence protocol - can be used in for...in loop
78 | extension Stack: Sequence {
79 |
80 | public func makeIterator() -> AnyIterator {
81 | let idexedIterator = IndexingIterator(_elements: self.elements.lazy.reversed())
82 | return AnyIterator(idexedIterator)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIImage/UIImage+Downsample.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Downsample.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 11/01/2019.
6 | // Copyright © 2019 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 |
13 | /// Downsamples the input image to the specified point size and scale factor. Can be used to present the thumbnails, supports caching. The best way to utilize this method is to use GCD in order to delegate the work off the main thread, then use the .main dispatch queue to sync the results.
14 | ///
15 | /// There is a possible caveat when using this method intensively by repeatedly creating dispatch queues. That may lead to Thread Explosion. If you need to process many images, for instances for `UITableView` or `UICollectionView`, please use a serial dispatch queue and asynchronously delegate the work. That will prevent your downsampling code to cause Thread Explosion and will keep the performance at a decent level.
16 | ///
17 | /// - Parameters:
18 | /// - url: a URL instnace that that specifies the location of an image
19 | /// - pointSize: is the target size represented by `CGSize` that you wish to get
20 | /// - scale: is the scale factor that is represented by `CGFloat`
21 | /// - Returns: an optional `UIImage` instnace that holds the downsampled image, or `nil` if either `CGImageSourceCreateWithURL` or `CGImageSourceCreateThumbnailAtIndex` has failed a `guard` statement
22 | public static func downsample(imageAt url: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage? {
23 | let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
24 | guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, imageSourceOptions) else { return nil }
25 |
26 | let maxDimensionsInPixels = max(pointSize.width, pointSize.height) * scale
27 | let downsampleOptions = [
28 | kCGImageSourceCreateThumbnailFromImageAlways : true,
29 | kCGImageSourceShouldCacheImmediately : true,
30 | kCGImageSourceCreateThumbnailWithTransform : true,
31 | kCGImageSourceThumbnailMaxPixelSize : maxDimensionsInPixels
32 | ] as CFDictionary
33 |
34 | guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else { return nil }
35 |
36 | return .init(cgImage: downsampledImage)
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIView/UIView+Constraints.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Constraints.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 09/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIView {
12 |
13 | public func pinSuperview() {
14 | guard let superview = self.superview else {
15 | return
16 | }
17 | self.translatesAutoresizingMaskIntoConstraints = false
18 | addSuperviewConstraint(constraint: topAnchor.constraint(equalTo: superview.topAnchor))
19 | addSuperviewConstraint(constraint: leftAnchor.constraint(equalTo: superview.leftAnchor))
20 | addSuperviewConstraint(constraint: bottomAnchor.constraint(equalTo: superview.bottomAnchor))
21 | addSuperviewConstraint(constraint: rightAnchor.constraint(equalTo: superview.rightAnchor))
22 | }
23 |
24 | public func addSuperviewConstraint(constraint: NSLayoutConstraint) {
25 | superview?.addConstraint(constraint)
26 | }
27 |
28 | /// Retrieves all constraints
29 | public func getAllConstraints() -> [NSLayoutConstraint] {
30 | // array will contain self and all superviews
31 | var views = [self]
32 |
33 | // get all superviews
34 | var view = self
35 | while let superview = view.superview {
36 | views.append(superview)
37 | view = superview
38 | }
39 |
40 | // transforms views to constraints and filter only those
41 | // constraints that include the view itself
42 | return views.flatMap({ $0.constraints }).filter { constraint in
43 | return constraint.firstItem as? UIView == self ||
44 | constraint.secondItem as? UIView == self
45 | }
46 | }
47 |
48 | public func getHeightConstraint() -> NSLayoutConstraint? {
49 | return getAllConstraints().filter({
50 | ($0.firstAttribute == .height && $0.firstItem as? UIView == self) ||
51 | ($0.secondAttribute == .height && $0.secondItem as? UIView == self)
52 | }).first
53 | }
54 |
55 | public func getWidthConstraint() -> NSLayoutConstraint? {
56 | return getAllConstraints().filter({
57 | ($0.firstAttribute == .width && $0.firstItem as? UIView == self) ||
58 | ($0.secondAttribute == .width && $0.secondItem as? UIView == self)
59 | }).first
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/SequenceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SequenceTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 14/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class SequenceTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testShuffled", testShuffled),
16 | ("testCount", testCount),
17 | ("testDuplicatedRemoved", testDuplicatedRemoved)
18 | ]
19 |
20 | override func setUp() {
21 | super.setUp()
22 | // Put setup code here. This method is called before the invocation of each test method in the class.
23 | }
24 |
25 | override func tearDown() {
26 | // Put teardown code here. This method is called after the invocation of each test method in the class.
27 | super.tearDown()
28 | }
29 |
30 | func testShuffled() {
31 | let array = [1,2,3,4,5,6,7]
32 | var shuffledArray = array.shuffled()
33 | var iterations = 10
34 |
35 | while iterations > -1 {
36 | XCTAssert(shuffledArray != array.shuffled())
37 |
38 | shuffledArray = array.shuffled()
39 | iterations -= 1
40 | }
41 |
42 | }
43 |
44 | func testCount() {
45 | let array = [1,2,3,4,5,6,7,8,9]
46 | let numberOfEvens = array.count {
47 | $0 % 2 == 0
48 | }
49 |
50 | XCTAssert(numberOfEvens == 4)
51 |
52 | let numberOfOdds = array.count {
53 | $0 % 2 == 1
54 | }
55 |
56 | XCTAssert(numberOfOdds == 5)
57 |
58 | let stringArray = ["Asdjf", "ASGhjsndm", "eds", "Hello", "snoyle", "Long", "Play", "vAMpire", "AsdinneR"]
59 |
60 | let prefixCount = stringArray.count {
61 | $0.hasPrefix("As")
62 | }
63 |
64 | XCTAssert(prefixCount == 2)
65 | }
66 |
67 | func testDuplicatedRemoved() {
68 | let test = [1,1,6,4,3,3,4,5,7,7,3,4,5,5,3,2,3,4]
69 |
70 | let removedDuplicates = test.removeDuplicates()
71 |
72 | XCTAssert(removedDuplicates == [1,6,4,3,5,7,2])
73 | }
74 |
75 | func testPerformanceExample() {
76 | // This is an example of a performance test case.
77 | self.measure {
78 | // Put the code you want to measure the time of here.
79 | }
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/String/String+Validation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Validation.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 29/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 |
13 | /// Determines if self is alphanumeric String
14 | ///
15 | /// - Returns: boolean value indicating wherher self is alphanumeric
16 | public var isAlphaNumeric: Bool {
17 | let alphaNumeric = NSCharacterSet.alphanumerics
18 | let output = self.unicodeScalars.split { !alphaNumeric.contains($0)}.map(String.init)
19 |
20 | if output.count == 1, output[0] != self {
21 | return false
22 | }
23 | return output.count == 1
24 | }
25 |
26 | /// Check whether the string is an email or not
27 | ///
28 | /// - Returns: boolean value indicating whether self is an email
29 | public var isEmail: Bool {
30 | let regex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
31 | let predicate = NSPredicate(format:"SELF MATCHES %@", regex)
32 | return predicate.evaluate(with: self)
33 | }
34 |
35 | /// Check whether the string contains one or more letters
36 | ///
37 | /// - Returns: boolean value indicating whether self has letters
38 | public var hasLetters: Bool {
39 | return rangeOfCharacter(from: .letters, options: .literal) != nil
40 | }
41 |
42 | /// Check whether the string contains one or more numbers
43 | ///
44 | /// - Returns: boolean value indicating whether self has numbers
45 | public var hasNumbers: Bool {
46 | return rangeOfCharacter(from: .decimalDigits, options: .literal) != nil
47 | }
48 |
49 | /// Check whether the string contains only letters
50 | ///
51 | /// - Returns: boolean value indicating whether self is alphabetic
52 | public var isAlphabetic: Bool {
53 | for c in self {
54 | if !(c >= "a" && c <= "z") && !(c >= "A" && c <= "Z") {
55 | return false
56 | }
57 | }
58 | return true
59 | }
60 |
61 | /// Check whether the string is a valid `JSON`
62 | ///
63 | /// - Returns: boolean value indicating whether self is `JSON`
64 | public var isJSON: Bool {
65 | guard
66 | let jsonDataToVerify = self.data(using: String.Encoding.utf8),
67 | let _ = try? JSONSerialization.jsonObject(with: jsonDataToVerify) else {
68 | return false
69 | }
70 | return true
71 | }
72 |
73 | /// Check whether the string is a valid `UUID`
74 | ///
75 | /// - Returns: boolean value indicating whether self is `UUID`
76 | var isUUID: Bool {
77 | return UUID(uuidString: self) != nil
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/Array+FilterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+FilterTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 06/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | final class ArrayFilterTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testSkip", testSkip),
16 | ("testAll", testAll),
17 | ("testAny", testAny)
18 | ]
19 |
20 | let testArrayInt = [1,2,3,4,5,6,7,8,9,10]
21 | let testArrayString = ["a", "b", "c", "d", "e", "f", "g"]
22 |
23 | override func setUp() {
24 | // Put setup code here. This method is called before the invocation of each test method in the class.
25 | }
26 |
27 | override func tearDown() {
28 | // Put teardown code here. This method is called after the invocation of each test method in the class.
29 | }
30 |
31 | func testSkip() {
32 | var result = testArrayInt.skip(5)
33 | XCTAssert(result == [6,7,8,9,10])
34 | result = testArrayInt.skip(2)
35 | XCTAssert(result == [3,4,5,6,7,8,9,10])
36 | result = testArrayInt.skip(10)
37 | XCTAssert(result == [])
38 |
39 | var resultS = testArrayString.skip(5)
40 | XCTAssert(resultS == ["f", "g"])
41 | resultS = testArrayString.skip(2)
42 | XCTAssert(resultS == ["c", "d", "e", "f", "g"])
43 | resultS = testArrayString.skip(10)
44 | XCTAssert(resultS == [])
45 | }
46 |
47 | func testAll() {
48 | var result = testArrayInt.all { $0 < 20 }
49 | XCTAssert(result == true)
50 | result = testArrayInt.all { $0 > -1 }
51 | XCTAssert(result == true)
52 | result = testArrayInt.all { $0 % 2 == 0 }
53 | XCTAssert(result == false)
54 |
55 | var resultS = testArrayString.all { $0.count == 1 }
56 | XCTAssert(resultS == true)
57 | resultS = testArrayString.all { $0.starts(with: "a") }
58 | XCTAssert(resultS == false)
59 | }
60 |
61 | func testAny() {
62 | var result = testArrayInt.any { $0 < 5 }
63 | XCTAssert(result == true)
64 | result = testArrayInt.any { $0 > 10 }
65 | XCTAssert(result == false)
66 | result = testArrayInt.any { $0 % 2 == 0 }
67 | XCTAssert(result == true)
68 |
69 | var resultS = testArrayString.any { $0.count == 1 }
70 | XCTAssert(resultS == true)
71 | resultS = testArrayString.any { $0.starts(with: "a") }
72 | XCTAssert(resultS == true)
73 | }
74 |
75 | func testPerformanceExample() {
76 | // This is an example of a performance test case.
77 | self.measure {
78 | // Put the code you want to measure the time of here.
79 | }
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/ExtendedAVFoundation/AVCaptureDevice+ToggleFlash.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AVCaptureDevice+ToggleFlash.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 17/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | #if canImport(AVFoundation) && os(iOS)
10 | import AVFoundation
11 |
12 | // MARK: - AVCaptureDevice+Flashlight extnesion that adds support for flashlight capabilities and management through a simple method with custom states.
13 | extension AVCaptureDevice {
14 |
15 | // MARK: - Enums
16 |
17 | /// Is a convinience enum type for Flashlight feature. Incapsulates a number of possible states
18 | ///
19 | /// - on: for flashlight is being turned on
20 | /// - off: for flashlight is being turned off
21 | /// - unavailable: for cases when current device has no torch capabilities
22 | /// - undefined: for cases when none of the listed cases takes place
23 | /// - error: for cases when AVCaptureDevice has no suport for video type
24 | enum AVFlashlightState {
25 | case on
26 | case off
27 | case unavailable
28 | case undefined
29 | case error
30 | }
31 |
32 | /// Convenience enum that provies several levels of flashlight brigtness levels
33 | ///
34 | /// - none: 0.0
35 | /// - min: 0.25
36 | /// - mid: 0.5
37 | /// - high: 0.75
38 | /// - max: 1.0
39 | enum AVBrightnessLevel: Float {
40 | case none = 0.0
41 | case min = 0.25
42 | case mid = 0.5
43 | case high = 0.75
44 | case max = 1.0
45 | }
46 |
47 | // MARK: - Methods
48 |
49 | /// Toggls built in flashlight on and off
50 | ///
51 | /// - Returns: AVFlashlightState enum type describing one of the following states:
52 | @discardableResult static func toggleFlashlight(with brightnessLevel: AVBrightnessLevel = .max) -> AVFlashlightState {
53 |
54 | guard let device = AVCaptureDevice.default(for: .video) else { return AVFlashlightState.error }
55 | if device.hasTorch {
56 | do {
57 | try device.lockForConfiguration()
58 |
59 | if device.torchMode == .on {
60 | device.torchMode = .off
61 | return AVFlashlightState.off
62 | } else {
63 | do {
64 | try device.setTorchModeOn(level: brightnessLevel.rawValue)
65 | return AVFlashlightState.on
66 | } catch {
67 | debugPrint("Error when attemping to turn on torch -> ", error)
68 | }
69 | }
70 | } catch {
71 | debugPrint("Error when attempting to lock for torch configuration -> ", error)
72 | }
73 | } else {
74 | return AVFlashlightState.unavailable
75 | }
76 | return AVFlashlightState.undefined
77 | }
78 | }
79 | #endif
80 |
--------------------------------------------------------------------------------
/Sources/ExtendedCoreAnimation/CAAnimation/CAAnimation+PatternReplicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CAAnimation+PatternReplicator.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 01/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | extension CAAnimation {
13 |
14 | @discardableResult
15 | public class func patternReplocator(with image: UIImage,
16 | size: CGSize,
17 | duration: CFTimeInterval = 2.0,
18 | opacity: ClosedRange = 0.1...1.0,
19 | delay: TimeInterval = 0.1,
20 | targetLayer: CALayer) -> CALayer {
21 |
22 | let replicatorLayer = CAReplicatorLayer()
23 | replicatorLayer.frame.size = size
24 | replicatorLayer.masksToBounds = true
25 | targetLayer.addSublayer(replicatorLayer)
26 |
27 | let imageLayer = CALayer()
28 | imageLayer.contents = image.cgImage
29 | imageLayer.frame.size = image.size
30 | replicatorLayer.addSublayer(imageLayer)
31 |
32 | let instanceCount = size.width / image.size.width
33 | replicatorLayer.instanceCount = Int(ceil(instanceCount))
34 |
35 | replicatorLayer.instanceTransform = CATransform3DMakeTranslation(
36 | image.size.width, 0, 0
37 | )
38 | let colorOffset = -1 / Float(replicatorLayer.instanceCount)
39 | replicatorLayer.instanceRedOffset = colorOffset
40 | replicatorLayer.instanceGreenOffset = colorOffset
41 |
42 | let verticalReplicatorLayer = CAReplicatorLayer()
43 | verticalReplicatorLayer.frame.size = size
44 | verticalReplicatorLayer.masksToBounds = true
45 | verticalReplicatorLayer.instanceBlueOffset = colorOffset
46 | targetLayer.addSublayer(verticalReplicatorLayer)
47 |
48 | let verticalInstanceCount = size.height / image.size.height
49 | verticalReplicatorLayer.instanceCount = Int(ceil(verticalInstanceCount))
50 | verticalReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(
51 | 0, image.size.height, 0
52 | )
53 | verticalReplicatorLayer.addSublayer(replicatorLayer)
54 |
55 | let delay = TimeInterval(0.1)
56 | replicatorLayer.instanceDelay = delay
57 | verticalReplicatorLayer.instanceDelay = delay
58 |
59 | let animation = CABasicAnimation(keyPath: "transform.scale")
60 | animation.duration = 2
61 | animation.fromValue = 1
62 | animation.toValue = 0.1
63 | animation.autoreverses = true
64 | animation.repeatCount = .infinity
65 | animation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.easeInEaseOut)
66 | imageLayer.add(animation, forKey: "wavyscale")
67 |
68 | return replicatorLayer
69 | }
70 | }
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationSorting/Array+MergeSort.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+MergeSort.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 29/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Merge sort is a sorting algorithm that has lower order running time than the insertion sort algorithm. Conceptually it is a devide and conquer sorting algorithm.
12 | extension Array where Element: Comparable {
13 |
14 | // MARK: - Typealiases
15 |
16 | public typealias ComparisonClosure = (Element, Element) -> Bool
17 |
18 | // MARK: - Methods
19 |
20 | /// Sorts an array of Comparable elements using Merge sort algorithm
21 | ///
22 | /// - Parameter list: is an Array of Comparable elements
23 | /// - Returns: the same sorted Array
24 | public static func mergeSort(_ list: [Element], order sign: ComparisonClosure) -> [Element] {
25 |
26 | if list.count < 2 { return list }
27 | let center = (list.count) / 2
28 |
29 | let leftMergeSort = mergeSort([Element](list[0.. [Element] {
42 |
43 | var leftIndex = 0
44 | var rightIndex = 0
45 | let totalCapacity = lhalf.count + rhalf.count
46 |
47 | var temp = [Element]()
48 | temp.reserveCapacity(totalCapacity)
49 |
50 | while leftIndex < lhalf.count && rightIndex < rhalf.count {
51 | let leftElement = lhalf[leftIndex]
52 | let rightElement = rhalf[rightIndex]
53 |
54 | let leftGreatherThanRight = sign(rightElement, leftElement) // sign(leftElement, rightElement)
55 | let leftSmallerThanRight = !leftGreatherThanRight
56 |
57 | if leftGreatherThanRight {
58 | temp.append(leftElement)
59 | leftIndex += 1
60 | } else if leftSmallerThanRight {
61 | temp.append(rightElement)
62 | rightIndex += 1
63 | } else {
64 | temp.append(leftElement)
65 | temp.append(rightElement)
66 | leftIndex += 1
67 | rightIndex += 1
68 | }
69 | }
70 |
71 | temp += [Element](lhalf[leftIndex.. CGPoint {
14 | return CGPoint(x: left.x + right.x, y: left.y + right.y)
15 | }
16 |
17 | public static func += (left: inout CGPoint, right: CGPoint) {
18 | left = left + right
19 | }
20 | public static func + (left: CGPoint, right: CGVector) -> CGPoint {
21 | return CGPoint(x: left.x + right.dx, y: left.y + right.dy)
22 | }
23 |
24 | public static func += (left: inout CGPoint, right: CGVector) {
25 | left = left + right
26 | }
27 | public static func - (left: CGPoint, right: CGPoint) -> CGPoint {
28 | return CGPoint(x: left.x - right.x, y: left.y - right.y)
29 | }
30 |
31 | public static func -= (left: inout CGPoint, right: CGPoint) {
32 | left = left - right
33 | }
34 |
35 | public static func - (left: CGPoint, right: CGVector) -> CGPoint {
36 | return CGPoint(x: left.x - right.dx, y: left.y - right.dy)
37 | }
38 |
39 | public static func -= (left: inout CGPoint, right: CGVector) {
40 | left = left - right
41 | }
42 |
43 | public static func * (left: CGPoint, right: CGPoint) -> CGPoint {
44 | return CGPoint(x: left.x * right.x, y: left.y * right.y)
45 | }
46 |
47 | public static func *= (left: inout CGPoint, right: CGPoint) {
48 | left = left * right
49 | }
50 |
51 | public static func * (point: CGPoint, scalar: CGFloat) -> CGPoint {
52 | return CGPoint(x: point.x * scalar, y: point.y * scalar)
53 | }
54 |
55 | public static func *= (point: inout CGPoint, scalar: CGFloat) {
56 | point = point * scalar
57 | }
58 |
59 | public static func * (left: CGPoint, right: CGVector) -> CGPoint {
60 | return CGPoint(x: left.x * right.dx, y: left.y * right.dy)
61 | }
62 |
63 | public static func *= (left: inout CGPoint, right: CGVector) {
64 | left = left * right
65 | }
66 |
67 | public static func / (left: CGPoint, right: CGPoint) -> CGPoint {
68 | return CGPoint(x: left.x / right.x, y: left.y / right.y)
69 | }
70 |
71 | public static func /= (left: inout CGPoint, right: CGPoint) {
72 | left = left / right
73 | }
74 |
75 | public static func / (point: CGPoint, scalar: CGFloat) -> CGPoint {
76 | return CGPoint(x: point.x / scalar, y: point.y / scalar)
77 | }
78 |
79 | public static func /= (point: inout CGPoint, scalar: CGFloat) {
80 | point = point / scalar
81 | }
82 |
83 | public static func / (left: CGPoint, right: CGVector) -> CGPoint {
84 | return CGPoint(x: left.x / right.dx, y: left.y / right.dy)
85 | }
86 |
87 | public static func /= (left: inout CGPoint, right: CGVector) {
88 | left = left / right
89 | }
90 |
91 | public static func lerp(start: CGPoint, end: CGPoint, t: CGFloat) -> CGPoint {
92 | return start + (end - start) * t
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundation/Data/Data+MimeType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data+MimeType.swift
3 | // ExtendedFoundation
4 | //
5 | // Created by Astemir Eleev on 23/02/2020.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public extension Data {
12 |
13 | // MARK: - Image Types
14 |
15 | var isPng: Bool {
16 | return matches(bytes: [0x89, 0x50, 0x4E, 0x47])
17 | }
18 |
19 | var isJPEG: Bool {
20 | return matches(bytes: [0xFF, 0xD8, 0xFF])
21 | }
22 |
23 | var isJpg: Bool {
24 | return isJPEG
25 | }
26 |
27 | var isGif: Bool {
28 | return matches(bytes: [0x47, 0x49, 0x46])
29 | }
30 |
31 | var isWebp: Bool {
32 | return matches(bytes: [0x57, 0x45, 0x42, 0x50], range: 8...11)
33 | }
34 |
35 | var isTiff: Bool {
36 | return matches(bytes: [0x49, 0x49, 0x2A, 0x00]) ||
37 | matches(bytes: [0x4D, 0x4D, 0x00, 0x2A])
38 | }
39 |
40 | var isBmp: Bool {
41 | return matches(bytes: [0x42, 0x4D])
42 | }
43 |
44 | var isPsd: Bool {
45 | return matches(bytes: [0x38, 0x42, 0x50, 0x53])
46 | }
47 |
48 | // MARK: - Audio Types
49 |
50 | var isAmr: Bool {
51 | return matches(bytes: [0x23, 0x21, 0x41,
52 | 0x4D, 0x52, 0x0A])
53 | }
54 |
55 | var isMp3: Bool {
56 | return matches(bytes: [0x49, 0x44, 0x33]) ||
57 | matches(bytes: [0xFF, 0xFB])
58 | }
59 |
60 | var isOgg: Bool {
61 | return matches(bytes: [0x4F, 0x67, 0x67, 0x53])
62 | }
63 |
64 | var isFlac: Bool {
65 | return matches(bytes: [0x66, 0x4C, 0x61, 0x43])
66 | }
67 |
68 | var isWav: Bool {
69 | return matches(bytes: [0x52, 0x49, 0x46, 0x46]) &&
70 | matches(bytes: [0x57, 0x41, 0x56, 0x45], range: 8...11)
71 | }
72 |
73 | var isMid: Bool {
74 | return matches(bytes: [0x4D, 0x54, 0x68, 0x64])
75 | }
76 |
77 | var isM4a: Bool {
78 | return matches(bytes: [0x4D, 0x34, 0x41, 0x20]) ||
79 | matches(bytes: [0x66, 0x74, 0x79, 0x70,
80 | 0x4D, 0x34, 0x41],
81 | range: 4...10)
82 | }
83 |
84 | var isOpus: Bool {
85 | return matches(bytes: [0x4F, 0x70, 0x75, 0x73,
86 | 0x48, 0x65, 0x61, 0x64],
87 | range: 28...35)
88 | }
89 |
90 | // MARK: - Application Types
91 |
92 | var isIco: Bool {
93 | return matches(bytes: [0x00, 0x00, 0x01, 0x00])
94 | }
95 |
96 | var isSqlite: Bool {
97 | return matches(bytes: [0x53, 0x51, 0x4C, 0x69])
98 | }
99 |
100 | var isTar: Bool {
101 | return matches(bytes: [0x75, 0x73, 0x74, 0x61, 0x72],
102 | range: 257...261)
103 | }
104 |
105 | var isRar: Bool {
106 | return matches(bytes: [0x52, 0x61, 0x72, 0x21, 0x1A, 0x07]) &&
107 | (matches(bytes: [0x0], range: 6...6) ||
108 | matches(bytes: [0x1], range: 6...6))
109 | }
110 |
111 | var isGzip: Bool {
112 | return matches(bytes: [0x1F, 0x8B, 0x08])
113 | }
114 |
115 | var isBz2: Bool {
116 | return matches(bytes: [0x42, 0x5A, 0x68])
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/DictionaryTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DictionaryTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 16/08/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class DictionaryTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testJsonData", testJsonData),
16 | ("testJsonString", testJsonString)
17 | ]
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 | }
23 |
24 | override func tearDown() {
25 | // Put teardown code here. This method is called after the invocation of each test method in the class.
26 | super.tearDown()
27 | }
28 |
29 | func testJsonData() {
30 | let data: [String: String] = ["1" : "Hello JSON!", "2" : "This is the second value!", "3" : "And the third one"]
31 |
32 | guard let jsonData = data.jsonData() else {
33 | XCTAssert(false, "Could not unwrap the Data?")
34 | return
35 | }
36 | do {
37 | let decodedData = try JSONSerialization.jsonObject(with: jsonData, options: [])
38 |
39 | if let udecodedData = decodedData as? [String : String] {
40 | if udecodedData == data {
41 |
42 | // Success
43 | XCTAssert(true)
44 | } else {
45 | // Failed
46 | XCTAssert(false, "The original data dict is not equal to the unwrapped one")
47 | }
48 | } else {
49 | XCTAssert(false, "Could not case Any JSON data to expected [String:String] dictionary")
50 | }
51 |
52 | } catch {
53 | XCTAssert(false, "JSON serualization raised an error: \(error)")
54 | }
55 | }
56 |
57 | func testJsonString() {
58 | let data: [String: String] = ["1" : "Hello JSON!", "2" : "This is the second value!", "3" : "And the third one"]
59 |
60 | guard let jsonString = data.jsonString() else {
61 | XCTAssert(false, "Could not unwrap the String?")
62 | return
63 | }
64 |
65 | guard let jsonObject = jsonString.data(using: .utf8) else {
66 | XCTAssert(false, "Could not unwrap the Data?")
67 | return
68 | }
69 |
70 | do {
71 | let decodedData = try JSONSerialization.jsonObject(with: jsonObject, options: [])
72 |
73 | if let udecodedData = decodedData as? [String : String] {
74 | if udecodedData == data {
75 |
76 | // Success
77 | XCTAssert(true)
78 | } else {
79 | // Failed
80 | XCTAssert(false, "The original data dict is not equal to the unwrapped one")
81 | }
82 | } else {
83 | XCTAssert(false, "Could not case Any JSON data to expected [String:String] dictionary")
84 | }
85 |
86 | } catch {
87 | XCTAssert(false, "JSON serualization raised an error: \(error)")
88 | }
89 | }
90 |
91 | func testPerformanceExample() {
92 | // This is an example of a performance test case.
93 | self.measure {
94 | // Put the code you want to measure the time of here.
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/Tests/ExtendedFoundationTests/IntDecimalToBinaryTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntDecimalToBinaryTests.swift
3 | // extensions-kit-tests
4 | //
5 | // Created by Astemir Eleev on 09/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExtendedFoundation
11 |
12 | class IntDecimalToBinaryTests: XCTestCase {
13 |
14 | static var allTests = [
15 | ("testDecimalToBinary", testDecimalToBinary),
16 | ("testBinaryToDecimal", testBinaryToDecimal)
17 | ]
18 |
19 | override func setUp() {
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 | }
22 |
23 | override func tearDown() {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | }
26 |
27 | func testDecimalToBinary() {
28 | // Decimal Binary
29 | // 0 0000
30 | // 1 0001
31 | // 2 0010
32 | // 3 0011
33 | // 4 0100
34 | // 5 0101
35 | // 6 0110
36 | // 7 0111
37 | // 8 1000
38 | // 9 1001
39 | // 10 1010
40 | // 11 1011
41 | // 12 1100
42 | // 13 1101
43 | // 14 1110
44 | // 15 1111
45 |
46 | let binary1 = 1.decimalToBinary()
47 | let binary2 = 2.decimalToBinary()
48 | let binary3 = 3.decimalToBinary()
49 | let binary4 = 4.decimalToBinary()
50 | let binary5 = 5.decimalToBinary()
51 | let binary6 = 6.decimalToBinary()
52 | let binary7 = 7.decimalToBinary()
53 | let binary8 = 8.decimalToBinary()
54 | let binary9 = 9.decimalToBinary()
55 | let binary10 = 10.decimalToBinary()
56 | let binary11 = 11.decimalToBinary()
57 | let binary12 = 12.decimalToBinary()
58 | let binary13 = 13.decimalToBinary()
59 | let binary14 = 14.decimalToBinary()
60 | let binary15 = 15.decimalToBinary()
61 |
62 | XCTAssert(binary1 == 1)
63 | XCTAssert(binary2 == 10)
64 | XCTAssert(binary3 == 11)
65 | XCTAssert(binary4 == 100)
66 | XCTAssert(binary5 == 101)
67 | XCTAssert(binary6 == 110)
68 | XCTAssert(binary7 == 111)
69 | XCTAssert(binary8 == 1000)
70 | XCTAssert(binary9 == 1001)
71 | XCTAssert(binary10 == 1010)
72 | XCTAssert(binary11 == 1011)
73 | XCTAssert(binary12 == 1100)
74 | XCTAssert(binary13 == 1101)
75 | XCTAssert(binary14 == 1110)
76 | XCTAssert(binary15 == 1111)
77 | }
78 |
79 | func testBinaryToDecimal() {
80 | let decimal1 = 1.binaryToDecimal()
81 | let decimal2 = 10.binaryToDecimal()
82 | let decimal3 = 11.binaryToDecimal()
83 | let decimal4 = 100.binaryToDecimal()
84 | let decimal5 = 101.binaryToDecimal()
85 | let decimal6 = 110.binaryToDecimal()
86 | let decimal7 = 111.binaryToDecimal()
87 |
88 | XCTAssert(decimal1 == 1)
89 | XCTAssert(decimal2 == 2)
90 | XCTAssert(decimal3 == 3)
91 | XCTAssert(decimal4 == 4)
92 | XCTAssert(decimal5 == 5)
93 | XCTAssert(decimal6 == 6)
94 | XCTAssert(decimal7 == 7)
95 | }
96 |
97 | func testPerformanceExample() {
98 | // This is an example of a performance test case.
99 | self.measure {
100 | // Put the code you want to measure the time of here.
101 | }
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/Sources/ExtendedSceneKit/SCNVector3+Operators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SCNVector3+Operators.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 11/05/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import SceneKit
10 |
11 | // MARK: - Standard operators
12 |
13 | func == (left: SCNVector3, right: SCNVector3) -> Bool {
14 | var isEqual = false
15 | if left.x == right.x && left.y == right.y && left.z == right.z {
16 | isEqual = true
17 | }
18 | return isEqual
19 | }
20 |
21 | func != (left: SCNVector3, right: SCNVector3) -> Bool {
22 | var isEqual = false
23 | if left.x == right.x && left.y == right.y && left.z == right.z {
24 | isEqual = true
25 | }
26 | return !isEqual
27 | }
28 |
29 | func + (left: SCNVector3, right: SCNVector3) -> SCNVector3 {
30 | return SCNVector3Make(left.x + right.x, left.y + right.y, left.z + right.z)
31 | }
32 |
33 | func += (left: inout SCNVector3, right: SCNVector3) {
34 | left = left + right
35 | }
36 |
37 | func - (left: SCNVector3, right: SCNVector3) -> SCNVector3 {
38 | return SCNVector3Make(left.x - right.x, left.y - right.y, left.z - right.z)
39 | }
40 |
41 | func -= (left: inout SCNVector3, right: SCNVector3) {
42 | left = left - right
43 | }
44 |
45 | func * (left: SCNVector3, right: SCNVector3) -> SCNVector3 {
46 | return SCNVector3Make(left.x * right.x, left.y * right.y, left.z * right.z)
47 | }
48 |
49 | func *= (left: inout SCNVector3, right: SCNVector3) {
50 | left = left * right
51 | }
52 |
53 | func * (vector: SCNVector3, scalar: Float) -> SCNVector3 {
54 | return SCNVector3Make(vector.x * scalar, vector.y * scalar, vector.z * scalar)
55 | }
56 |
57 | func * (vector: SCNVector3, scalar: CGFloat) -> SCNVector3 {
58 | let floatScalar = Float(scalar)
59 | return SCNVector3Make(vector.x * floatScalar, vector.y * floatScalar, vector.z * floatScalar)
60 | }
61 |
62 | func *= (vector: inout SCNVector3, scalar: Float) {
63 | vector = vector * scalar
64 | }
65 |
66 | func *= (vector: inout SCNVector3, scalar: CGFloat) {
67 | vector = vector * Float(scalar)
68 | }
69 |
70 | func / (left: SCNVector3, right: SCNVector3) -> SCNVector3 {
71 | return SCNVector3Make(left.x / right.x, left.y / right.y, left.z / right.z)
72 | }
73 |
74 | func /= (left: inout SCNVector3, right: SCNVector3) {
75 | left = left / right
76 | }
77 |
78 | func / (vector: SCNVector3, scalar: Float) -> SCNVector3 {
79 | return SCNVector3Make(vector.x / scalar, vector.y / scalar, vector.z / scalar)
80 | }
81 |
82 | func / (vector: SCNVector3, scalar: CGFloat) -> SCNVector3 {
83 | let floatScalar = Float(scalar)
84 | return SCNVector3Make(vector.x / floatScalar, vector.y / floatScalar, vector.z / floatScalar)
85 | }
86 |
87 | func /= (vector: inout SCNVector3, scalar: Float) {
88 | vector = vector / scalar
89 | }
90 |
91 | func /= (vector: inout SCNVector3, scalar: CGFloat) {
92 | vector = vector / Float(scalar)
93 | }
94 |
95 | // MARK: - Special operators
96 |
97 | infix operator +*
98 | infix operator +*=
99 |
100 | /// Cross product of two vectors
101 | func +* (left: SCNVector3, right: SCNVector3) -> SCNVector3 {
102 | let x = left.y * right.z - left.z * right.y
103 | let y = left.z * right.x - left.x * right.z
104 | let z = left.x * right.y - left.y * right.x
105 | return SCNVector3(x, y, z)
106 | }
107 |
108 | /// Cross product of two vectors
109 | func +*= (left: inout SCNVector3, right: SCNVector3) {
110 | let x = left.y * right.z - left.z * right.y
111 | let y = left.z * right.x - left.x * right.z
112 | let z = left.x * right.y - left.y * right.x
113 |
114 | left = SCNVector3(x, y, z)
115 | }
116 |
--------------------------------------------------------------------------------
/Sources/ExtendedUIKit/UIView/UIView+CACorners.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+CaCorners.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 10/09/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @available(iOS, introduced: 11.0)
12 | public extension UIView {
13 |
14 | /// Wrapps `CACornerMask` for more consistent naming. Serves as a proxy for `CACornerMask` and simplifies naming convension for rounded corners.
15 | ///
16 | /// Rather than doing this:
17 | ///
18 | ///`CACornerMask.layerMinXMinYCorner`
19 | ///
20 | /// you do this:
21 | ///
22 | /// `UICorner.topLeft`
23 | ///
24 | /// The struct provides all the capabilities of the standard API `CACornerMask` through `OptoinSet` protocol.
25 | ///
26 | /// There are `5` different constants that can be chained together: 4 constants for each corner and the 5th one for all corners.
27 | struct UICorner: OptionSet {
28 |
29 | // MARK: - Typealiases
30 |
31 | public typealias RawValue = UInt
32 |
33 | // MARK: - Conformance to OptionSet protocol
34 |
35 | public let rawValue: UInt
36 |
37 | // MARK: - Static properties
38 |
39 | public static let topLeft = UICorner(rawValue: 1 << 0)
40 | public static let topRight = UICorner(rawValue: 1 << 1)
41 | public static let bottomLeft = UICorner(rawValue: 1 << 2)
42 | public static let bottomRight = UICorner(rawValue: 1 << 3)
43 |
44 | public static let all: UICorner = [.topLeft, .topRight, .bottomLeft, .bottomRight]
45 |
46 | // MARK: - Initializers
47 |
48 | public init(rawValue: RawValue) {
49 | self.rawValue = rawValue
50 | }
51 |
52 | // MARK: - Fileprivate methods for internal usage
53 |
54 | fileprivate func convert() -> CACornerMask {
55 | switch self {
56 | case .topLeft:
57 | return CACornerMask.layerMinXMinYCorner
58 | case .topRight:
59 | return CACornerMask.layerMaxXMinYCorner
60 | case .bottomLeft:
61 | return CACornerMask.layerMinXMaxYCorner
62 | case .bottomRight:
63 | return CACornerMask.layerMaxXMaxYCorner
64 | case .all:
65 | return [CACornerMask.layerMinXMinYCorner, CACornerMask.layerMaxXMinYCorner, CACornerMask.layerMinXMaxYCorner, CACornerMask.layerMaxXMaxYCorner]
66 | default:
67 | return CACornerMask(rawValue: rawValue)
68 | }
69 | }
70 |
71 | static fileprivate func encode(cornerMask: CACornerMask) -> UICorner {
72 | return UICorner(rawValue: cornerMask.rawValue)
73 | }
74 | }
75 |
76 | /// Rounds the specified `corners` wit the given `radius`
77 | ///
78 | /// - Parameters:
79 | /// - corners: is a UICorner struct
80 | /// - radius: is a CGFloat argument
81 | func round(corners: UICorner, radius: CGFloat) {
82 | layer.cornerRadius = radius
83 | layer.maskedCorners = corners.convert()
84 | clipsToBounds = true
85 | }
86 |
87 | /// Rounds all the corners with the given radius
88 | func roundAllCorners(with radius: CGFloat) {
89 | round(corners: .all, radius: radius)
90 | }
91 |
92 | /// Returns current corner radius
93 | func getCornerRadius() -> CGFloat {
94 | return layer.cornerRadius
95 | }
96 |
97 | func getRoundedCorners() -> UIView.UICorner {
98 | return UICorner.encode(cornerMask: layer.maskedCorners)
99 | }
100 |
101 | func resetRoundedCorners() {
102 | layer.cornerRadius = 0
103 | layer.maskedCorners = .init(rawValue: 0)
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Sources/ExtendedFoundationDataStructures/Dequeue/Dequeue.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dequeue.swift
3 | // extensions-kit
4 | //
5 | // Created by Astemir Eleev on 01/10/2018.
6 | // Copyright © 2018 Astemir Eleev. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public struct Dequeue {
12 | // MARK: - Private properties
13 |
14 | private var data: [T]
15 |
16 | // MARK: - Public properties
17 |
18 | public var count: Int {
19 | return data.count
20 | }
21 |
22 | public var capacity: Int {
23 | get {
24 | return data.capacity
25 | }
26 | set {
27 | data.reserveCapacity(newValue)
28 | }
29 | }
30 |
31 | // MARK: - Initializers
32 |
33 | public init() {
34 | data = []
35 | }
36 |
37 | public init(_ elements: S) where S.Iterator.Element == T {
38 | self.init()
39 | data.append(contentsOf: elements)
40 | }
41 |
42 | // MARK: - Methods
43 |
44 | public mutating func dequeueFront() -> T? {
45 | return data.removeFirst()
46 | }
47 |
48 | public mutating func dequeueBack() -> T? {
49 | return data.removeLast()
50 | }
51 |
52 | public mutating func enqueue(front element: T) {
53 | data.insert(element, at: 0)
54 | }
55 |
56 | public mutating func enqueue(back element: T) {
57 | data.append(element)
58 | }
59 |
60 | public mutating func clear() {
61 | data.removeAll()
62 | }
63 |
64 | public mutating func isFull() -> Bool {
65 | return count == data.capacity
66 | }
67 |
68 | public func isEmpty() -> Bool {
69 | return data.isEmpty
70 | }
71 |
72 | public func peekFirst() -> T? {
73 | return data.first
74 | }
75 |
76 | public func peekLast() -> T? {
77 | return data.last
78 | }
79 |
80 | // MARK: - Private methods
81 |
82 | private func checkIndex(index: Int) throws {
83 | if index < 0 || index > count {
84 | throw DequeueError.indexOutOfRange
85 | }
86 | }
87 | }
88 |
89 | enum DequeueError: Error {
90 | case indexOutOfRange
91 | }
92 |
93 | extension Dequeue: CustomStringConvertible, CustomDebugStringConvertible {
94 |
95 | public var description: String {
96 | return data.description
97 | }
98 |
99 | public var debugDescription: String {
100 | return data.description
101 | }
102 | }
103 |
104 | extension Dequeue: ExpressibleByArrayLiteral {
105 |
106 | public init(arrayLiteral elements: T...) {
107 | self.init(elements)
108 | }
109 | }
110 |
111 | extension Dequeue: Sequence {
112 |
113 | public func makeIterator() -> AnyIterator {
114 | let indexedIterator = IndexingIterator(_elements: data.lazy)
115 | return AnyIterator(indexedIterator)
116 | }
117 |
118 | public func generate() -> AnyIterator {
119 | let indexingIteratoer = IndexingIterator(_elements: data.lazy)
120 | return AnyIterator(indexingIteratoer)
121 |
122 | }
123 | }
124 |
125 |
126 | extension Dequeue: MutableCollection {
127 |
128 | // MARK: - Core protocol conformance
129 |
130 | public var startIndex: Int {
131 | return 0
132 | }
133 |
134 | public var endIndex: Int {
135 | return count - 1
136 | }
137 |
138 | public func index(after i: Int) -> Int {
139 | return data.index(after: i)
140 | }
141 |
142 | // MARK: - Subscript implementation
143 |
144 | public subscript(index: Int) -> T {
145 | get {
146 | checkHandledIndex(index: index)
147 | return data[index]
148 | }
149 | set {
150 | checkHandledIndex(index: index)
151 | data[index] = newValue
152 | }
153 | }
154 |
155 | // MARK: - Utility method
156 |
157 | private func checkHandledIndex(index: Int) {
158 | do {
159 | try checkIndex(index: index)
160 | } catch {
161 | fatalError(error.localizedDescription)
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------