Ring Timer Animation with Chasing Tail using CoreAnimation
There is one problem with your approach: Additional sublayers are added
in draw() and in animateTimerReset() and never removed. That can
cause memory problems and slower drawing eventually.

I would suggest to create and add only a single instance of the animation layer, this can for example be done in

override func awakeFromNib() {
    layer.addSublayer(animationLayer)
}


Then animate this single animation layer:

func animateTimer(over duration: TimeInterval) {
    animationLayer.removeAllAnimations()
    let head = CABasicAnimation(keyPath: "strokeEnd")
    head.toValue = 1
    head.duration = duration
    head.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    head.fillMode = kCAFillModeForwards
    head.isRemovedOnCompletion = false
    animationLayer.add(head, forKey: "strokeEnd")
}

func animateTimerReset() {
    let tail = CABasicAnimation(keyPath: "strokeStart")
    tail.toValue = 1
    tail.duration = 0.1
    tail.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    tail.fillMode = kCAFillModeForwards
    tail.isRemovedOnCompletion = false
    animationLayer.add(tail, forKey: "strokeStart")
}


Some further remarks: At several places the explicit type
annotation can be removed, e.g.

var trackColor: UIColor = UIColor(red: 0.3, green: 0.3, blue: 0.3, alpha: 1)
var trackShade: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
var trackLight: UIColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.3)


is shorter written as

var trackColor = UIColor(red: 0.3, green: 0.3, blue: 0.3, alpha: 1)
var trackShade = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
var trackLight = UIColor(red: 1, green: 1, blue: 1, alpha: 0.3)


Also “implicit member expressions“ can be used if the type is inferred from the context, e.g.

layer.strokeEnd = CGFloat.leastNormalMagnitude


becomes

layer.strokeEnd = .leastNormalMagnitude