Search code examples
swiftpass-by-referenceinout

Swift inout parameter for const properties


Is it possible to use a let property with a similar function parameter to inout when I do't want to change the property itself but that property's properties?

e.g.

let someLine = CAShapeLayer()

func setupLine(inout line:CAShapeLayer, startingPath: CGPath) {
    line.path = startingPath
    line.strokeColor = UIColor.whiteColor().CGColor
    line.fillColor = nil
    line.lineWidth = 1
}

setupLine(&someLine, startingPath: somePath)

Also if there is a better way to set up a bunch of properties the same way when they aren't in a loop that would be helpful too.


Solution

  • CAShapeLayer is a class and therefore a reference type.

    let someLine = CAShapeLayer()
    

    is a constant reference to a CAShapeLayer object. You can simply pass this reference to the function and modify the properties of the referenced object within the function. There is no need for the & operator or inout:

    func setupLine(line: CAShapeLayer, startingPath: CGPath) {
        line.path = startingPath
        line.strokeColor = UIColor.whiteColor().CGColor
        line.fillColor = nil
        line.lineWidth = 1
    }
    
    let someLine = CAShapeLayer()
    setupLine(someLine, startingPath: somePath)
    

    A possible alternative is a convenience initializer

    extension CAShapeLayer {
        convenience init(lineWithPath path: CGPath) {
            self.init()
            self.path = path
            self.strokeColor = UIColor.whiteColor().CGColor
            self.fillColor = nil
            self.lineWidth = 1
        }
    }
    

    so that the layer can be created as

    let someLine = CAShapeLayer(lineWithPath: somePath)
    

    A complete example for your playground. Note that it uses default parameters to make it more versatile:

    import UIKit
    
    class ShapedView: UIView{
        override var layer: CALayer {
            let path = UIBezierPath(ovalInRect:CGRect(x:0, y:0, width: self.frame.width, height: self.frame.height)).CGPath
            return CAShapeLayer(lineWithPath: path)
        }
    }
    
    extension CAShapeLayer {
        convenience init(lineWithPath path: CGPath, strokeColor:UIColor? = .whiteColor(), fillColor:UIColor? = nil, lineWidth:CGFloat = 1) {
            self.init()
            self.path = path
            if let strokeColor = strokeColor { self.strokeColor = strokeColor.CGColor } else {self.strokeColor = nil}
            if let fillColor   = fillColor   { self.fillColor   = fillColor.CGColor   } else {self.fillColor   = nil}
            self.lineWidth     = lineWidth
        }
    }
    
    
    let view = ShapedView(frame: CGRect(x:0, y:0, width: 100, height: 100))
    

    result with defaults:

    screenshot