├── Common ├── Array.swift ├── DateFormatter.swift ├── FactoryPattern.swift ├── Fonts.swift └── Typealias.swift ├── README.md ├── SwiftUI └── Views.swift └── UIKit └── Constraints.swift /Common /Array.swift: -------------------------------------------------------------------------------- 1 | 2 | extension Array { 3 | public subscript(safeIndex index: Int) -> Iterator.Element? { 4 | return index >= 0 && index < endIndex ? self[index] : nil 5 | } 6 | } 7 | 8 | let numbers = [10, 23, 12, 63, 1, 3] 9 | let number1 = numbers[safeIndex: 0] // = 10 10 | let number2 = numbers[safeIndex: 6] // = nil 11 | -------------------------------------------------------------------------------- /Common /DateFormatter.swift: -------------------------------------------------------------------------------- 1 | 2 | var date = Date.now 3 | var dateString = "" 4 | 5 | // 7/25/2021, 12:55 AM 6 | dateString = date.formatted() 7 | 8 | // The current day of the year 9 | dateString = date.formatted( 10 | .dateTime 11 | .dayOfYear() 12 | ) 13 | 14 | 15 | // The second Wed in July will return 2 16 | dateString = date.formatted( 17 | .dateTime 18 | .day(.ordinalOfDayInMonth) 19 | ) 20 | 21 | // 1 22 | dateString = date.formatted( 23 | .dateTime 24 | .day(.twoDigits) 25 | ) 26 | 27 | // 01 AM 28 | dateString = date.formatted( 29 | .dateTime 30 | .hour(.twoDigits(amPM: .abbreviated)) 31 | ) 32 | 33 | 34 | // Jul 3, 2021 35 | dateString = date.formatted( 36 | .dateTime 37 | .year() 38 | .day() 39 | .month() 40 | ) 41 | 42 | // July 3, 2021 43 | dateString = date.formatted( 44 | .dateTime 45 | .year() 46 | .month(.wide) 47 | .day() 48 | ) 49 | 50 | // July 03, 21 51 | dateString = date.formatted( 52 | .dateTime 53 | .year(.twoDigits) 54 | .month(.wide) 55 | .day(.twoDigits) 56 | ) 57 | 58 | // Q3 59 | dateString = date.formatted( 60 | .dateTime 61 | .quarter(.abbreviated) 62 | ) 63 | 64 | // 03 65 | dateString = date.formatted( 66 | .dateTime 67 | .quarter(.twoDigits) 68 | ) 69 | 70 | // 3rd quarter 71 | dateString = date.formatted( 72 | .dateTime 73 | .quarter(.wide) 74 | ) 75 | 76 | // Sunday, July 25, 2021 77 | dateString = date.formatted( 78 | .dateTime 79 | .day() 80 | .month() 81 | .year() 82 | .weekday(.wide) 83 | ) 84 | 85 | // 2021-07-17 86 | dateString = date.formatted( 87 | .iso8601 88 | .day() 89 | .month() 90 | .year() 91 | .dateSeparator(.dash) 92 | ) 93 | 94 | // 25 de julio de 2021 95 | dateString = date.formatted( 96 | .dateTime 97 | .day() 98 | .month(.wide) 99 | .year() 100 | .locale( 101 | Locale(identifier: "es_UY") 102 | ) 103 | ) 104 | 105 | // ----------------------------------------------------------------------- 106 | 107 | // Sunday, July 25, 2021 108 | dateString = date.formatted(date: .complete, time: .omitted) 109 | 110 | // Jul 25, 2021 111 | dateString = date.formatted(date: .abbreviated, time: .omitted) 112 | 113 | // July 25, 2021 114 | dateString = date.formatted(date: .long, time: .omitted) 115 | 116 | // 7/25/2021 117 | dateString = date.formatted(date: .numeric, time: .omitted) 118 | 119 | // 7/25/2021, 1:18:14 AM GMT-3 120 | dateString = date.formatted(date: .numeric, time: .complete) 121 | 122 | // 7/25/2021, 1:18 AM 123 | dateString = date.formatted(date: .numeric, time: .shortened) 124 | 125 | // Sunday, July 25, 2021, 1:19:11 AM 126 | dateString = date.formatted(date: .complete, time: .standard) 127 | 128 | // ----------------------------------------------------------------------- 129 | 130 | var dateFormat = 131 | Date.FormatStyle() 132 | .day(.twoDigits) 133 | .month(.wide) 134 | .year() 135 | .locale( 136 | Locale(identifier: "es_UY") 137 | ) 138 | 139 | // 25 de julio de 2021 140 | dateString = date.formatted(dateFormat) 141 | 142 | dateFormat = Date.FormatStyle(date: .numeric, time: .omitted) 143 | 144 | // 7/25/2021 145 | dateString = date.formatted(dateFormat) 146 | 147 | var serverDate = try? Date("01/08/2021", strategy: dateFormat) 148 | 149 | // 2021-01-08 150 | dateString = serverDate!.formatted( 151 | .iso8601 152 | .day() 153 | .month() 154 | .year() 155 | .dateSeparator(.dash) 156 | ) 157 | 158 | 159 | let formatStrategy = Date.ParseStrategy( 160 | format: "\(day: .twoDigits)-\(month: .twoDigits)-\(year: .defaultDigits)", 161 | locale: Locale(identifier: "es_UY"), 162 | timeZone: .current 163 | ) 164 | serverDate = try? Date("01-08-2021", strategy: formatStrategy) 165 | -------------------------------------------------------------------------------- /Common /FactoryPattern.swift: -------------------------------------------------------------------------------- 1 | extension String { 2 | static var empty: String { 3 | return "" 4 | } 5 | } 6 | 7 | enum LabelType { 8 | 9 | typealias LabelInformation = (font: UIFont, color: UIColor) 10 | 11 | case custom(information: LabelInformation) 12 | case regular 13 | } 14 | 15 | extension LabelType { 16 | static var title: Self { 17 | .custom(information: LabelInformation(font: .titleFont, color: .black)) 18 | } 19 | 20 | static var subtitle: Self { 21 | .custom(information: LabelInformation(font: .subtitleFont, color: .gray)) 22 | } 23 | 24 | static var body: Self { 25 | .custom(information: LabelInformation(font: .regularFont, color: .black)) 26 | } 27 | } 28 | 29 | extension LabelType { 30 | var information: LabelInformation { 31 | switch self { 32 | case .custom(let labelInformation): 33 | return labelInformation 34 | default: 35 | return LabelInformation(font: .systemFont(ofSize: 16), color: .black) 36 | } 37 | } 38 | } 39 | 40 | extension UIFont { 41 | enum CustomFontType: String { 42 | case regular = "OpenSans-Regular" 43 | case semiBold = "OpenSans-SemiBold" 44 | case bold = "OpenSans-Bold" 45 | case light = "OpenSans-Light" 46 | } 47 | 48 | static func customFont(type: CustomFontType, size: CGFloat) -> UIFont { 49 | return UIFont(name: type.rawValue, size: size) ?? UIFont.systemFont(ofSize: size) 50 | } 51 | 52 | static var titleFont: UIFont { 53 | return .customFont(type: .bold, size: 24) 54 | } 55 | 56 | static var subtitleFont: UIFont { 57 | return .customFont(type: .semiBold, size: 18) 58 | } 59 | 60 | static var regularFont: UIFont { 61 | return .customFont(type: .regular, size: 16) 62 | } 63 | } 64 | 65 | extension UILabel { 66 | static func newLabel(withMessage text: String = .empty, type: LabelType) -> UILabel { 67 | let label = UILabel() 68 | label.text = text 69 | label.font = type.information.font 70 | label.textColor = type.information.color 71 | return label 72 | } 73 | } 74 | 75 | 76 | let titleLabel = UILabel.newLabel(withMessage: "Welcome", type: .title) 77 | let subtitleLabel = UILabel.newLabel(withMessage: "Create an account to start", type: .subtitle) 78 | let customSizeLabel = UILabel.newLabel(withMessage: "Custom size label", type: .custom(information: LabelType.LabelInformation(font: .customFont(type: .bold, size: 32), color: .black))) 79 | let labelWithoutText = UILabel.newLabel(type: .regular) 80 | -------------------------------------------------------------------------------- /Common /Fonts.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | enum CustomFontType: String { 5 | case regular = "OpenSans-Regular" 6 | case semiBold = "OpenSans-SemiBold" 7 | case bold = "OpenSans-Bold" 8 | case light = "OpenSans-Light" 9 | } 10 | 11 | // MARK: - SwiftUI 12 | 13 | extension Font { 14 | 15 | static func customFont(type: CustomFontType, size: CGFloat) -> Font { 16 | return custom(type.rawValue, size: size) 17 | } 18 | 19 | static let titleFont: Font = { 20 | return customFont(type: .semiBold, size: 26) 21 | }() 22 | 23 | static let descriptionFont: Font = { 24 | return customFont(type: .semiBold, size: 14) 25 | }() 26 | } 27 | 28 | struct ContentView: View { 29 | var body: some View { 30 | Text("Hello World!") 31 | .font(.titleFont) 32 | } 33 | } 34 | 35 | // MARK: - UIKit 36 | 37 | extension UIFont { 38 | 39 | static func customFont(type: CustomFontType, size: CGFloat) -> UIFont { 40 | return UIFont(name: type.rawValue, size: size) ?? UIFont.systemFont(ofSize: size) 41 | } 42 | 43 | static let titleFont: UIFont = { 44 | return customFont(type: .semiBold, size: 26) 45 | }() 46 | 47 | static let descriptionFont: UIFont = { 48 | return customFont(type: .semiBold, size: 14) 49 | }() 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Common /Typealias.swift: -------------------------------------------------------------------------------- 1 | 2 | typealias JSONObject = [String: Any] 3 | var json = JSONObject() 4 | 5 | // ----------------------------------- 6 | 7 | typealias Email = String 8 | typealias Password = String 9 | 10 | extension Email { 11 | func isValid() -> Bool { 12 | // ... 13 | } 14 | } 15 | 16 | // ----------------------------------- 17 | 18 | struct User { 19 | // ... 20 | } 21 | 22 | typealias ApiHandler = (Result) -> Void 23 | func signin(email: Email, password: Password, onCompletion: @escaping ApiHandler) { 24 | // ... 25 | } 26 | 27 | // ----------------------------------- 28 | 29 | 30 | typealias TableViewConfiguration = UITableViewDataSource & UITableViewDelegate 31 | 32 | class ViewController: UIViewController, TableViewConfiguration { 33 | 34 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 35 | // ... 36 | } 37 | 38 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 39 | // ... 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Utilities 2 | A list with some of the Swift Utilities that I use in my work day as an iOS developer 3 | 4 | I have separate into 3 categories: UIKit, SwiftUI and Common (for those that you can use with UIKit and SwiftUI as well) 5 | 6 | ### Common 7 | - [Fonts](https://github.com/blorenzo10/SwiftUtilities/blob/master/Common%20/Fonts.swift "Fonts") 8 | - [Array](https://github.com/blorenzo10/swift-utilities/blob/master/Common%20/Array.swift) 9 | - [Typealias](https://github.com/blorenzo10/swift-utilities/blob/master/Common%20/Typealias.swift) 10 | - [Factory Pattern](https://github.com/blorenzo10/swift-utilities/blob/master/Common%20/FactoryPattern.swift) 11 | - [Dates Formatter](https://github.com/blorenzo10/swift-utilities/blob/master/Common%20/DateFormatter.swift) 12 | 13 | ### SwiftUI 14 | - [Separate views setup into variables](https://github.com/blorenzo10/swift-utilities/blob/master/SwiftUI/Views.swift) 15 | ### UIKit 16 | - [Constraints](https://github.com/blorenzo10/SwiftUtilities/blob/master/UIKit/Constraints.swift "Constraints") 17 | -------------------------------------------------------------------------------- /SwiftUI/Views.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct SigninView: View { 4 | 5 | @State var userEmail = "" 6 | @State var userPassword = "" 7 | 8 | var body: some View { 9 | ZStack { 10 | Color.black 11 | .edgesIgnoringSafeArea(.all) 12 | 13 | ScrollView { 14 | signinForm 15 | signinFooter 16 | } 17 | } 18 | } 19 | } 20 | 21 | // MARK: - Setup Views 22 | 23 | extension SigninView { 24 | 25 | var signinForm: some View { 26 | VStack { 27 | TextField("email", text: $userEmail) 28 | SecureField("password", text: $userPassword) 29 | } 30 | } 31 | 32 | var signinFooter: some View { 33 | Button(action: { 34 | print("Sign in tapped") 35 | }, label: { 36 | Text("Sign in!") 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /UIKit/Constraints.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIView { 4 | 5 | private func setAutoresizingMaskIfNeeded() { 6 | if translatesAutoresizingMaskIntoConstraints { 7 | translatesAutoresizingMaskIntoConstraints = false 8 | } 9 | } 10 | 11 | func setSize(width: CGFloat? = nil, height: CGFloat? = nil) { 12 | setAutoresizingMaskIfNeeded() 13 | 14 | if let width = width { 15 | widthAnchor.constraint(equalToConstant: width).isActive = true 16 | } 17 | 18 | if let height = height { 19 | heightAnchor.constraint(equalToConstant: height).isActive = true 20 | } 21 | } 22 | 23 | func setSizeEqual(to view: UIView, factor: CGFloat = 1.0) { 24 | setAutoresizingMaskIfNeeded() 25 | widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: factor).isActive = true 26 | heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: factor).isActive = true 27 | } 28 | 29 | func centerView(into view: UIView) { 30 | setAutoresizingMaskIfNeeded() 31 | centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 32 | centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 33 | } 34 | 35 | func setConstraint(top: NSLayoutYAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil, leading: NSLayoutXAxisAnchor? = nil, trailing: NSLayoutXAxisAnchor? = nil, padding: UIEdgeInsets = .zero) { 36 | setAutoresizingMaskIfNeeded() 37 | 38 | if let topConstraint = top { 39 | topAnchor.constraint(equalTo: topConstraint, constant: padding.top).isActive = true 40 | } 41 | 42 | if let bottomConstraint = bottom { 43 | bottomAnchor.constraint(equalTo: bottomConstraint, constant: padding.bottom).isActive = true 44 | } 45 | 46 | if let leadingConstraint = leading { 47 | leadingAnchor.constraint(equalTo: leadingConstraint, constant: padding.left).isActive = true 48 | } 49 | 50 | if let trailingConstaint = trailing { 51 | trailingAnchor.constraint(equalTo: trailingConstaint, constant: padding.right).isActive = true 52 | } 53 | } 54 | } 55 | 56 | class ViewController: UIViewController { 57 | 58 | override func viewDidLoad() { 59 | super.viewDidLoad() 60 | 61 | let childView = UIView() 62 | view.addSubview(childView) 63 | 64 | childView.setConstraint(top: view.topAnchor, leading: view.leadingAnchor, trailing: view.trailingAnchor, padding: .init(top: 10, left: 5, bottom: 0, right: 5)) 65 | childView.setSize(height: 50) 66 | } 67 | } 68 | --------------------------------------------------------------------------------