---
title: Importing Bézier path data into PencilKit
framework: PencilKit
role: article
role_heading: Article
path: pencilkit/importing-external-drawing-data-into-pencilkit
---

# Importing Bézier path data into PencilKit

Convert existing Bézier-based stroke data into PencilKit drawing strokes.

## Overview

Overview If your app handles drawing data as Bézier paths, such as when you import an existing file format, interface with a third-party library, or store drawing data in your own data model, you can convert those Bézier paths directly into PencilKit drawing strokes and adopt PencilKit as your rendering engine without discarding your existing data. PencilKit uses a stroke format optimized for Apple Pencil input, represented by PKStroke, which differs from the Bézier path format that Core Graphics and other frameworks use. Because the two formats represent curves differently, the conversion is an approximation. After you convert your stroke data, review the results to confirm the strokes look as you expect. Convert a Bézier path to a stroke To convert a Bézier path, initialize a PKStrokePath from a CGPath using init(bezierPath:creationDate:pointProvider:). Because a Bézier path describes only the shape of a curve and not the per-point properties PencilKit uses for rendering, such as pressure, opacity, and size, you supply those values through the pointProvider closure, which the system calls once for each point in the resulting path. important: init(bezierPath:creationDate:pointProvider:) only converts the first subpath. Split each subpath into its own CGPath before converting. To call the initializer, you supply two values alongside the Bézier path: a creationDate for the stroke, and a pointProvider closure that returns rendering properties for each point. The creationDate is the start time of the stroke. Pass a timestamp from your source data if one exists, or use Date() for the current time. The number of points PencilKit derives from your Bézier path may differ from the number of control points in the original. Use index and pointCount to calculate values that vary along the stroke rather than mapping points one-to-one from your source data. The initializer calls pointProvider once for each derived point, passing a PKStrokePath.ConvertedBezierPoint with the following values: location: The position of the derived B-spline control point, which you pass directly to PKStrokePoint. index and pointCount: index is the zero-based position of the control point in the derived path, and pointCount is the total number of control points. Divide one by the other to calculate a progress value between 0 and 1 for properties that vary along the stroke. bezierSegmentIndex: The index of the original Bézier segment that this derived control point maps to. Use this value if your source data stores per-segment properties like color or width. PencilKit sets location from the Bézier path geometry, but you define all other properties — such as size, opacity, and force — in your pointProvider closure. For the following properties, consider whether your source data includes values to use rather than a fixed default: timeOffset: The time in seconds from the stroke’s creationDate to this derived control point. The example below distributes time evenly along the stroke based on index position, giving earlier points smaller offsets and later points larger ones. If your source data includes per-point or per-segment timestamps, use those elapsed times instead. azimuth and altitude: These properties describe the orientation of an Apple Pencil. For imported data that doesn’t include pencil orientation, use reasonable values instead. For example, an altitude of .pi / 4 matches how most people naturally hold a pencil. Because azimuth only affects rendering once the pencil is tilted, choose a value that fits your ink and app rather than relying on a single default. The following example shows a complete conversion from a CGPath to a PKStroke, using index and pointCount to calculate timeOffset and applying uniform values for all other properties: func makeStroke(from bezierPath: CGPath, ink: PKInk) -> PKStroke {     let path = PKStrokePath(         bezierPath: bezierPath,         creationDate: Date(),         pointProvider: { convertedPoint in             let progress = CGFloat(convertedPoint.index) / CGFloat(convertedPoint.pointCount)             return PKStrokePoint(                 location: convertedPoint.location,                 timeOffset: 0.5 * progress,                 size: CGSize(width: 3.0, height: 3.0),                 opacity: 1.0,                 force: 1.0,                 azimuth: .pi,                 altitude: .pi / 4,                 secondaryScale: 1.0,                 threshold: 0.0             )         }     )     return PKStroke(ink: ink, path: path) } Save and load PencilKit strokes in a Bézier file format If you want to keep Bézier paths as your file format after adopting PencilKit, you can export strokes back to Bézier paths for saving and reload them with full fidelity. Use bezierRepresentation to export a stroke path to a CGPath for saving, then init(bezierPath:creationDate:pointProvider:) to load it back. A Bézier path stores only the shape of the curve and doesn’t include properties like size, opacity, and force. When exporting a PencilKit stroke, save those properties separately for each point so you can reconstruct the full stroke when loading back. The following example shows how to save these additional properties alongside the Bézier path: struct SavedPoint: Codable {     let timeOffset: TimeInterval     let size: CGSize     let opacity: CGFloat     let force: CGFloat     let azimuth: CGFloat     let altitude: CGFloat     let secondaryScale: CGFloat     let threshold: CGFloat

init(_ point: PKStrokePoint) {         timeOffset = point.timeOffset         size = point.size         opacity = point.opacity         force = point.force         azimuth = point.azimuth         altitude = point.altitude         secondaryScale = point.secondaryScale         threshold = point.threshold     } }

let bezierPath = stroke.path.bezierRepresentation let savedPoints = stroke.path.map { SavedPoint($0) } When loading back a path exported with bezierRepresentation, the number of control points is guaranteed to match the original stroke’s point count — so you can look up each point’s saved data by index: let restoredPath = PKStrokePath(     bezierPath: bezierPath,     creationDate: Date(),     pointProvider: { convertedPoint in         let saved = savedPoints[convertedPoint.index]         return PKStrokePoint(             location: convertedPoint.location,             timeOffset: saved.timeOffset,             size: saved.size,             opacity: saved.opacity,             force: saved.force,             azimuth: saved.azimuth,             altitude: saved.altitude,             secondaryScale: saved.secondaryScale,             threshold: saved.threshold         )     } ) Add the converted strokes to a drawing PKDrawing holds all the strokes that appear in a canvas. After converting your strokes, set the strokes property on a new drawing and assign it to your PKCanvasView. This replaces any existing content in the canvas with your imported strokes. The following example converts a collection of legacy paths to strokes and assigns them to a canvas: let strokes = legacyDocument.paths.map { path in     makeStroke(from: path.cgPath, ink: PKInk(inkType: .pen, color: path.color)) }

var drawing = PKDrawing() drawing.strokes = strokes canvasView.drawing = drawing

## See Also

### Canvas

- [Drawing with PencilKit](pencilkit/drawing-with-pencilkit.md)
- [Customizing Scribble with Interactions](pencilkit/customizing-scribble-with-interactions.md)
- [Inspecting, Modifying, and Constructing PencilKit Drawings](pencilkit/inspecting-modifying-and-constructing-pencilkit-drawings.md)
- [Controlling stroke rendering for animation and editing](pencilkit/controlling-stroke-rendering-for-animation-and-editing.md)
- [PKCanvasView](pencilkit/pkcanvasview.md)
- [PKDrawing](pencilkit/pkdrawing-swift.struct.md)
- [PKStroke](pencilkit/pkstroke-swift.struct.md)
- [PKStrokePath](pencilkit/pkstrokepath-swift.struct.md)
- [PKStrokePoint](pencilkit/pkstrokepoint-swift.struct.md)
- [PKInk](pencilkit/pkink-swift.struct.md)
