20 Essential Swift Extensions Every iOS Developer Must Know
If you frequently work with UIKit and Swift, you definitely understand why extensions are incredibly useful in iOS development! Extensions allow us to add functionality to existing data types without having to modify the original code.
In this article, we'll explore 20 powerful Swift extensions that can help simplify the development process, reduce repetitive code, and make your iOS applications look better and easier to maintain.
Part 1: UIView Extensions - Making UI Development More Efficient
1. Adding Multiple Subviews at Once
How many times have you written multiple addSubview
calls one after another? With this simple extension, you can add multiple subviews in just one function call!
extension UIView {
func addSubviews(_ views: UIView...) {
views.forEach { addSubview($0) }
}
}
view.addSubviews(titleLabel, imageView, descriptionLabel, actionButton)
- Shorter and more concise code
- Reduces repetitive
.addSubview
calls - Easier to read and maintain
2. Pinning a View to its Superview
Setting up constraints to pin a view to its superview's edges is something we do often. This extension makes it easier with customizable insets.
extension UIView {
func pinToSuperview(top: CGFloat = 0, left: CGFloat = 0,
bottom: CGFloat = 0, right: CGFloat = 0) {
guard let superview = superview else {
print("Error: superview does not exist")
return
}
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
topAnchor.constraint(equalTo: superview.topAnchor, constant: top),
leftAnchor.constraint(equalTo: superview.leftAnchor, constant: left),
bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: -bottom),
rightAnchor.constraint(equalTo: superview.rightAnchor, constant: -right)
])
}
}
view.pinToSuperview(top: 16, left: 16, bottom: 16, right: 16)
3. Animation UIView
Extension to create UIView animations easily, including fade in and fade out effects.
extension UIView {
/// Chain multiple animations with completion handler
func animate(
duration: TimeInterval,
delay: TimeInterval = 0,
options: UIView.AnimationOptions = [],
animations: @escaping (UIView) -> Void,
completion: ((Bool) -> Void)? = nil
) {
UIView.animate(withDuration: duration, delay: delay,
options: options, animations: {
animations(self)
}, completion: completion)
}
/// Fade in view with customizable parameters
func fadeIn(duration: TimeInterval = 0.3, delay: TimeInterval = 0,
completion: ((Bool) -> Void)? = nil) {
alpha = 0
isHidden = false
UIView.animate(withDuration: duration, delay: delay,
options: .curveEaseInOut, animations: {
self.alpha = 1
}, completion: completion)
}
/// Fade out view with customizable parameters
func fadeOut(duration: TimeInterval = 0.3, delay: TimeInterval = 0,
completion: ((Bool) -> Void)? = nil) {
UIView.animate(withDuration: duration, delay: delay,
options: .curveEaseInOut, animations: {
self.alpha = 0
}, completion: { [weak self] finished in
self?.isHidden = true
completion?(finished)
})
}
}
// Multiple animations
view.animate(duration: 1.0) { view in
view.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}
// Fade out animation
view.fadeOut()
// Fade in animation
view.fadeIn()
4. Shake Animation for Error Feedback
A shake animation is a great way to provide visual feedback when validation fails.
extension UIView {
func shake(duration: TimeInterval = 0.5, repeatCount: Float = 2) {
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
animation.timingFunction = CAMediaTimingFunction(name: .linear)
animation.duration = duration
animation.values = [-10, 10, -8, 8, -5, 5, -3, 3, 0]
animation.repeatCount = repeatCount
layer.add(animation, forKey: "shake")
}
}
if passwordTextField.text?.isEmpty ?? true {
passwordTextField.shake()
showError("Password cannot be empty")
}
5. Pulse Animation for Highlighting Elements
Need to highlight a specific UI element? This pulse animation is exactly what you need.
extension UIView {
func pulse(duration: TimeInterval = 0.5, scale: CGFloat = 1.1) {
UIView.animate(withDuration: duration / 2, animations: {
self.transform = CGAffineTransform(scaleX: scale, y: scale)
}, completion: { _ in
UIView.animate(withDuration: duration / 2) {
self.transform = CGAffineTransform.identity
}
})
}
}
submitButton.pulse()
6. Rounded Corners for Specific Edges
Sometimes, you might want to round only specific corners of a view.
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(
roundedRect: bounds,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius)
)
let mask = CAShapeLayer()
mask.path = path.cgPath
layer.mask = mask
}
}
cardView.roundCorners(corners: [.topLeft, .topRight], radius: 12)
7. Adding Custom Shadows
Shadows can add depth and dimension to your UI, but setting them up often requires a lot of code.
extension UIView {
func addShadow(
color: UIColor = .black,
opacity: Float = 0.3,
offset: CGSize = CGSize(width: 0, height: 2),
radius: CGFloat = 4
) {
layer.shadowColor = color.cgColor
layer.shadowOpacity = opacity
layer.shadowOffset = offset
layer.shadowRadius = radius
layer.masksToBounds = false
}
}
profileCard.addShadow(color: .darkGray, opacity: 0.2, radius: 8)
8. Easy Gradient Backgrounds
A gradient background can make your app look more visually appealing, and this extension makes it easy to implement.
extension UIView {
func addGradient(
colors: [UIColor],
startPoint: CGPoint = CGPoint(x: 0.5, y: 0),
endPoint: CGPoint = CGPoint(x: 0.5, y: 1)
) -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = colors.map { $0.cgColor }
gradientLayer.startPoint = startPoint
gradientLayer.endPoint = endPoint
layer.insertSublayer(gradientLayer, at: 0)
return gradientLayer
}
}
let gradient = headerView.addGradient(
colors: [.systemBlue, .systemIndigo],
startPoint: CGPoint(x: 0, y: 0),
endPoint: CGPoint(x: 1, y: 1)
)
9. Converting Views to Images
Need to capture a view as an image for sharing or further processing? This extension makes it simple.
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
let chartImage = chartView.asImage()
// Now you can share this image via UIActivityViewController
10. Making Any View Touchable
This extension allows any view to respond to touch events using a simple closure-based approach.
extension UIView {
func makeTouchable(action: @escaping () -> Void) {
self.isUserInteractionEnabled = true
let tap = TapGestureRecognizer(action: action)
tap.numberOfTapsRequired = 1
self.addGestureRecognizer(tap)
}
private class TapGestureRecognizer: UITapGestureRecognizer {
private var action: () -> Void
init(action: @escaping () -> Void) {
self.action = action
super.init(target: nil, action: nil)
self.addTarget(self, action: #selector(self.execute))
}
@objc private func execute() {
self.action()
}
}
}
infoIcon.makeTouchable {
self.showInfoAlert()
}
Part 2: Swift Extensions - Productive Development Tools
11. Extension UICollectionView Setup
When working with UICollectionView, managing cells can become repetitive. This extension simplifies cell registration and dequeuing.
import UIKit
extension UICollectionView {
func registerCellType(_ cellClass: T.Type) {
let identifier = "\(cellClass)"
let nib = UINib(nibName: identifier, bundle: Bundle(for: cellClass))
register(nib, forCellWithReuseIdentifier: identifier)
}
func dequeueReusableCell(_ cellClass: T.Type,
for indexPath: IndexPath) -> T {
let identifier = "\(cellClass)"
guard let cell = dequeueReusableCell(withReuseIdentifier: identifier,
for: indexPath) as? T else {
fatalError("Error dequeueing cell")
}
return cell
}
}
// Setup
private func setupView() {
collectionView.dataSource = self
collectionView.delegate = self
collectionView.registerCellType(ScheduleTimeCollectionViewCell.self)
}
// DataSource
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
ScheduleTimeCollectionViewCell.self,
for: indexPath
)
return cell
}
}
12. Extension UITableView Setup
Similar to UICollectionView, this extension simplifies UITableView setup.
import UIKit
extension UITableView {
func registerNib(_ cell: T.Type) {
let identifier = "\(cell)"
register(UINib(nibName: String(describing: cell), bundle: nil),
forCellReuseIdentifier: identifier)
}
func dequeueReusableCell(_ cell: T.Type,
for indexPath: IndexPath) -> T {
let identifier = "\(cell)"
guard let cell = dequeueReusableCell(withIdentifier: identifier,
for: indexPath) as? T else {
fatalError("Error dequeueing cell")
}
return cell
}
}
private func setupTableView() {
tableView.dataSource = self
tableView.delegate = self
tableView.registerNib(StudentTopDetailCell.self)
tableView.registerNib(StudentMidDetailCell.self)
tableView.registerNib(StudentBottomDetailCell.self)
tableView.separatorStyle = .none
}
13. Extension String Validation
Ensuring user input is valid is crucial. This extension adds validation capabilities directly to the String type.
extension String {
var isValidEmail: Bool {
let regularExpression = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let predicate = NSPredicate(format: "SELF MATCHES %@", regularExpression)
return predicate.evaluate(with: self)
}
var isValidPhone: Bool {
let regularExpression = "0[0-9]*"
let predicate = NSPredicate(format: "SELF MATCHES %@", regularExpression)
return predicate.evaluate(with: self)
}
var isValidPassword: Bool {
let regularExpression = "(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,}"
let predicate = NSPredicate(format: "SELF MATCHES %@", regularExpression)
return predicate.evaluate(with: self)
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
switch type {
case .email:
if let text = textField.text, text.isEmpty || text.isValidEmail {
resetError()
} else {
setError("Email is not valid")
}
case .password:
if let text = textField.text, text.isEmpty || text.isValidPassword {
resetError()
} else {
setError("Password must contain uppercase, lowercase, number and special character")
}
}
}
14. Extension Default String
Managing common string values like empty strings, spaces, and symbols becomes more consistent.
extension String {
static var empty: String {
return SC.empty
}
static var space: String {
return SC.space
}
static var dash: String {
return SC.dash
}
}
typealias SC = StringConstants
enum StringConstants {
static let empty = ""
static let space = " "
static let dash = "-"
}
// Empty variable
private(set) var id: String = .empty
// Optional handling
func set(student: ScheduleStudents) {
self.id = student.id ?? .empty
}
// Spacing
totalStudentsLabel.text = total + SC.space + studentText
// Placeholder
field?.placeholder = .dash
15. Extension UIApplication Top Controller
Extension to find the topmost view controller that is currently active.
extension UIApplication {
class func topViewController(_ viewController: UIViewController? =
UIApplication.shared.currentActiveWindow?.rootViewController)
-> UIViewController? {
if let nav = viewController as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = viewController as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = viewController?.presentedViewController {
return topViewController(presented)
}
return viewController
}
var currentActiveWindow: UIWindow? {
return UIApplication.shared.connectedScenes.flatMap {
($0 as? UIWindowScene)?.windows ?? []
}.first { $0.isKeyWindow }
}
}
private func presentPopup(message: String) {
let popupViewController = PopoverToastViewController(message: message)
popupViewController.modalPresentationStyle = .overCurrentContext
UIApplication.topViewController()?.present(popupViewController, animated: true)
}
16. Extension Date Manipulation
Extension for easy Date manipulation.
extension Date {
func dayOfWeek() -> Int {
let calendar = Calendar.current
let components = calendar.dateComponents([.weekday], from: self)
return components.weekday!
}
func addMonth(n: Int) -> Date {
let cal = NSCalendar.current
return cal.date(byAdding: .month, value: n, to: self) ?? Date()
}
func addDay(n: Int) -> Date {
let cal = NSCalendar.current
return cal.date(byAdding: .day, value: n, to: self) ?? Date()
}
func addSec(n: Int) -> Date {
let cal = NSCalendar.current
return cal.date(byAdding: .second, value: n, to: self) ?? Date()
}
}
// Day of week
let day = Date()
print(day.dayOfWeek()) // Thursday = 5
// Add month
@IBAction func nextButtonDidClick(_: UIButton) {
baseDate = baseDate.addMonth(n: 1)
monthLabel.text = dateFormatter.string(from: baseDate)
}
// Add seconds
let now = Date()
let futureDate = now.addSec(n: 120) // Add 2 minutes
17. Extension UIImage Base64
Convert images to Base64 strings for upload or API integrations.
extension UIImage {
enum Format: String {
case png
case jpeg
}
func toBase64(type: Format = .jpeg, quality: CGFloat = 1,
addMimePrefix: Bool = false) -> String? {
let imageData: Data?
switch type {
case .jpeg: imageData = jpegData(compressionQuality: quality)
case .png: imageData = pngData()
}
guard let data = imageData else { return nil }
let base64 = data.base64EncodedString()
var result = base64.replacingOccurrences(of: "\"", with: "",
options: NSString.CompareOptions.literal,
range: nil)
if addMimePrefix {
let prefix = "data:image/\(type.rawValue);base64,"
result = prefix + base64
}
return result
}
}
func imageUploadButtonDidClick(image: UIImage?) {
guard let image = image,
let imageString = image.toBase64(addMimePrefix: true) else { return }
// Upload imageString to server
}
18. Extension UITextField Add Done Button
Add a "Done" button to the keyboard for easy dismissal.
extension UITextField {
func addDoneButton() {
let doneToolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 50))
doneToolbar.barStyle = UIBarStyle.default
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil, action: nil)
let done = UIBarButtonItem(title: "Done", style: .done,
target: self,
action: #selector(self.doneButtonAction))
var items = [UIBarButtonItem]()
items.append(flexSpace)
items.append(done)
doneToolbar.items = items
doneToolbar.sizeToFit()
self.inputAccessoryView = doneToolbar
}
@objc func doneButtonAction() {
self.endEditing(true)
}
}
lazy var textField: UITextField = {
let textField = UITextField()
textField.addDoneButton() // Add done button
textField.backgroundColor = .blue
return textField
}()
19. Extension Converting Type
Simplify type conversions between Int, Double, and String.
extension Int {
func toString() -> String {
return String(describing: self)
}
}
extension Double {
func toString() -> String {
return String(describing: self)
}
}
extension String {
func toInt() -> Int {
return Int(self) ?? .zero
}
}
// Int to String
let number = 42
let numberAsString = number.toString()
// Double to String
let pi = 3.14159
let piAsString = pi.toString()
// String to Int
let validNumber = "123"
let convertedNumber = validNumber.toInt() // 123
20. Extension UIScrollView (To Top and Bottom)
Scroll to top or bottom programmatically with ease.
public extension UIScrollView {
func scrollToBottom(animated: Bool, additionalBottomPadding: CGFloat = 0) {
let bottomOffset = CGPoint(x: 0,
y: contentSize.height - bounds.size.height +
contentInset.bottom + additionalBottomPadding)
setContentOffset(bottomOffset, animated: animated)
}
func scrollToTop() {
let desiredOffset = CGPoint(x: 0, y: -contentInset.top)
setContentOffset(desiredOffset, animated: true)
}
}
// Scroll to top
var currentPage: Int = 1 {
didSet {
guard currentPage == 1 else { return }
collectionView.scrollToTop()
}
}
// Scroll to bottom when keyboard shows
@objc func keyboardWillShow(notification: NSNotification) {
let keyboardHeight = tableView.getKeyboardHeight(notification: notification)
tableView.scrollToBottom(animated: false,
additionalBottomPadding: keyboardHeight)
}
Conclusion
Swift extensions are incredibly powerful tools for making your code cleaner, more reusable, and maintainable. By using the 20 extensions we've discussed above, you can:
- Reduce repetitive code - Write once, use multiple times
- Improve readability - Code is easier to read and understand
- Speed up development - Focus on business logic, not boilerplate code
- Reduce bugs - Centralized logic reduces the chance of errors
These extensions have proven helpful in various iOS projects, from small apps to enterprise-level applications. Start integrating these extensions into your projects and experience the difference!
Happy Coding! 🚀
Pro Tip: Store these extensions in separate files based on category (e.g., UIView+Extensions.swift
, String+Extensions.swift
) for better organization.
No comments: