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)
}
}