Sources/XCMetricsBackendLib/Common/Utils/TemporaryFile.swift (38 lines of code) (raw):

// Copyright (c) 2020 Spotify AB. // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. import Foundation /// A wrapper around a temporary file in a temporary directory. The directory /// has been especially created for the file, so it's safe to delete when you're /// done working with the file. /// /// Call `deleteDirectory` when you no longer need the file. struct TemporaryFile { let directoryURL: URL let fileURL: URL /// Deletes the temporary directory and all files in it. let deleteDirectory: () throws -> Void /// Creates a temporary directory with a unique name and initializes the /// receiver with a `fileURL` representing a file named `filename` in that /// directory. /// /// - Note: This doesn't create the file! init(creatingTempDirectoryForFilename filename: String) throws { let (directory, deleteDirectory) = try FileManager.default .urlForUniqueTemporaryDirectory() self.directoryURL = directory self.fileURL = directory.appendingPathComponent(filename) self.deleteDirectory = deleteDirectory } } extension FileManager { /// Creates a temporary directory with a unique name and returns its URL. /// /// - Returns: A tuple of the directory's URL and a delete function. /// Call the function to delete the directory after you're done with it. /// /// - Note: You should not rely on the existence of the temporary directory /// after the app is exited. func urlForUniqueTemporaryDirectory(preferredName: String? = nil) throws -> (url: URL, deleteDirectory: () throws -> Void) { let basename = preferredName ?? UUID().uuidString var counter = 0 var createdSubdirectory: URL? = nil repeat { do { let subdirName = counter == 0 ? basename : "\(basename)-\(counter)" let subdirectory = temporaryDirectory .appendingPathComponent(subdirName, isDirectory: true) try createDirectory(at: subdirectory, withIntermediateDirectories: false) createdSubdirectory = subdirectory } catch CocoaError.fileWriteFileExists { // Catch file exists error and try again with another name. // Other errors propagate to the caller. counter += 1 } } while createdSubdirectory == nil let directory = createdSubdirectory! let deleteDirectory: () throws -> Void = { try self.removeItem(at: directory) } return (directory, deleteDirectory) } }