Save and store electronic signatures in UWP

PSPDFKit allows you to implement your own mechanism for storing signatures.

If you provide such a mechanism, then signatures may optionally be saved at the same time they’re added to a document. The saving of signatures is based on the Controller.ElectronicSignatureStorage property, which allows the user to choose whether or not to save a signature in the UI.

Screenshot of enabled toggle for saving signatures.

If you provide stored signatures to PSPDFKit, then when the user selects a signature form field or the signature tool, the list of stored signatures will be shown instead of the signature creation UI.

Screenshot showing list with two signatures: John Appleseed and J.A.

To achieve this, you can set the ElectronicSignatureStorage property of the Controller class. Once this property is set, the signatures UI is updated to display a checkbox to allow users to decide whether or not they want their signatures stored.

The OnSignatureCreated method is invoked with the signature annotation created by the user. There, you can serialize the signature and send it to a backend server, or add it to any other storage mechanism such as an SQL database or LocalSettings. The OnSignatureDeleted method is invoked when the annotation is deleted, and should instead remove the passed annotation from the storage.

Your implementation of the IElectronicSignatureStorage interface should also set the InitialSignatures property. This property describes the contents of the storage and will be used by the UI to display signatures to the user the first time they open the signature UI.

As an example, the code below shows how to implement the IElectronicSignatureStorage interface to persist signatures by serializing them into JSON and then saving them to a file. Please note that this code isn’t ideal for production applications, since its main purpose is to illustrate how to handle the callbacks and not to deal with performance and resilience:

public sealed class ExternalJsonFileStorage : IElectronicSignatureStorage
{
	private readonly string fileName;

	// Signatures that are initially loaded when a document is opened.
	// Note that this isn't updated automatically, and, if set, is always used when opening documents.
	public IList<IAnnotation> InitialSignatures { get; set; }

	private ExternalJsonFileStorage(string fileName, IList<IAnnotation> initialSignatures)
	{
		this.fileName = fileName;
		InitialSignatures = initialSignatures;
	}

	public async void OnSignatureCreated(IAnnotation signature)
	{
		var signatures = await ReadSignaturesFromFile(fileName);
		signatures.Add(signature);
		await WriteSignaturesToFile(fileName, signatures);
	}

	public async void OnSignatureDeleted(IAnnotation signature)
	{
		var signatures = await ReadSignaturesFromFile(fileName);
		var newSignatures = signatures.Where(s => s.Id != signature.Id).ToList();
		await WriteSignaturesToFile(fileName, newSignatures);
	}

	public static async Task<ExternalJsonFileStorage> Create(string fileName)
	{
		// If the file already exists, we read it and pass the saved signatures as the initial set.
		var initialSignatures = await ReadSignaturesFromFile(fileName);
		return new ExternalJsonFileStorage(fileName, initialSignatures);
	}

	private static async Task<StorageFile> TryReadingFile(string fileName)
		=> await ApplicationData.Current.LocalFolder.TryGetItemAsync(fileName) as StorageFile;

	private static async Task<IList<IAnnotation>> ReadSignaturesFromFile(string fileName)
	{
		var file = await TryReadingFile(fileName);
		return file is null
				? new List<IAnnotation>()
				: JsonArray
					.Parse(await FileIO.ReadTextAsync(file))
					.Select(signatureJson => Factory.FromJson(signatureJson.GetObject()))
					.ToList();
	}

	private async Task WriteSignaturesToFile(string fileName, IList<IAnnotation> signatures)
	{
		// Convert the annotations to JSON.
		var annotationsJson = new JsonArray();
		foreach (var annotation in signatures)
		{
			annotationsJson.Add(annotation.ToJson());
		}

		var file = await ApplicationData.Current.LocalFolder
				.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);

		await FileIO.WriteTextAsync(file, annotationsJson.Stringify());
	}
}