Swift extension example
Creating an extension
Add a new swift file with File > New > File... > iOS > Source > Swift File. You can call it what you want.
The general naming convention is to call it TypeName+NewFunctionality.swift.
Example 1 - Double
Double+Conversions.swift
import Swift // or Foundation
extension Double {
func celsiusToFahrenheit() -> Double {
return self * 9 / 5 + 32
}
func fahrenheitToCelsius() -> Double {
return (self - 32) * 5 / 9
}
}
Usage:
let boilingPointCelsius = 100.0
let boilingPointFarenheit = boilingPointCelsius.celsiusToFahrenheit()
print(boilingPointFarenheit) // 212.0
Example 2 - String
String+Shortcuts.swift
import Swift // or Foundation
extension String {
func replace(target: String, withString: String) -> String {
return self.replacingOccurrences(of: target, with: withString)
}
}
Usage:
let newString = "the old bike".replace(target: "old", withString: "new")
print(newString) // "the new bike"
Here are some more common String
extensions.
Example 3 - UIColor
UIColor+CustomColor.swift
import UIKit
extension UIColor {
class var customGreen: UIColor {
let darkGreen = 0x008110
return UIColor.rgb(fromHex: darkGreen)
}
class func rgb(fromHex: Int) -> UIColor {
let red = CGFloat((fromHex & 0xFF0000) >> 16) / 0xFF
let green = CGFloat((fromHex & 0x00FF00) >> 8) / 0xFF
let blue = CGFloat(fromHex & 0x0000FF) / 0xFF
let alpha = CGFloat(1.0)
return UIColor(red: red, green: green, blue: blue, alpha: alpha)
}
}
See here also.
Usage:
view.backgroundColor = UIColor.customGreen
Notes
- Once you define an extension it can be used anywhere in your app just like the built in class functions.
- If you are not sure of exactly what the function or property syntax should look like, you can Option+click a similar built in method. For example, when I Option+clicked
UIColor.greenColor
I see the declaration isclass func greenColor() -> UIColor
. That gives me a good clue for how to set up my custom method. - Apple Documentation for Extensions
- In Objective-C extensions are known as categories.
Swift extension for [String]?
You could follow the declaration of joinWithSeparator
(Cmd-click on it) and find that it is defined as an extension of the protocol SequenceType
instead of the type Array
.
// swift 2:
extension SequenceType where Generator.Element == String {
public func joinWithSeparator(separator: String) -> String
}
(Note: In Xcode 8 / Swift 3 if you Cmd-click on join(separator:)
you will land on Array
even if it is still implemented inside Sequence
, but that won't invalidate the idea below)
We could do the same with your function, where we extend a protocol adopted by Array instead of Array itself:
// swift 2:
extension CollectionType where
Generator.Element == String,
SubSequence.Generator.Element == String,
Index: BidirectionalIndexType
{
func joinWithCommas() -> String {
switch count {
case 0, 1, 2:
return joinWithSeparator(" or ")
default:
return dropLast(1).joinWithSeparator(", ") + ", or " + last!
}
}
}
// swift 3:
extension BidirectionalCollection where
Iterator.Element == String,
SubSequence.Iterator.Element == String
{
func joinWithCommas() -> String {
switch count {
case 0, 1, 2:
return joined(separator: " or ")
default:
return dropLast().joined(separator: ", ") + ", or " + last!
}
}
}
Note:
- we extend
CollectionType
to be able to usecount
- we constraint
Generator.Element == String
to usejoinWithSeparator
- we constraint
SubSequence.Generator.Element == String
to ensuredropLast(1)
can usejoinWithSeparator
.dropLast(1)
returns the associated typeSubSequence
. - we constraint
Index: BidirectionalIndexType
to uselast
.
Swift: How to add a class method in 'String extension
Class
and static
functions are not called on an instance of a class/struct, but on the class/struct itself, so you can't just append a string to a class.
Apple Documentation:
Within the body of a type method, the implicit self property refers to
the type itself, rather than an instance of that type.
You can, however, append a string to a variable instance of a String
using the mutating
keyword:
extension String {
mutating func aaa() {
self += "hello"
}
}
let foo = "a"
foo.aaa() // ERROR: Immutable value of type 'String' only has mutating members named 'aaa'
var bar = "b"
bar.aaa() // "bhello"
If you are trying to use a pointer to a string as a parameter, you can use the inout
keyword to alter the inputed string:
extension String {
static func aaa(inout path: String) {
path += "Hello"
}
}
var foo = "someText"
String.aaa(&foo)
foo //someTextHello
Call String extension including function written in Swift from Objective-C
A Swift class, extension, or protocol can only be represented in Objective-C if it's convertible or marked explicitly with @objc
.. which means it requires all parameters to be convertible to Objective-C as well.
struct
also cannot be converted so that leaves us with just classes alone (IE: extensions on classes and concrete classes) and to mark a class @objc
, it must inherit NSObject
.
Example:
@objc
extension NSString {
func asImage(withAttributes attributes: [NSAttributedString.Key: Any]? = nil, size: CGSize = .zero) -> UIImage? {
let size = size == .zero ? self.size(withAttributes: attributes!) : size
if #available(iOS 10.0, *) {
return UIGraphicsImageRenderer(size: size).image { _ in
self.draw(in: CGRect(origin: .zero, size: size),
withAttributes: attributes!)
}
} else {
// Fallback on earlier versions
return nil
}
}
}
Notice that I changed the CGSize
parameter to non-optional and compared against zero instead..
Also note that I marked it @objc
and changed it to an extension on NSString
because String
itself cannot be converted to Objective-C as it's a struct
!
Then in Objective-C you can call it like so:
[str asImageWithAttributes:@{....} size:CGSizeZero];
or whatever parameters you want..
How can I design a string type extension that represents a day of the week and returns the corresponding number? On Swift
You better create a enumeration where its rawValue is a string and add a computed property to return a value:
extension Calendar {
enum Weekday: String, CaseIterable, CustomStringConvertible {
case sunday, monday, tuesday, wednesday, thursday, friday, saturday
var description: String { rawValue.capitalized }
var value: Int { Self.allCases.firstIndex(where: {$0.rawValue == rawValue})! + 1 }
}
}
let monday = Calendar.Weekday.monday
print(monday) // Monday
print(monday.value) // 2
If you need to convert the string to a number you can initialize a new case and return its value:
extension String {
var weekday: Int? { Calendar.Weekday(rawValue: lowercased())?.value }
}
"monday".weekday // 2
"Monday".weekday // 2
"MONDAY".weekday // 2
How to add custom init for String extension?
Edit: Don't format currencies yourself.
However you might think currencies are formatted, you're almost certainly wrong. Just compare:
- US/Canada:
$3,490,000.89
- French Canadian:
3 490 000,89 $
- France:
3 490 000,89 €
- Germany:
3.490.000,89 €
Instead, use NumberFormatter
with numberStyle
set to .currency
, with a specified locale.
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
currencyFormatter.locale = Locale.current
let priceString = currencyFormatter.string(from: 9999.99)!
print(priceString) // Displays $9,999.99 in the US locale
Original answer:
The initializers (and mutating methods) of value types can simply assign directly to self
:
import Foundation
extension String {
init(_ amount: Double, decimalPlaces: UInt) {
let currencyAmount = String(format: "%\(decimalPlaces).f", amount)
let currencySign = NSLocalizedString("Defaults.CurrencySign", comment: "currency sign")
self = "\(currencySign)\(currencyAmount)"
}
}
let price = Double(155.15)
let formattedPrice = String(price, decimalPlaces: 2) // formattedPrice = "$155.15"
String extension for matching regular expression
.characters
are gone. You can use the string itself directly.
Change self.characters.indices
to self.indices
Change self.rangeOfString(expression, options: NSString.CompareOptions.RegularExpressionSearch, range: nil, locale: nil)
to self.range(of: expression, options: .regularExpression, range: nil, locale: nil)
And lastly, you can use NSRegularExpression
instead of recursively call the function, but note that it can throw
some errors, so you should handle it somehow. Use this extension:
extension String {
func stringsMatchingRegularExpression(expression regex: String) throws -> [String] {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: self,
range: NSRange(self.startIndex..., in: self))
return results.map {
String(self[Range($0.range, in: self)!])
}
}
}
- More Swifty-Style:
extension String {
func matching(expression regex: @autoclosure () throws -> NSRegularExpression) rethrows -> [String] {
let results = try regex().matches(in: self, range: NSRange(self.startIndex..., in: self))
return results.map {
String(self[Range($0.range, in: self)!])
}
}
func matching(pattern regexPattern: String) throws -> [String] {
return try self.matching(expression: NSRegularExpression(pattern: regexPattern))
}
}
How to add an optional string extension?
In Swift 3.1 you can add an extension to optional values as well:
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}
Related Topics
Convert String to Staticstring
API Violation - Multiple Calls Made to -[Xctestexpectation Fulfill]
How to Set the iOS13 Uisegmentedcontrol Backgroundcolor to White
Is .Playground a Swift File? Who Can 'See' It
Swift Cannot Infer Type from Context
Why Are Uiscreen.Bounds Incorrect in iOS11
How to Get Walking and Running Distance Using Healthkit in Swift
Realmswift Initialize List:Cannot Specialize a Non-Generic Definition
Swift Protocol as Generic Parameter
How to Remove Node from Parent If Touched More Than Once
Nspredicate in Query from Array Elements
Toolbar Is Deleting My Back Button in the Navigationview
Modifying an Array Passed as an Argument to a Function in Swift
Alamofire Returns Wrong Encoding