DJSwiftHelpers is a Swift library containing useful Swift extensions. It allows you to perform many tasks easily.
Add the package [email protected]:ddaddy/DJSwiftHelpers.git to your project.
For most helpers you can import DJSwiftHelpers however for additional UIKit or SwiftUI helpers that are not extension friendly, also import DJSwiftHelpers_UIKit or import DJSwiftHelpers_SwiftUI
Install Carthage if not already installed
Change to the directory of your Xcode project, and Create and Edit your Cartfile and add DJSwiftHelpers:
$ cd /path/to/MyProject
$ touch Cartfile
$ open CartfileAdd the following line to your Cartfile
github "ddaddy/DJSwiftHelpers"
Save and then run:
$ carthage update --use-xcframeworksOnce complete, drag libraries as required
- Carthage/Build/iOS/DJSwiftHelpers
- Carthage/Build/iOS/DJSwiftHelpers_Extension
- Carthage/Build/iOS/DJSwiftHelpers_SwiftUI
into the Frameworks, Libraries & Embeded Content section of your project.
If adding to an extension, select Do not embed but also add to your main iOS or watchOS app and select Embed & Sign.
Removes an element from an Array. If the element occurs multiple times in the array, only the first element will be removed.
remove(element: Element)Moves an element to a new index
move(_ element: Element, to newIndex: Index)move(from oldIndex: Index, to newIndex: Index)Split an Array into multiple arrays of size
chunked(into size: Int) -> [[Element]]Filters an array to return one of each item where the keyPath elements are unique
uniques<T: Hashable>(by keyPath: KeyPath<Element, T>) -> [Element]
Example:
.uniques(by: \.surname)Filters an array to return one of each item where the combined keyPath elements are unique
uniques<T: Hashable, U: Hashable>(by keyPath: KeyPath<Element, T>, and secondKeyPath: KeyPath<Element, U>) -> [Element]
Example:
.uniques(by: \.firstName, and: \.surname)Adds a listener to array elements
elementChangeListener(cancellables: inout [AnyCancellable], change: @escaping (Element)->())Determine if a location is within a bounding rect
locationIsInside(minLat:Double, maxLat:Double, minLong:Double, maxLong:Double) -> BoolRemoves the risk of indexOutOfBounds crashes.
subscript(safe index: Index) -> Element?
// array[safe: 5]Create a Date from some date elements
Date.from(year: Int, month: Int, day: Int) -> Date?Create a Date from a date string. Be careful using this because DateFormatter's can be
very expensive for performance.
Date.parse(_ string: String, format: String = "yyyy-MM-dd") -> Date?Adds additional seconds or minutes to a Date
adding(seconds: Int) -> Dateadding(minutes: Int) -> DateDeduct one Date from another
- (lhs: Date, rhs: Date) -> TimeIntervalConverts an iso8601 string with or without milliseconds 2020-11-11T11:39:00Z ro 2020-11-11T11:39:00.000Z
WARNING! These are expensive operations so probably best not to use in a loop
var iso8601: Stringvar iso8601withFractionalSeconds: Stringvar iso8601: Date?var iso8601withFractionalSeconds: Date?Skips to the next container whilst iterating over an unkeyed decoding container using while !container.isAtEnd { }
skip()Property wrapper that allows a Decodable var to be decoded from either a String or an Int
struct GeneralProduct: Decodable {
var price: Double
// These can be decoded from either String or Int
@Flexible var intId: Int
@Flexible var stringId: String
}Round doubles for less accurate comparisons
static func equal(_ lhs: Double, _ rhs: Double, precise value: Int? = nil) -> Boolfunc precised(_ value: Int = 1) -> DoubleDetermines if running inside an app extension or not
static var isExtension:BoolFetches all contents (files & folders) in a specified file path and all subfolders
allContents(path: String) -> [URL]Fetch contents (files & folders) in a specifies file path, none recursive
urls(for directory: FileManager.SearchPathDirectory, skipsHiddenFiles: Bool = true ) -> [URL]?
urls(for directory: URL, skipsHiddenFiles: Bool = true ) -> [URL]?Parse a json file into the specified object type
readJSONFromFile<T: Decodable>(fileURL: URL, type: T.Type) -> T?A UIView subclass with a gradient background.
Can be used in a storyboard as it supports IBDesignable however to use IBDesignable from an external framework you need to add this small class to your project:
@IBDesignable
class GradientView: Gradient {
@IBInspectable override var startColor: UIColor {get { return super.startColor }set { super.startColor = newValue }}
@IBInspectable override var endColor: UIColor {get { return super.endColor }set { super.endColor = newValue }}
@IBInspectable override var startLocation: Double {get { return super.startLocation }set { super.startLocation = newValue }}
@IBInspectable override var endLocation: Double {get { return super.endLocation }set { super.endLocation = newValue }}
@IBInspectable override var horizontalMode: Bool {get { return super.horizontalMode }set { super.horizontalMode = newValue }}
@IBInspectable override var diagonalMode: Bool {get { return super.diagonalMode }set { super.diagonalMode = newValue }}
}Return the emoji flag for the given Locale
var emojiFlag: StringChange the colour of certain text within an NSMutableAttributedString
func setColorForText(textToFind: String, withColor color: UIColor)Truncate a string by removing all characters at the position
truncated(limit: Int, position: TruncationPosition = .tail, leader: String = "") -> StringDelete a prefix if it exists
deletingPrefix(_ prefix: String) -> StringAllows you to use [8] subscript to get 1 character from a String
subscript(_ i: Int) -> StringAllows you to use [0..<9] subscript to truncate a String
subscript (r: Range<Int>) -> StringAllows you to use [0...8] subscript to truncate a String
subscript (r: CountableClosedRange<Int>) -> StringConvert a String into a Bool by looking for the relevant type texts (Yes, No, True etc..)
var bool: Bool?Convert a String into a Double
toDouble() -> Double?Convert a String to Data then saves it to the given URL path
save(path:URL) -> Boolindex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index?endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index?indices<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Index]ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>]Splits a string into an array of lines
var lines: [SubSequence]Returns a string that appears between 2 strings
func slice(from: String? = nil, to: String? = nil) -> String?Creates a recurring Task that executes until success
static func retrying(priority: TaskPriority? = nil, maxRetryCount: Int = .max, retryDelay: TimeInterval = 1, operation: @Sendable @escaping () async throws -> Success) -> TaskReturn the top most ViewController regardless if embeded in a navigation stack or not
class getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController?Display an alert regardless of what's on the screen
class displayGlobalAlert(title:String, message:String, completion: (() -> Void)? = nil)Dismiss back to the root view controller
class dismisstoRoot(animated:Bool, completion:(() -> Void)? = nil)Add an underline style to the title label
underlineText()Convert a hex string to a UIColor
init?(hex: String)Find the opposite colour of a UIColor
var inverted: UIColorvar isPhone: Bool
var isPad: Bool
var isTV: Bool
var isCarPlay: BoolSpecify a font weight to a dynamic font style
static func preferredFont(for style: TextStyle, weight: Weight) -> UIFontuse like:
label.font = UIFont.preferredFont(for: .title2, weight: .medium)Convert a String to Data then save it to the given URL path
saveAsPNG(path:URL) -> BoolSave an image to a temporary file
tempURLForImage(named name: String) -> URL?Resize an image
resize(width: CGFloat) -> UIImage
resize(height: CGFloat) -> UIImage
resize(size: CGSize) -> UIImageAdds an SFSymbol image to the beginning or end of a UILabel's text
addSFSymbol(named:String, position:UILabel.SFSymbolPosition = .beginning, fontWeight weight:UIFont.Weight = .bold, fontDescriptorDesign design:UIFontDescriptor.SystemDesign = .rounded)Helpers to use with Task.sleep(nanoseconds:)
static let oneSecond: UInt64 = 1_000_000_000
static let twoSeconds: UInt64 = 2_000_000_000
static let threeSeconds: UInt64 = 3_000_000_000
static let fourSeconds: UInt64 = 4_000_000_000
static let fiveSeconds: UInt64 = 5_000_000_000
static let tenSeconds: UInt64 = 10_000_000_000
static let twentySeconds: UInt64 = 20_000_000_000
static let thirtySeconds: UInt64 = 30_000_000_000
static let sixtySeconds: UInt64 = 60_000_000_000Remove and dealloc all subviews from a UIStackView
safelyRemoveArrangedSubviews()Add a background color to a UIStackView
addBackground(color: UIColor)Sets up the autolayout constraints to pin a view to it's superview
pinToSuperview(with insets: UIEdgeInsets = .zero, edges: UIRectEdge = .all)Add a border to the insied of a UIView at a specific edge
addBorder(_ edge: UIRectEdge, color: UIColor, thickness: CGFloat)Displays a UIAlertController message with an "Ok" cancel button
displayAlert(title:String?, message:String?)displayAlert(title:String?, message:String?, buttonAction:((UIAlertAction)->())?)Add's & removes a child UIViewController to a continer view
addChild(_ child: UIViewController, in containerView: UIView)removeChild(_ child: UIViewController)Fetches the key window
var key: UIWindow?Generates a URLRequest whilst automatically converting headers and body to the correct formats
init?(url:URL, headers:[String:String], postBody:[String:Any], json:Bool = true, timeout:TimeInterval = 60.0)init?(url:URL, headers:[String:String], postString:String, timeout:TimeInterval = 60.0)init?(url:URL, headers:[String:String], parameters:[String:String], timeout:TimeInterval = 60.0)Returns the HTTP status code from a URLResponse
statusCode() -> Int?A URLSession that will bypass an SSL certificate check.
static var selfSignedSSLSession:URLSessionSave to UserDefaults or remove it if nil
setOrDelete(value:String?, forKey key:String)
setOrDelete(value:Int?, forKey key:String)Delete all UserDefault's for the containing bundleIdentifier
func resetDefaults()A backward compatible NavigationStack
NavigationStackPre16() {
SomeRootView()
}A SwiftUI view to display the share sheet with Open in Safari
Example:
.background(
// Share button action
SafariActivityView(isPresented: $activityPresented, url: url)
)A UIActivityViewController used to present a Share Sheet
Example:
struct ShareItem: Identifiable {
let id = UUID()
let data: Data
}
SomeView()
.sheet(item: $shareItem) { shareItem in
ActivityView(item: shareItem.data)
.presentationDetents([.medium])
}An NSSharingServicePicker used to present a Share Sheet on macOS
Example:
.background(
SharingServicePicker(isPresented: $sharePDF, sharingItems: [pdfURL as Any])
)A SwiftUI view modifier that executes a block of code only once during the first display of the view
.onFirstAppear(_ action: @escaping () -> ()) -> some ViewA SwiftUI view modifier that conditionally hides the view
.hideable(isHidden: true)A SwiftUI view modifier that captures the hosting NSWindow. Useful when closing a specific window.
@State private var hostingWindow: NSWindow?
SomeView()
.captureWindow(into: $hostingWindow)
func close() {
hostingWindow?.performClose(nil)
}Copyright (c) 2020 Darren Jones (Dappological Ltd.)