Skip to content

Commit

Permalink
feat: add foundation extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
gtokman committed Apr 24, 2021
1 parent e3fae25 commit 06e1412
Show file tree
Hide file tree
Showing 16 changed files with 492 additions and 4 deletions.
16 changes: 16 additions & 0 deletions Sources/ExtensionKit/CoreGraphics/CGPoint.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import CoreGraphics

public extension CGPoint {

/// Offset point by new x and y
/// - Parameters:
/// - x: x
/// - y: y
/// - Returns: new point
func offseted(x: CGFloat = 0.0, y: CGFloat = 0.0) -> CGPoint {
var point = self
point.x += x
point.y += y
return point
}
}
64 changes: 64 additions & 0 deletions Sources/ExtensionKit/CoreGraphics/CGRect.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import CoreGraphics

public extension CGRect {

var topLeft: CGPoint {
return origin
}

var topRight: CGPoint {
return CGPoint(x: maxX, y: minY)
}

var topMiddle: CGPoint {
return CGPoint(x: midX, y: minY)
}

var bottomLeft: CGPoint {
return CGPoint(x: minX, y: maxY)
}

var bottomRight: CGPoint {
return CGPoint(x: maxX, y: maxY)
}

var bottomMiddle: CGPoint {
return CGPoint(x: midX, y: maxY)
}

var leftMiddle: CGPoint {
return CGPoint(x: minX, y: midY)
}

var rightMiddle: CGPoint {
return CGPoint(x: maxX, y: midY)
}

var midX: CGFloat {
return (maxX - minX) / 2
}

var midY: CGFloat {
return (maxY - minY) / 2
}

/// Center taking size into account
var center: CGPoint {
get {
let x = origin.x + size.width / 2
let y = origin.y + size.height / 2
return CGPoint(x: x, y: y)
}
set {
origin.x = newValue.x - size.width / 2
origin.y = newValue.y - size.height / 2
}
}

var sameCenterSquare: CGRect {
let maxLength = max(size.width, size.height)
var rect = CGRect(x: 0, y: 0, width: maxLength, height: maxLength)
rect.center = center
return rect
}
}
17 changes: 17 additions & 0 deletions Sources/ExtensionKit/CoreGraphics/Operators.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import CoreGraphics

public func +(lhs: CGSize, rhs: CGSize) -> CGSize {
return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height)
}

public func -(lhs: CGSize, rhs: CGSize) -> CGSize {
return CGSize(width: lhs.width - rhs.width, height: lhs.height - rhs.height)
}

public func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
}

public func /(lhs: CGSize, rhs: CGFloat) -> CGSize {
return CGSize(width: lhs.width / rhs, height: lhs.height / rhs)
}
3 changes: 0 additions & 3 deletions Sources/ExtensionKit/ExtensionKit.swift

This file was deleted.

27 changes: 27 additions & 0 deletions Sources/ExtensionKit/Foundation/Array.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

public extension Array where Element: Equatable {

/// Removes the given element in the array.
///
/// - Parameter element: The element to be removed.
/// - Returns: The element got removed, or `nil` if the element doesn't exist.
@discardableResult
mutating func remove(_ element: Element) -> Element? {
if let index = self.firstIndex(of: element) {
return self.remove(at: index)
}
return nil
}

/// Returns an array where repeating elements of the receiver got removed.
var removingRepeatElements: Array<Element> {
var arr = Array<Element>()
forEach {
if !arr.contains($0) {
arr.append($0)
}
}
return arr
}
}
10 changes: 10 additions & 0 deletions Sources/ExtensionKit/Foundation/CFRunLoopTimer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Foundation

public extension CFRunLoopTimer {

/// Invalidate CFRunLoopTimer
func invalidate() {
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), self, .commonModes)
}

}
13 changes: 13 additions & 0 deletions Sources/ExtensionKit/Foundation/Collection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation

public extension Collection {

/// Safe indexing.
/// Returns the element at the specified index iff it is within bounds, otherwise nil.
/// ref: https://stackoverflow.com/questions/25329186/safe-bounds-checked-array-lookup-in-swift-through-optional-bindings
///
/// - Parameter index: The index used to retrieve a value / an object.
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
11 changes: 11 additions & 0 deletions Sources/ExtensionKit/Foundation/Data.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

public extension Data {
/// Returns a string of hex value.
var hexString: String {
return withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> String in
let buffer = bytes.bindMemory(to: UInt8.self)
return buffer.map {String(format: "%02hhx", $0)}.reduce("", { $0 + $1 })
}
}
}
15 changes: 15 additions & 0 deletions Sources/ExtensionKit/Foundation/Date.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

public extension Date {

/// Days of the week in Gregorian calendar (Sunday - Saturday)
static let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

/// Current day of the week in Gregorian calendar
var day: String {
let gregorian = Calendar(identifier: .gregorian)
let weekday = gregorian.component(.weekday, from: self)
return Date.days[weekday]
}

}
67 changes: 67 additions & 0 deletions Sources/ExtensionKit/Foundation/FileManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Foundation

