Can I apply the same extension to many classes without copy paste? - swift

Let's say I have this extension for CGFloat
extension CGFloat {
// common
public var thrice: CGFloat { return self * 3.0 }
public var twice: CGFloat { return self * 2.0 }
public var half: CGFloat { return self * 0.5 }
public var third: CGFloat { return self / 3.0 }
public var fourth: CGFloat { return self * 0.25 }
public var sixth: CGFloat { return self / 6.0 }
public var eighth: CGFloat { return self * 0.125 }
public var twelfth: CGFloat { return self / 12.0 }
public var sixteenth: CGFloat { return self * 0.0625 }
//
public var inverse: CGFloat { return 1.0 / self }
}
What I want to do is have these apply to CGFloat, Double and Float without having to copy/paste the code. Is this at all possible?
Thanks!

You cannot do it by extending the types (all at once), but you could do it using templated functions:
protocol FloatLiteralMultipliable: FloatLiteralConvertible {
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
}
extension Float: FloatLiteralMultipliable {}
extension CGFloat: FloatLiteralMultipliable {}
extension Double: FloatLiteralMultipliable {}
func thrice<T: FloatLiteralMultipliable>(value: T) -> T { return value * 3.0 }
func twice<T: FloatLiteralMultipliable>(value: T) -> T { return value * 2.0 }
func half<T: FloatLiteralMultipliable>(value: T) -> T { return value / 2.0 }
func third<T: FloatLiteralMultipliable>(value: T) -> T { return value / 3.0 }
func fourth<T: FloatLiteralMultipliable>(value: T) -> T { return value / 4.0 }
func sixth<T: FloatLiteralMultipliable>(value: T) -> T { return value / 6.0 }
func eighth<T: FloatLiteralMultipliable>(value: T) -> T { return value * 0.125 }
func twelfth<T: FloatLiteralMultipliable>(value: T) -> T { return value / 12.0 }
func sixteenth<T: FloatLiteralMultipliable>(value: T) -> T { return value / 0.0625 }
func inverse<T: FloatLiteralMultipliable>(value: T) -> T { return 1.0 / value }
thrice(Float(10.0)) // 30.0
half(CGFloat(2)) // 1.0
inverse(Double(1.0/10.0)) // 10.0
Note: I am only explicitly constructing the types in my example calls to prove that it the functions can be used with each of the types

Related

Swift: calculating atan2(y,x) for FloatingPoint numbers?

If the only thing I know about the variables x, y is that they are of type T which is conforming to the FloatingPoint protocol, how can I calculate the value of atan2(y, x) and return it as an instance of type T?
Actually, I was trying to implement a Vector2D protocol, but I met problems with the following methods/properties:
.init(radius r:Component, angle a:Component)
.angle
The following is my implementation:
/*
Swift Tip: Vector Algebra with Protocols
https://www.objc.io/blog/2018/11/29/vector-calculus-with-protocols/
FloatingPoint
https://developer.apple.com/documentation/swift/floatingpoint
*/
import Foundation // cos, sin
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: FloatingPoint
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral a: Component...) {
assert(a.count >= 2, "Must initialize vector with at least 2 values.")
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r:Component, angle a:Component) {
let r = r as! Double // ⛔️doesn't work‼️
let a = a as! Double
let x = r * cos(a) as! Component
let y = r * sin(a) as! Component
self.init(x: x, y: y)
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { // in radians
atan2(y as! Double, x as! Double) as! Component // ⛔️doesn't work‼️
}
public var degrees: Component { angle / .deg } // in degrees
}
My workaround: (including 2 files - Vector2D.swift and CGPoint+Vector2D.swift)
Vector2D.swift
import Foundation
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
// protocol for Vector2D.Component
public protocol VectorComponent: FloatingPoint {
static func cos(_ x: Self) -> Self
static func sin(_ x: Self) -> Self
static func atan2(_ y: Self, _ x: Self) -> Self
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: VectorComponent
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral elements: Component...) {
let a = elements + [0, 0] // make sure that a.count >= 2
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r: Component, angle a: Component) {
self.init(x: r * Component.cos(a), y: r * Component.sin(a))
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { Component.atan2(y, x) } // in radians
public var degrees: Component { angle / .deg } // in degrees
}
// vector functions
public func abs<T: Vector2D>(_ v: T) -> T.Component {
v.length
}
CGPoint+Vector2D.swift
import CoreGraphics // for CGPoint, cos, sin, atan2
// CGFloat (protocol conformance)
extension CGFloat: VectorComponent {
public static func cos(_ x: CGFloat) -> CGFloat { CoreGraphics.cos(x) }
public static func sin(_ x: CGFloat) -> CGFloat { CoreGraphics.sin(x) }
public static func atan2(_ y: CGFloat, _ x: CGFloat) -> CGFloat { CoreGraphics.atan2(y,x) }
}
// CGPoint (protocol conformance)
extension CGPoint: Vector2D {}

