Improve conveniences (methods) in frameworks and outdated semantics (UI, CA, CG...)

The core libraries often show a lack of convenience methods that could make our code much tidier and compact. This also applies to outdated semantics such as UIColor.withAlphaComponent(_:) instead of UIColor.with(alpha:)

Here are just some of the examples of conveniences, that in my opinion should be part of the frameworks rather than numerous extensions written by developers, and outdated semantics which should catch up with the language.

Conveniences


CoreGraphics

extension CGRect {

    init(size: CGSize) {
        self.init(origin: CGPoint.zero, size: size)
    }

    init(size: CGFloat) {
        self.init(size: CGSize(width: size, height: size))
    }
    
    init(width: CGFloat, height: CGFloat) {
        self.init(x: 0, y: 0, width: width, height: height)
    }
}
extension CGSize {
    
    init(value: CGFloat) {
        
        self.init(width: value, height: value)
    }

    static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
        
        return CGSize.init(width: lhs.width * rhs, height: lhs.height * rhs)
    }

    static func *(lhs: CGFloat, rhs: CGSize) -> CGSize {
        
        return rhs * lhs
    }
    
    static func *=(lhs: inout CGSize, rhs: CGFloat) {
        
        lhs = lhs * rhs
    }

Somewhat analogous conveniences for CGPoint with (maybe) some vector-like arithmetics.

extension CGAffineTransform {
    
    init(scale: CGFloat) {
        
        self.init(scaleX: scale, y: scale)
    }
    
    init(translation value: CGFloat) {
        
        self.init(translationX: value, y: value)
    }
    
    
    static func translation(x: CGFloat = 0, y: CGFloat = 0) -> CGAffineTransform {
        
        return self.init(translationX: x, y: y)
    }
    

    static func scale(x: CGFloat = 1, y: CGFloat = 1) -> CGAffineTransform {
        
        return self.init(scaleX: x, y: y)
    }
    
    
    func translated(by value: CGFloat) -> CGAffineTransform {
        
        return self.translatedBy(x: value, y: value)
    }

    func translated(x: CGFloat = 0, y: CGFloat = 0) -> CGAffineTransform {
        
        return self.translatedBy(x: x, y: y)
    }
    

    func scaled(by value: CGFloat) -> CGAffineTransform {
        
        return self.scaledBy(x: value, y: value)
    }
    
    func scaled(x: CGFloat = 1, y: CGFloat = 1) -> CGAffineTransform {
        
        return self.scaledBy(x: x, y: y)
    }
}

UIKit

extension NSLayoutAnchor {
    
    @objc internal func constraint(_ relation: NSLayoutRelation = .equal, to anchor: NSLayoutAnchor<AnchorType>, multiplier m: CGFloat = 1.0, constant c: CGFloat = 0.0) -> NSLayoutConstraint {
       
         ... // There is no function with a multiplier in NSLayoutAnchor.
             // The only way to apply one is to directly use NSLayoutConstraint, which is severely inconvenient.
    }
}
extension UIViewPropertyAnimator {

    // These are three initializers of UIViewPropertyAnimator

    init(duration: TimeInterval, controlPoint1: CGPoint, controlPoint2: CGPoint, animations: (() -> ())?)
    init(duration: TimeInterval, curve: UIViewAnimationCurve, animations: (() -> ())?)
    init(duration: TimeInterval, timing: CAMediaTimingFunction, animations: (() -> ())?)
    
    // Now the question:  Isn't this: 
    init(duration: TimeInterval, timing: CAMediaTimingFunction, animations: (() -> ())?)
    // enough? 
    // (Especially if CAMediaTimingFunction is modified with some conveniences or even 
    // becomes an enum (see post bottom))
    
    // Or at least get rid of the second one out of the three above?

    // More like a better option rather than a convenience 
}

SceneKit

SCNVector3 arithmetics.

Outdated Semantics


UIKit

extension UIColor {
        
    func with(alpha: CGFloat) -> UIColor {
        
        return self.withAlphaComponent(alpha)
    }
}

Core Animation

Why are all these current CATransform3D global?

extension CATransform3D: Equatable {
    
    public var identity: CATransform3D { return CATransform3DIdentity }
    
    public var isIdentity: Bool { return CATransform3DIsIdentity(self) }
    
    public var inverted: CATransform3D { return CATransform3DInvert(self) }
    
    public var affine: CGAffineTransform { return CATransform3DGetAffineTransform(self) }
    
    public var isAffine: Bool { return CATransform3DIsAffine(self) }
    

    static public func ==(lhs: CATransform3D, rhs: CATransform3D) -> Bool {
        
        return CATransform3DEqualToTransform(lhs, rhs)
    }

    
    public static func from(affine: CGAffineTransform) -> CATransform3D {
        
        return CATransform3DMakeAffineTransform(affine)
    }
    
    public static func translation(x: CGFloat, y: CGFloat, z: CGFloat) -> CATransform3D {
        
        return CATransform3DMakeTranslation(x, y, z)
    }

    public static func scale(x: CGFloat, y: CGFloat, z: CGFloat) -> CATransform3D {
        
        return CATransform3DMakeScale(x, y, z)
    }
    
    public static func rotation(_ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat) -> CATransform3D {
        
        return CATransform3DMakeRotation(angle, x, y, z)
    }
    
    public func translated(x: CGFloat, y: CGFloat, z: CGFloat) -> CATransform3D {
        
        return CATransform3DTranslate(self, x, y, z)
    }
    
    public func scaled(x: CGFloat, y: CGFloat, z: CGFloat) -> CATransform3D {
        
        return CATransform3DScale(self, x, y, z)
    }
    
    public func rotated(_ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat) -> CATransform3D {
        
        return CATransform3DRotate(self, angle, x, y, z)
    }
    
    public func concatenated(with: CATransform3D) -> CATransform3D {
        
        return CATransform3DConcat(self, with)
    }
}
extension CAMediaTimingFunction {
    
    static var linear: CAMediaTimingFunction {
        return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    }
    static var easeIn: CAMediaTimingFunction {
        return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
    }
    static var easeOut: CAMediaTimingFunction {
        return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
    }
    static var easeInEaseOut: CAMediaTimingFunction {
        return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    }
}

Maybe CAMediaTimingFunction at its current state should be reconsidered and become an enum?

enum CAMediaTimingFunctionm {
    
    case linear
    case easeIn
    case easeOut
    case easeInOut
    
    case custom(Float, Float, Float, Float)
    
    ...
}

Concering Foundation , String seems to revert to being a collection of characters again, so I will omit obvious subscripts that spare us from using self[self.index(startIndex, offsetBy: i)]. That is the Standard Library, however.

P.S.
Methods with default values for all parameters can be split into several methods in case they are found confusing when called without arguments

4 Likes

Hi @anthonylatsis,

The best place for requests on the Darwin platform SDKs is in (Radar. That gets it straight to the teams that maintain these APIs and can hear your suggestions. If you follow up here with the radar numbers I can make sure they get to the right people. I would suggest one for each framework (CG, UIKit, CA, etc.).

@Tony_Parker thank you for the advice. Will follow up with the numbers as soon as I submit the suggestions.