public extension FileManager {

/// Size of file at path
///
/// - Parameter path: file path
/// - Returns: Size in bytes
func fileSize(atPath path: String) -> Int {
let attributes = try? attributesOfItem(atPath: path)
return (attributes?[FileAttributeKey.size] as? Int) ?? 0
}

/// Size of folder
///
/// - Parameter path: folder path
/// - Returns: size in bytes
func folderSize(atPath path: String) -> Int {
let manager = FileManager.default
if manager.fileExists(atPath: path) {
do {
let childFilesEnumerator = try (manager.subpathsOfDirectory(atPath: path) as NSArray).objectEnumerator()
var folderSize = 0
while childFilesEnumerator.nextObject() != nil {
if let fileName = childFilesEnumerator.nextObject() as? String,
let url = URL(string: path) {
let fileAbsolutePath = url.appendingPathComponent(fileName).absoluteString
folderSize += self.fileSize(atPath: fileAbsolutePath)
}
}
return folderSize

} catch {
dprint(error)
}
}
return 0
}


/// Size of directory at URL
///
/// - Parameter URL: URL
/// - Returns: Size in bytes
func directorySize(at URL: URL) -> Int {
var result = 0
let properties = [URLResourceKey.localizedNameKey, URLResourceKey.creationDateKey, URLResourceKey.localizedTypeDescriptionKey]
let manager = FileManager.default
do {
let urls = try manager.contentsOfDirectory(at: URL, includingPropertiesForKeys: properties, options: .skipsHiddenFiles)
for fileSystemItem in urls {
var directory: ObjCBool = false
let path = fileSystemItem.path
manager.fileExists(atPath: path, isDirectory: &directory)
if directory.boolValue {
result += directorySize(at: fileSystemItem)
} else {
result += try manager.attributesOfItem(atPath: path)[FileAttributeKey.size] as! Int
}
}

} catch {
dprint("Error: \(error)")
}
return result
}
}
30 changes: 30 additions & 0 deletions Sources/ExtensionKit/Foundation/Global.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

/// AppVersion set in `CFBundleShortVersionString`
public var appVersion: String {
return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
}

/// Languages set in user defaults key `AppleLanguages`
public var systemLanguages: [String] {
return UserDefaults.standard.stringArray(forKey: "AppleLanguages") ?? []
}

/// Sleeps the thread
/// - Parameter duration: in seconds
public func sleep(duration: TimeInterval) {
#if DEBUG
Thread.sleep(forTimeInterval: duration)
#endif
}

/// Swift still calls `print()` and/or `debugPrint()` in shipped apps.
/// We use a method described in onevcat's post (https://onevcat.com/2016/02/swift-performance/)
/// to optimaze the performance.
///
/// - Parameter item: items to print
public func dprint(_ item: @autoclosure () -> Any) {
#if DEBUG
print(item())
#endif
}
72 changes: 72 additions & 0 deletions Sources/ExtensionKit/Foundation/Int.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import Foundation

public extension Int {

/// Whether self is an odd number
var isOdd: Bool {
return !isEven
}

/// Whether self is an even number
var isEven: Bool {
return self % 2 == 0
}

/// Treats 0 as nil
var nilIfZero: Int? {
if self == 0 { return nil }
return self
}

/// Make the number to string
var string: String {
return String(self)
}

/// Make a range from zero to self
var range: CountableRange<Int> {
return 0..<self
}

/// Return a number of instances
///
/// - Parameter creation: The initialization of the object
/// - Returns: An array containing the objects
func instances<T>(of creation: @autoclosure () throws -> T) rethrows -> [T] {
return try (0 ..< self).map { _ in
try creation()
}
}

/// Return if `self` is in the given range.
///
/// - Parameter range: Target range.
/// - Returns: `true` if self is in the range, otherwise `false`.
func inRange(_ range: Range<Int>) -> Bool {
return range.contains(self)
}


// minutes to seconds
var minutes: Int {
return self * 60
}

// hours to seconds
var hours: Int {
return (self * 60).minutes
}

var days: Int {
return (self * 24).hours
}

var months: Int {
return (self * 30).days
}

var years: Int {
return (self * 12).months
}

}
31 changes: 31 additions & 0 deletions Sources/ExtensionKit/Foundation/NSObject.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Foundation

public extension NSObject {

/// Exchange two implementations of the given selectors, aka method swizzling.
///
/// - Parameters:
/// - originalSelector: The original selector.
/// - swizzledSelector: Another selector.
class func exchangeImplementations(originalSelector: Selector, swizzledSelector: Selector) {
guard
let originalMethod = class_getInstanceMethod(self, originalSelector),
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
else {
dprint("Error: Unable to exchange method implemenation!!")
return
}
method_exchangeImplementations(originalMethod, swizzledMethod)
}

/// Return class name.
var className: String {
return type(of: self).description().components(separatedBy: ".").last ?? ""
}

/// Print the deinitialization message of self.
final func printDeinitMessage() {
dprint("Deinit Message: \(className): \(self)")
}

}
Loading

0 comments on commit 06e1412

Please sign in to comment.