Getting a base value into ValueTransformer

I'm trying to output a value relative to a base value that is calculated elsewhere. I was hoping that ValueTransformer would do the trick but I have no idea how to get a base value into the ValueTransformer.The initWithBase func shown below does not work. Any advice would be welcome. Thanks.
import Cocoa
class PercentTransformer: ValueTransformer {
var baseValue: Double = 1
override class func transformedValueClass() -> Swift.AnyClass {
return NSString.self
}
override class func allowsReverseTransformation() -> Bool {
return false
}
func initWithBase(base: Double) {
baseValue = base
return
}
override func transformedValue(_ value: Any?) -> Any? {
let inputValue = value as! Double
if inputValue == 0.0 {
return "--------------"
}
// calculate output value
let percent = inputValue/baseValue * 100
let output = String(format: "%.2e", inputValue) + " " + String(format: "%.1f", percent) + "%"
return output as NSString
}
}

how to write an extension for operator?

For example, write an extension for operator ">" to compare CGSize
let sizeA = CGSize(...)
let sizeB = CGSize(...)
sizeA > sizeB // How to let operator ">" support CGSize
You can extend CGSize to conform to Comparable protocol. #MartinR got the point. I updated my answer to compare the size as area comparison.
extension CGSize {
public static func <(lhs: CGSize, rhs: CGSize) -> Bool {
return (lhs.width * lhs.height) < (rhs.width * rhs.height)
}
public static func >(lhs: CGSize, rhs: CGSize) -> Bool {
return (lhs.width * lhs.height) > (rhs.width * rhs.height)
}
}
Simply define the > function for CGSize:
extension CGSize {
static func > (_ lhs: CGSize, _ rhs: CGSize) -> Bool {
// Implement as needed
return lhs.width > rhs.width && lhs.height > rhs.height
}
}
Just to overload the operator >
extension CGSize{
static func >(_ left: CGSize, _ right: CGSize)->Bool{
return left.width*left.height > right.width*right.height
}
}

How to write a generic function for floating point values in swift

I would like to call floating point methods on floating point types in swift.
func example<T : FloatingPoint>(_ x:T) -> T {
return cos(cbrt(x + 1))
}
Is there a better way to do so than this?
protocol SupportsFloatingPoint : FloatingPoint {
var cubeRoot:Self { get }
var cosine:Self { get }
}
extension Double : SupportsFloatingPoint {
var cubeRoot:Double { return Darwin.cbrt(self) }
var cosine:Double { return Darwin.cos(self) }
}
extension Float : SupportsFloatingPoint {
var cubeRoot:Float { return Darwin.cbrt(self) }
var cosine:Float { return Darwin.cos(self) }
}
func cbrt<T : SupportsFloatingPoint>(_ x:T) -> T {
return x.cubeRoot
}
func cos<T : SupportsFloatingPoint>(_ x:T) -> T {
return x.cosine
}
func example<T : SupportsFloatingPoint>(_ x:T) -> T {
return cos(cbrt(x - 1))
}
Note that the addition operator is lost here. You can do -, * and / but not + on SupportsFloatingPoint types.
Generic Math(s) Functions were proposed in SE-0246, and accepted with modifications. The implementation is currently blocked by some other factors.
In the meanwhile, you can access the same functionality from the Numerics library hosted on Apple's own GitHub page, and distributed with the Swift Package Manager.
Original Answer
You don't need a new protocol. You can extend the existing FloatingPoint protocol for supported types:
// Just for fun :)
prefix operator √
prefix operator ∛
extension FloatingPoint where Self == Double {
var squareRoot: Self { return sqrt(self) }
var cubeRoot: Self { return cbrt(self) }
var sine: Self { return sin(self) }
var cosine: Self { return cos(self) }
static prefix func √(_ x: Self) -> Self { return x.squareRoot }
static prefix func ∛(_ x: Self) -> Self { return x.cubeRoot }
}
extension FloatingPoint where Self == Float {
var squareRoot: Self { return sqrt(self) }
var cubeRoot: Self { return cbrt(self) }
var sine: Self { return sin(self) }
var cosine: Self { return cos(self) }
static prefix func √(_ x: Self) -> Self { return x.squareRoot }
static prefix func ∛(_ x: Self) -> Self { return x.cubeRoot }
}
print(Double.pi.cosine)
print(√25.0)
print(∛8.0)
Admittedly, there's a lot of code duplication, which I'm currently investigating how to minimize. On the bright side, at least this is all static, inlineable code that'll produce very fast machine code.

