Q:

Draw app is Lagging when adding shapeLayer into views layer in Swift / IOS

I am creating a drawing app. This app allows user to draw on the view. i name that view drawing view in the code given below. To create this app I have use BeizerPath and ShapeLayer, whenever user touches the screen the UIBeizerPath and CAShapeLayer is initialized.  The Problem with this app is after some drawing view gets Lagged. I cannot figure out what is happening. Is there better way to optimize this app?
CanvasViewController: UIViewController {

    // MARK: Properties

    @IBOutlet weak var verticalScrollView: UIScrollView!
    var lastPoint:CGPoint?   // var mutablePath:CGMutablePath?
    var drawLine:UIBezierPath?
    var shapeLayer:CAShapeLayer?
    let dropDown = DropDown()
    var shape:CAShapeLayer?
    let listOfDropDownMenu:[String] = ["Create New Canvas","Save","Change Color"]
    var presenter: CanvasModuleInterface?

    // MARK: IBOutlets
     @IBOutlet weak var drawingView: UIView!
    @IBOutlet weak var showColorViewControllerDropDownButton: UIBarButtonItem!

    // MARK: VC's Life cycle

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setup()
        self.verticalScrollView.isUserInteractionEnabled = false
    }

    // MARK: IBActions

    // MARK: Other Functions

    @IBAction func ShowColorViewControllerAction(_ sender: Any) {
         self.dropDown.anchorView = self.showColorViewControllerDropDownButton
         self.dropDown.dataSource = self.listOfDropDownMenu
        self.dropDown.selectionAction = { [unowned self] (index: Int, item: String) in
            switch index{
            case 0:
              self.createNewView()
            case 1:
              self.saveTheCanvas()
            case 2:
             self.presentToColorViewController()

            //self.presentColorController()
            default:
                print("out of order !!!")
            }

        }
        self.dropDown.show()
    }

    private func createNewView(){
        //Mark: - create new canvas
        self.drawingView.layer.sublayers!.forEach { (layers) in
            layers.removeFromSuperlayer()
        }
    }
    private func presentToColorViewController(){
        // Mark : - naviagate to color view controller.

    }
    private func saveTheCanvas(){
        // Mark: - save the drawing

    }

    private func setup() {
        // all setup should be done here
    }
    private func presentColorController(){
        let colorSelectionController = EFColorSelectionViewController()
        let colorNavigationController = UINavigationController(rootViewController: colorSelectionController)
        colorNavigationController.navigationBar.backgroundColor = UIColor.white
        colorNavigationController.navigationBar.isTranslucent = false
        colorNavigationController.modalPresentationStyle = UIModalPresentationStyle.popover
        colorSelectionController.delegate = self
        colorSelectionController.color = self.view.backgroundColor ?? UIColor.white

        if UIUserInterfaceSizeClass.compact == self.traitCollection.horizontalSizeClass {
            let doneButton: UIBarButtonItem = UIBarButtonItem(
                title: NSLocalizedString("Done", comment: ""),
                style: UIBarButtonItem.Style.done,
                target: self,
                action: #selector(dismissViewController)
            )
            colorSelectionController.navigationItem.rightBarButtonItem = doneButton
        }
        self.present(colorNavigationController, animated: true, completion: nil)
    }
    @objc func dismissViewController(){
        //##ask confusion.....
        self.dismiss(animated: true, completion: nil)
    }

}

// MARK: CanvasViewInterface extension CanvasViewController: CanvasViewInterface {
     } extension CanvasViewController: EFColorSelectionViewControllerDelegate{
    func colorViewController(_ colorViewCntroller: EFColorSelectionViewController, didChangeColor color: UIColor) {

    } } extension CanvasViewController{

    //Mark: - this extension contains function for drawing the circle.
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("touches begin")

        self.drawLine = UIBezierPath()
        self.shapeLayer = CAShapeLayer()
        //       self.mutablePath = CGMutablePath()

        if let touchesPoint = touches.first{
            self.lastPoint = touchesPoint.location(in: self.drawingView)
         }

    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
         //        self.drawingView.layer.sublayers!.forEach { (layer) in //            print(layer) //        }
      //  print(self.drawingView.layer)

    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?){ //        print("\(self.drawingView)") //        print("\(self.drawingView.layer.sublayers)")

        var nextPoint:CGPoint?
        if let touchesPoint = touches.first{
            nextPoint = touchesPoint.location(in: self.drawingView)
            guard let lastLinePoint = self.lastPoint , let nextLinePoint = nextPoint else{return}
            self.drawLineInDrawingView(from: lastLinePoint, to: nextLinePoint)
        }
        if let newPoint = nextPoint{
        self.lastPoint  = newPoint
        }
    }
    func drawLineInDrawingView(from:CGPoint,to:CGPoint){
        drawLine!.move(to: CGPoint(x: from.x, y: from.y))
        drawLine!.addLine(to: CGPoint(x: to.x, y:  to.y))
        shapeLayer!.path = drawLine!.cgPath
        shapeLayer!.strokeColor = UIColor.black.cgColor
        shapeLayer!.lineCap = .round
        shapeLayer!.lineWidth = 100
        self.drawingView.layer.addSublayer(shapeLayer!)    //     print(self.drawingView.layer.sublayers)
    }
     }

A:

Without knowing what CanvasViewController you are are using, one can't entirely answer your question correctly, but I will answer, assuming it works similar to any regular UIView in iOS.
There are a few things I can point out to improve performance:
First of all, don't add the same ShapeLayer over and over inside drawLineInDrawingView(from:CGPoint,to:CGPoint) to the drawing view, instead do so only once in touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) and store a reference to it.
Secondly your drawLineInDrawingView(from:CGPoint,to:CGPoint) should look like this:
func drawLineInDrawingView(from:CGPoint,to:CGPoint){
    CATransaction.begin()
    CATransaction.setDisableActions(true)

    drawLine!.move(to: CGPoint(x: from.x, y: from.y))
    drawLine!.addLine(to: CGPoint(x: to.x, y:  to.y))
    shapeLayer!.path = drawLine!.cgPath
    shapeLayer!.strokeColor = UIColor.black.cgColor
    shapeLayer!.lineCap = .round
    shapeLayer!.lineWidth = 100
    shapeLayer!.setNeedsDisplay()

    CATransaction.commit()
}

So in conclusion: Upon touchesBegan() you add the shapeLayer and store a reference to it. Inside touchesMoved, you call the drawLineInDrawingsFunction.
Your drawLineInDrawingsFunction, only updates your Shapelayer with shapeLayer.setNeedsDisplay().
Putting your transformation inside 
CATransaction.begin()
CATransaction.setDisableActions(true)
/**Your transformation here**/
CATransaction.commit()

Is only there to stop your View from automatically animating changes, and instead show them immediately.
I hope this helps.