Switch Generic Numeric Function with Protocol

This is working great for Doubles and Floats. But getting other numerals like Ints involved is proving really difficult.
public protocol TemperatureConvertable: FloatLiteralConvertible, CustomStringConvertible {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
}
extension Double: TemperatureConvertable {}
extension Float: TemperatureConvertable {}
public func fahrenheitToCelsius<T: TemperatureConvertable>(fahrenheit: T) -> T {
// (°F − 32) ÷ 1.8 =°C
return (fahrenheit - 32.0) / 1.8
}
public func celsiusToFahrenheit<T: TemperatureConvertable>(celsius: T) -> T {
// (°C × 1.8) + 32 =°F
return (celsius * 1.8) + 32.0
}
From what I can find there isn't a NumberLiteralConvertible or similar. And just creating a Numeric protocol like others suggest makes the arithmetic fail because the types don't match. Ideally I'd like something like this thats more generic but this I get Binary operator '-' cannot be applied to operands of type 'T' and 'Double' on the line of actual math for fahrenheit - 32.0 because the lhs and rhs aren't both Self.
public protocol TemperatureConvertable: CustomStringConvertible {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
}
extension Int: TemperatureConvertable {}
extension Double: TemperatureConvertable {}
extension Float: TemperatureConvertable {}
public func fahrenheitToCelsius<T: TemperatureConvertable>(fahrenheit: T) -> T {
// (°F − 32) ÷ 1.8 =°C
return (fahrenheit - 32.0) / 1.8
}
public func celsiusToFahrenheit<T: TemperatureConvertable>(celsius: T) -> T {
// (°C × 1.8) + 32 =°F
return (celsius * 1.8) + 32.0
}
You need a way to create a TemperatureConvertable from a Double and viceversa.
So your functions will be able to internally use Double(s) to make the operations. And finally convert the result to T for the output.
Step 1
public protocol TemperatureConvertible: CustomStringConvertible {
// this part is not needed
// func +(lhs: Self, rhs: Self) -> Self
// func -(lhs: Self, rhs: Self) -> Self
// func *(lhs: Self, rhs: Self) -> Self
// func /(lhs: Self, rhs: Self) -> Self
init(_ other: Double)
var double: Double { get }
}
Now lets update your extensions to Double, Float and Int.
You don't need to add the initializer we declared above since these types already have it.
extension Int: TemperatureConvertible {
public var double: Double { return Double(self) }
}
extension Double: TemperatureConvertible {
public var double: Double { return self }
}
extension Float: TemperatureConvertible {
public var double: Double { return Double(self) }
}
Step 2
Now you can rewrite your functions like this
public func fahrenheitToCelsius<T: TemperatureConvertible>(fahrenheit: T) -> T {
// (°F − 32) ÷ 1.8 =°C
let celsius = (fahrenheit.double - 32.0) / 1.8
return T(celsius)
}
public func celsiusToFahrenheit<T: TemperatureConvertible>(celsius: T) -> T {
// (°C × 1.8) + 32 =°F
let fahrenheit = (celsius.double * 1.8) + 32.0
return T(fahrenheit)
}
Test
let celsius: Int = 20
let fahrenheit = celsiusToFahrenheit(celsius)
// ^ it's an Int
Tip
For readability and consistency with the Cocoa framework I suggest you to rename your functions like this
public func fahrenheitFromCelsius<T: TemperatureConvertible>(_ celsius: T) -> T
public func celsiusFromFahrenheit<T: TemperatureConvertible>(_ fahrenheit: T) -> T