Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
0be9d19396 | |
![]() |
ef89fdcbf6 | |
![]() |
838d43e66d | |
![]() |
4f31371f20 | |
![]() |
1c604afe9c | |
![]() |
bd8231252f | |
![]() |
d79ed6d6bc | |
![]() |
375187f613 | |
![]() |
38b15c9b8b | |
![]() |
f68cc90187 | |
![]() |
a42084f924 | |
![]() |
2368bb8d56 |
32
README.md
32
README.md
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
WebDAV communication library for Swift
|
WebDAV communication library for Swift
|
||||||
|
|
||||||
|
**Note**: This has only been tested on **Nextcloud** servers.
|
||||||
|
Submit an [issue](https://github.com/Isvvc/WebDAV-Swift/issues) if you have compatibility issues with other WebDAV servers.
|
||||||
|
Help in adding support for additional services would be greatly appreciated!
|
||||||
|
See [Contribution](#contribution).
|
||||||
|
|
||||||
|
* [\[#43\]](https://github.com/Isvvc/WebDAV-Swift/issues/43) Apache WebDAV has been reported to not work
|
||||||
|
|
||||||
## Table of contents
|
## Table of contents
|
||||||
|
|
||||||
+ [Install](#install)
|
+ [Install](#install)
|
||||||
|
@ -182,6 +189,31 @@ Image functions include:
|
||||||
_Why is there no `deleteCachedImage` or `cachedImageURL` function when there is `getCachedThumbnail` and `cachedThumbnailURL`?_
|
_Why is there no `deleteCachedImage` or `cachedImageURL` function when there is `getCachedThumbnail` and `cachedThumbnailURL`?_
|
||||||
Images are stored in the disk cache the same way as data. The image-specific functions exist as a convenience for converting the data to UIImages and caching them in memory that way. Since the cached data URL does not change whether the data is an image or not, `deleteCachedData` and `cachedDataURL` can be used for images.
|
Images are stored in the disk cache the same way as data. The image-specific functions exist as a convenience for converting the data to UIImages and caching them in memory that way. Since the cached data URL does not change whether the data is an image or not, `deleteCachedData` and `cachedDataURL` can be used for images.
|
||||||
|
|
||||||
|
#### Thumbnail preview
|
||||||
|
|
||||||
|
If there are already cached thumbnails for the image you are trying to fetch, you can use the `preview` parameter to specify that you would like to get that thumbnail first while the full-size image is downloading. When you do this, the completion closure will run with a `.placeholder` error on the call with the thumbnail.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
webDAV.downloadImage(path: imagePath, account: account, password: password, preview: .memoryOnly) { image, error in
|
||||||
|
switch error {
|
||||||
|
case .none, .placeholder:
|
||||||
|
// .none is the full-size image.
|
||||||
|
// .placeholder is the thumbnail.
|
||||||
|
// The completion closure will not be run with the thumbnail after
|
||||||
|
// it is run with the full-size image, so assuming you don't have
|
||||||
|
// a use for the thumbnail after the full-size image loads, you
|
||||||
|
// shouldn't need to check which it is before displaying.
|
||||||
|
break
|
||||||
|
case .some(let unexpectedError):
|
||||||
|
// Log the error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the image
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Thumbnails](#thumbnails) for more details on thumbnails.
|
||||||
|
|
||||||
### Thumbnails
|
### Thumbnails
|
||||||
|
|
||||||
Along with downloading full-sized images, you can download **thumbnails** from Nextcloud servers.
|
Along with downloading full-sized images, you can download **thumbnails** from Nextcloud servers.
|
||||||
|
|
|
@ -23,9 +23,16 @@ public extension WebDAV {
|
||||||
func cachedDataURL<A: WebDAVAccount>(forItemAtPath path: String, account: A) -> URL? {
|
func cachedDataURL<A: WebDAVAccount>(forItemAtPath path: String, account: A) -> URL? {
|
||||||
guard let encodedDescription = UnwrappedAccount(account: account)?.encodedDescription,
|
guard let encodedDescription = UnwrappedAccount(account: account)?.encodedDescription,
|
||||||
let caches = cacheFolder else { return nil }
|
let caches = cacheFolder else { return nil }
|
||||||
return caches
|
let trimmedPath = path.trimmingCharacters(in: AccountPath.slash)
|
||||||
.appendingPathComponent(encodedDescription)
|
|
||||||
.appendingPathComponent(path.trimmingCharacters(in: AccountPath.slash))
|
if trimmedPath.isEmpty {
|
||||||
|
return caches
|
||||||
|
.appendingPathComponent(encodedDescription)
|
||||||
|
} else {
|
||||||
|
return caches
|
||||||
|
.appendingPathComponent(encodedDescription)
|
||||||
|
.appendingPathComponent(trimmedPath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the local cached data URL for the item at the specified path if there is cached data there.
|
/// Get the local cached data URL for the item at the specified path if there is cached data there.
|
||||||
|
@ -178,13 +185,14 @@ extension WebDAV {
|
||||||
|
|
||||||
func cleanupDiskCache<A: WebDAVAccount>(at path: String, account: A, files: [WebDAVFile]) throws {
|
func cleanupDiskCache<A: WebDAVAccount>(at path: String, account: A, files: [WebDAVFile]) throws {
|
||||||
let fm = FileManager.default
|
let fm = FileManager.default
|
||||||
guard let url = cachedDataURL(forItemAtPath: path, account: account),fm.fileExists(atPath: url.path) else { return }
|
guard let url = cachedDataURL(forItemAtPath: path, account: account),
|
||||||
|
fm.fileExists(atPath: url.path) else { return }
|
||||||
|
|
||||||
let goodFilePaths = Set(files.compactMap { cachedDataURL(forItemAtPath: $0.path, account: account)?.path })
|
let goodFilePaths = files.compactMap { cachedDataURL(forItemAtPath: $0.path, account: account)?.path }
|
||||||
|
|
||||||
let infoPlist = filesCacheURL?.path
|
let infoPlist = filesCacheURL?.path
|
||||||
for path in try fm.contentsOfDirectory(atPath: url.path).map({ url.appendingPathComponent($0).path })
|
for path in try fm.contentsOfDirectory(atPath: url.path).map({ url.appendingPathComponent($0).path })
|
||||||
where !goodFilePaths.contains(path)
|
where !goodFilePaths.contains(where: { path.starts(with: $0) })
|
||||||
&& path != infoPlist {
|
&& path != infoPlist {
|
||||||
try fm.removeItem(atPath: path)
|
try fm.removeItem(atPath: path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,16 @@ public struct ThumbnailProperties: Hashable {
|
||||||
|
|
||||||
public extension WebDAV {
|
public extension WebDAV {
|
||||||
|
|
||||||
|
enum ThumbnailPreviewMode {
|
||||||
|
/// Only show a preview thumbnail if there is already one loaded into memory.
|
||||||
|
case memoryOnly
|
||||||
|
/// Allow the disk cache to be loaded if there are no thumbnails in memory.
|
||||||
|
/// Note that this can be an expensive process and is run on the main thread.
|
||||||
|
case diskAllowed
|
||||||
|
/// Load the specific thumbnail if available, from disk if not in memory.
|
||||||
|
case specific(ThumbnailProperties)
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: Images
|
//MARK: Images
|
||||||
|
|
||||||
/// Download and cache an image from the specified file path.
|
/// Download and cache an image from the specified file path.
|
||||||
|
@ -75,16 +85,40 @@ public extension WebDAV {
|
||||||
/// - account: The WebDAV account.
|
/// - account: The WebDAV account.
|
||||||
/// - password: The WebDAV account's password.
|
/// - password: The WebDAV account's password.
|
||||||
/// - options: Options for caching the results. Empty set uses default caching behavior.
|
/// - options: Options for caching the results. Empty set uses default caching behavior.
|
||||||
/// - completion: If account properties are invalid, this will run immediately on the same thread.
|
/// - preview: Behavior for running the completion closure with cached thumbnails before the full-sized image is fetched.
|
||||||
/// Otherwise, it runs when the network call finishes on a background thread.
|
/// Note that `.diskAllowed` will load all thumbnails for the given image which can be an expensive process.
|
||||||
|
/// `.memoryOnly` and `.specific()` are recommended unless you do not know what thumbnails exist.
|
||||||
|
/// - completion: If account properties are invalid, this will run immediately on the same thread with an error.
|
||||||
|
/// Otherwise, it will run on a utility thread with a preview (if available and a `preview` mode is provided) with a `.placeholder` error,
|
||||||
|
/// then run on a background thread when the network call finishes.
|
||||||
|
/// This will not be called with the thumbnail after being called with the full-size image.
|
||||||
/// - image: The image downloaded, if successful.
|
/// - image: The image downloaded, if successful.
|
||||||
/// The cached image if it has already been downloaded.
|
/// The cached image if it has already been downloaded.
|
||||||
/// - cachedImageURL: The URL of the cached image.
|
/// - cachedImageURL: The URL of the cached image.
|
||||||
/// - error: A WebDAVError if the call was unsuccessful. `nil` if it was.
|
/// - error: A WebDAVError if the call was unsuccessful. `nil` if it was.
|
||||||
/// - Returns: The request identifier.
|
/// - Returns: The request identifier.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func downloadImage<A: WebDAVAccount>(path: String, account: A, password: String, caching options: WebDAVCachingOptions = [], completion: @escaping (_ image: UIImage?, _ error: WebDAVError?) -> Void) -> URLSessionDataTask? {
|
func downloadImage<A: WebDAVAccount>(path: String, account: A, password: String, caching options: WebDAVCachingOptions = [], preview: ThumbnailPreviewMode? = .none, completion: @escaping (_ image: UIImage?, _ error: WebDAVError?) -> Void) -> URLSessionDataTask? {
|
||||||
cachingDataTask(cache: imageCache, path: path, account: account, password: password, caching: options, valueFromData: { UIImage(data: $0) }, completion: completion)
|
cachingDataTask(cache: imageCache, path: path, account: account, password: password, caching: options, valueFromData: { UIImage(data: $0) }, placeholder: {
|
||||||
|
// Load placeholder thumbnail
|
||||||
|
var thumbnails = self.getAllMemoryCachedThumbnails(forItemAtPath: path, account: account)?.values
|
||||||
|
|
||||||
|
switch preview {
|
||||||
|
case .none:
|
||||||
|
return nil
|
||||||
|
case .specific(let properties):
|
||||||
|
return self.getCachedThumbnail(forItemAtPath: path, account: account, with: properties)
|
||||||
|
case .memoryOnly:
|
||||||
|
break
|
||||||
|
case .diskAllowed:
|
||||||
|
// Only load from disk if there aren't any in memory
|
||||||
|
if thumbnails?.isEmpty ?? true {
|
||||||
|
thumbnails = self.getAllMemoryCachedThumbnails(forItemAtPath: path, account: account)?.values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let largestThumbnail = thumbnails?.max(by: { $0.size.width * $0.size.height < $1.size.width * $1.size.height })
|
||||||
|
return largestThumbnail
|
||||||
|
}, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: Thumbnails
|
//MARK: Thumbnails
|
||||||
|
|
|
@ -384,7 +384,10 @@ extension WebDAV {
|
||||||
|
|
||||||
//MARK: Standard Requests
|
//MARK: Standard Requests
|
||||||
|
|
||||||
func cachingDataTask<A: WebDAVAccount, Value: Equatable>(cache: Cache<AccountPath, Value>, path: String, account: A, password: String, caching options: WebDAVCachingOptions, valueFromData: @escaping (_ data: Data) -> Value?, completion: @escaping (_ value: Value?, _ error: WebDAVError?) -> Void) -> URLSessionDataTask? {
|
func cachingDataTask<A: WebDAVAccount, Value: Equatable>(
|
||||||
|
cache: Cache<AccountPath, Value>, path: String, account: A, password: String,
|
||||||
|
caching options: WebDAVCachingOptions, valueFromData: @escaping (_ data: Data) -> Value?, placeholder: (() -> Value?)? = nil,
|
||||||
|
completion: @escaping (_ value: Value?, _ error: WebDAVError?) -> Void) -> URLSessionDataTask? {
|
||||||
|
|
||||||
// Check cache
|
// Check cache
|
||||||
|
|
||||||
|
@ -407,10 +410,19 @@ extension WebDAV {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cached data was not returned. Continue with network fetch.
|
||||||
|
|
||||||
if options.contains(.removeExistingCache) {
|
if options.contains(.removeExistingCache) {
|
||||||
try? deleteCachedData(forItemAtPath: path, account: account)
|
try? deleteCachedData(forItemAtPath: path, account: account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let placeholderTask = DispatchWorkItem {
|
||||||
|
if let placeholderValue = placeholder?() {
|
||||||
|
completion(placeholderValue, .placeholder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DispatchQueue.global(qos: .utility).async(execute: placeholderTask)
|
||||||
|
|
||||||
// Create network request
|
// Create network request
|
||||||
|
|
||||||
guard let request = authorizedRequest(path: path, account: account, password: password, method: .get) else {
|
guard let request = authorizedRequest(path: path, account: account, password: password, method: .get) else {
|
||||||
|
@ -427,6 +439,7 @@ extension WebDAV {
|
||||||
return completion(nil, error)
|
return completion(nil, error)
|
||||||
} else if let data = data,
|
} else if let data = data,
|
||||||
let value = valueFromData(data) {
|
let value = valueFromData(data) {
|
||||||
|
placeholderTask.cancel()
|
||||||
// Cache result
|
// Cache result
|
||||||
if !options.contains(.removeExistingCache),
|
if !options.contains(.removeExistingCache),
|
||||||
!options.contains(.doNotCacheResult) {
|
!options.contains(.doNotCacheResult) {
|
||||||
|
@ -528,7 +541,7 @@ extension WebDAV {
|
||||||
for (key, _) in filesCache
|
for (key, _) in filesCache
|
||||||
where key.path != directory
|
where key.path != directory
|
||||||
&& key.path.starts(with: directory)
|
&& key.path.starts(with: directory)
|
||||||
&& !files.contains(where: { key.path.starts(with: $0.path) }) {
|
&& !files.contains(where: { key.path.starts(with: $0.path.trimmingCharacters(in: AccountPath.slash)) }) {
|
||||||
filesCache.removeValue(forKey: key)
|
filesCache.removeValue(forKey: key)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ public enum WebDAVError: Error {
|
||||||
case unsupported
|
case unsupported
|
||||||
/// Another unspecified Error occurred.
|
/// Another unspecified Error occurred.
|
||||||
case nsError(Error)
|
case nsError(Error)
|
||||||
|
/// The returned value is simply a placeholder.
|
||||||
|
case placeholder
|
||||||
|
|
||||||
static func getError(statusCode: Int?, error: Error?) -> WebDAVError? {
|
static func getError(statusCode: Int?, error: Error?) -> WebDAVError? {
|
||||||
if let statusCode = statusCode {
|
if let statusCode = statusCode {
|
||||||
|
|
|
@ -20,7 +20,7 @@ public struct WebDAVFile: Identifiable, Codable, Equatable, Hashable {
|
||||||
public private(set) var size: Int
|
public private(set) var size: Int
|
||||||
public private(set) var etag: String
|
public private(set) var etag: String
|
||||||
|
|
||||||
init(path: String, id: String, isDirectory: Bool, lastModified: Date, size: Int, etag: String) {
|
public init(path: String, id: String, isDirectory: Bool, lastModified: Date, size: Int, etag: String) {
|
||||||
self.path = path
|
self.path = path
|
||||||
self.id = id
|
self.id = id
|
||||||
self.isDirectory = isDirectory
|
self.isDirectory = isDirectory
|
||||||
|
|
|
@ -356,29 +356,46 @@ final class WebDAVTests: XCTestCase {
|
||||||
func testCleanupFilesCacheRoot() {
|
func testCleanupFilesCacheRoot() {
|
||||||
guard let (account, password) = getAccount() else { return XCTFail() }
|
guard let (account, password) = getAccount() else { return XCTFail() }
|
||||||
|
|
||||||
|
let listRealDirExpectation = XCTestExpectation(description: "List files from real directory.")
|
||||||
let expectation = XCTestExpectation(description: "List files from WebDAV")
|
let expectation = XCTestExpectation(description: "List files from WebDAV")
|
||||||
|
|
||||||
let path = UUID().uuidString
|
let realDir = createFolder(account: account, password: password)
|
||||||
let accountPath = AccountPath(account: account, path: path)
|
let realDirAccountPath = AccountPath(account: account, path: realDir)
|
||||||
|
|
||||||
|
let fakeDir = UUID().uuidString
|
||||||
|
let fakeDirAccountPath = AccountPath(account: account, path: fakeDir)
|
||||||
|
|
||||||
|
// Load real file into cache
|
||||||
|
uploadData(to: realDir, account: account, password: password)
|
||||||
|
webDAV.listFiles(atPath: realDir, account: account, password: password, caching: .doNotReturnCachedResult) { _, _ in
|
||||||
|
listRealDirExpectation.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(for: [listRealDirExpectation], timeout: 10000.0)
|
||||||
|
|
||||||
|
XCTAssertNotNil(webDAV.filesCache[realDirAccountPath])
|
||||||
|
|
||||||
// Place fake cache
|
// Place fake cache
|
||||||
|
|
||||||
webDAV.filesCache[accountPath] = [WebDAVFile(path: path + "/fake.txt", id: "0", isDirectory: true, lastModified: Date(), size: 0, etag: "0")]
|
webDAV.filesCache[fakeDirAccountPath] = [WebDAVFile(path: fakeDir + "/fake.txt", id: "0", isDirectory: true, lastModified: Date(), size: 0, etag: "0")]
|
||||||
XCTAssertNotNil(webDAV.filesCache[accountPath])
|
XCTAssertNotNil(webDAV.filesCache[fakeDirAccountPath])
|
||||||
|
|
||||||
// List files
|
// List files
|
||||||
|
|
||||||
webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
|
webDAV.listFiles(atPath: "/", account: account, password: password, caching: .doNotReturnCachedResult) { files, error in
|
||||||
XCTAssertNotNil(files)
|
XCTAssertNotNil(files)
|
||||||
XCTAssertNil(error)
|
XCTAssertNil(error)
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
wait(for: [expectation], timeout: 10.0)
|
wait(for: [expectation], timeout: 10000.0)
|
||||||
|
|
||||||
// Check that the fake cached file was cleaned up
|
// Check that the fake cached file was cleaned up
|
||||||
|
XCTAssertNil(webDAV.filesCache[fakeDirAccountPath])
|
||||||
|
// Check that the real file still exists
|
||||||
|
XCTAssertNotNil(webDAV.filesCache[realDirAccountPath])
|
||||||
|
|
||||||
XCTAssertNil(webDAV.filesCache[accountPath])
|
deleteFile(path: realDir, account: account, password: password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCleanupFilesCacheSubdirectory() {
|
func testCleanupFilesCacheSubdirectory() {
|
||||||
|
@ -397,7 +414,7 @@ final class WebDAVTests: XCTestCase {
|
||||||
|
|
||||||
// List files
|
// List files
|
||||||
|
|
||||||
webDAV.listFiles(atPath: folder, account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
|
webDAV.listFiles(atPath: folder, account: account, password: password, foldersFirst: false, caching: .doNotReturnCachedResult) { files, error in
|
||||||
XCTAssertNotNil(files)
|
XCTAssertNotNil(files)
|
||||||
XCTAssertNil(error)
|
XCTAssertNil(error)
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
|
@ -413,23 +430,31 @@ final class WebDAVTests: XCTestCase {
|
||||||
|
|
||||||
//MARK: Disk Cache Cleanup
|
//MARK: Disk Cache Cleanup
|
||||||
|
|
||||||
func testCleanupDiskCacheFile() {
|
func testCleanupDiskCacheFileRoot() {
|
||||||
guard let (account, password) = getAccount() else { return XCTFail() }
|
guard let (account, password) = getAccount() else { return XCTFail() }
|
||||||
|
|
||||||
let expectation = XCTestExpectation(description: "List files from WebDAV")
|
let expectation = XCTestExpectation(description: "List files from WebDAV")
|
||||||
|
|
||||||
// Add dummy file to disk cache
|
// Add dummy file to disk cache
|
||||||
|
|
||||||
let path = UUID().uuidString + ".txt"
|
let dummyPath = UUID().uuidString + ".txt"
|
||||||
let data = UUID().uuidString.data(using: .utf8)!
|
let data = UUID().uuidString.data(using: .utf8)!
|
||||||
let tempFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)!
|
let dummyFileURL = webDAV.cachedDataURL(forItemAtPath: dummyPath, account: account)!
|
||||||
XCTAssertNoThrow(try webDAV.saveDataToDiskCache(data, url: tempFileURL))
|
XCTAssertNoThrow(try webDAV.saveDataToDiskCache(data, url: dummyFileURL))
|
||||||
|
|
||||||
XCTAssert(FileManager.default.fileExists(atPath: tempFileURL.path))
|
XCTAssert(FileManager.default.fileExists(atPath: dummyFileURL.path))
|
||||||
|
|
||||||
|
// Create real file
|
||||||
|
|
||||||
|
let realFile = uploadData(account: account, password: password)
|
||||||
|
let realFileURL = webDAV.cachedDataURL(forItemAtPath: realFile.fileName, account: account)!
|
||||||
|
downloadData(path: realFile.fileName, account: account, password: password)
|
||||||
|
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path))
|
||||||
|
|
||||||
// List files
|
// List files
|
||||||
|
|
||||||
webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
|
webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .doNotReturnCachedResult) { files, error in
|
||||||
XCTAssertNotNil(files)
|
XCTAssertNotNil(files)
|
||||||
XCTAssertNil(error)
|
XCTAssertNil(error)
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
|
@ -438,8 +463,11 @@ final class WebDAVTests: XCTestCase {
|
||||||
wait(for: [expectation], timeout: 10.0)
|
wait(for: [expectation], timeout: 10.0)
|
||||||
|
|
||||||
// Check that the fake cached file was cleaned up
|
// Check that the fake cached file was cleaned up
|
||||||
|
XCTAssertFalse(FileManager.default.fileExists(atPath: dummyFileURL.path))
|
||||||
|
// Check that the real file still exists
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path))
|
||||||
|
|
||||||
XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path))
|
deleteFile(path: realFile.fileName, account: account, password: password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCleanupDiskCacheFolder() {
|
func testCleanupDiskCacheFolder() {
|
||||||
|
@ -450,13 +478,23 @@ final class WebDAVTests: XCTestCase {
|
||||||
// Add dummy folder to disk cache
|
// Add dummy folder to disk cache
|
||||||
|
|
||||||
let path = UUID().uuidString
|
let path = UUID().uuidString
|
||||||
let tempFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)!
|
let dummyFileURL = webDAV.cachedDataURL(forItemAtPath: path, account: account)!
|
||||||
XCTAssertNoThrow(try FileManager.default.createDirectory(at: tempFileURL, withIntermediateDirectories: true))
|
XCTAssertNoThrow(try FileManager.default.createDirectory(at: dummyFileURL, withIntermediateDirectories: true))
|
||||||
XCTAssert(FileManager.default.fileExists(atPath: tempFileURL.path))
|
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: dummyFileURL.path))
|
||||||
|
|
||||||
|
// Create real folder
|
||||||
|
|
||||||
|
let folder = createFolder(account: account, password: password)
|
||||||
|
let realFile = uploadData(to: folder, account: account, password: password)
|
||||||
|
downloadData(path: realFile.fileName, account: account, password: password)
|
||||||
|
let realFileURL = webDAV.cachedDataURL(forItemAtPath: realFile.fileName, account: account)!
|
||||||
|
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path))
|
||||||
|
|
||||||
// List files
|
// List files
|
||||||
|
|
||||||
webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
|
webDAV.listFiles(atPath: "/", account: account, password: password, foldersFirst: false, caching: .doNotReturnCachedResult) { files, error in
|
||||||
XCTAssertNotNil(files)
|
XCTAssertNotNil(files)
|
||||||
XCTAssertNil(error)
|
XCTAssertNil(error)
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
|
@ -465,8 +503,11 @@ final class WebDAVTests: XCTestCase {
|
||||||
wait(for: [expectation], timeout: 10.0)
|
wait(for: [expectation], timeout: 10.0)
|
||||||
|
|
||||||
// Check that the fake cached folder was cleaned up
|
// Check that the fake cached folder was cleaned up
|
||||||
|
XCTAssertFalse(FileManager.default.fileExists(atPath: dummyFileURL.path))
|
||||||
|
// Check that the real file still exists
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: realFileURL.path))
|
||||||
|
|
||||||
XCTAssertFalse(FileManager.default.fileExists(atPath: tempFileURL.path))
|
deleteFile(path: folder, account: account, password: password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCleanupDiskCacheWithGoodFile() {
|
func testCleanupDiskCacheWithGoodFile() {
|
||||||
|
@ -496,7 +537,7 @@ final class WebDAVTests: XCTestCase {
|
||||||
|
|
||||||
// List files
|
// List files
|
||||||
|
|
||||||
webDAV.listFiles(atPath: directory.path, account: account, password: password, foldersFirst: false, caching: .ignoreCache) { files, error in
|
webDAV.listFiles(atPath: directory.path, account: account, password: password, caching: .doNotReturnCachedResult) { files, error in
|
||||||
XCTAssertNotNil(files)
|
XCTAssertNotNil(files)
|
||||||
XCTAssertNil(error)
|
XCTAssertNil(error)
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
|
@ -511,7 +552,35 @@ final class WebDAVTests: XCTestCase {
|
||||||
XCTAssertNoThrow(try webDAV.deleteCachedData(forItemAtPath: imagePath, account: account))
|
XCTAssertNoThrow(try webDAV.deleteCachedData(forItemAtPath: imagePath, account: account))
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: Image Cache
|
func testCleanupDiskCacheKeepingThumbnail() {
|
||||||
|
guard let (account, password) = getAccount() else { return XCTFail() }
|
||||||
|
guard let imagePath = ProcessInfo.processInfo.environment["image_path"] else {
|
||||||
|
return XCTFail("You need to set the image_path in the environment.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let expectation = XCTestExpectation(description: "List files from WebDAV")
|
||||||
|
|
||||||
|
// Download Thumbnail
|
||||||
|
downloadThumbnail(imagePath: imagePath, account: account, password: password)
|
||||||
|
let cachedThumbnailURL = webDAV.cachedThumbnailURL(forItemAtPath: imagePath, account: account, with: .default)!
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: cachedThumbnailURL.path))
|
||||||
|
|
||||||
|
// List files
|
||||||
|
|
||||||
|
let imageURL = URL(fileURLWithPath: imagePath, isDirectory: false)
|
||||||
|
let directory = imageURL.deletingLastPathComponent()
|
||||||
|
|
||||||
|
webDAV.listFiles(atPath: directory.path, account: account, password: password, caching: .doNotReturnCachedResult) { _, _ in
|
||||||
|
expectation.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(for: [expectation], timeout: 10.0)
|
||||||
|
|
||||||
|
// Check that the cached thumbnail still exists
|
||||||
|
XCTAssert(FileManager.default.fileExists(atPath: cachedThumbnailURL.path))
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: Images
|
||||||
|
|
||||||
func testDownloadImage() {
|
func testDownloadImage() {
|
||||||
guard let (account, password) = getAccount() else { return XCTFail() }
|
guard let (account, password) = getAccount() else { return XCTFail() }
|
||||||
|
@ -628,6 +697,34 @@ final class WebDAVTests: XCTestCase {
|
||||||
XCTAssertFalse(FileManager.default.fileExists(atPath: cachedThumbnailFitURL.path))
|
XCTAssertFalse(FileManager.default.fileExists(atPath: cachedThumbnailFitURL.path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testThumbnailPlaceholder() {
|
||||||
|
guard let (account, password) = getAccount() else { return XCTFail() }
|
||||||
|
guard let imagePath = ProcessInfo.processInfo.environment["image_path"] else {
|
||||||
|
return XCTFail("You need to set the image_path in the environment.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let thumbnailExpectation = XCTestExpectation(description: "Get the cached thumbnail")
|
||||||
|
let imageExpectation = XCTestExpectation(description: "Fetch image")
|
||||||
|
|
||||||
|
downloadThumbnail(imagePath: imagePath, account: account, password: password)
|
||||||
|
|
||||||
|
try? webDAV.deleteCachedData(forItemAtPath: imagePath, account: account)
|
||||||
|
webDAV.downloadImage(path: imagePath, account: account, password: password, preview: .memoryOnly) { image, error in
|
||||||
|
switch error {
|
||||||
|
case .placeholder:
|
||||||
|
thumbnailExpectation.fulfill()
|
||||||
|
case .none:
|
||||||
|
imageExpectation.fulfill()
|
||||||
|
case .some(let unexpectedError):
|
||||||
|
XCTFail("\(unexpectedError)")
|
||||||
|
}
|
||||||
|
XCTAssertNotNil(image)
|
||||||
|
}
|
||||||
|
try? webDAV.deleteCachedData(forItemAtPath: imagePath, account: account)
|
||||||
|
|
||||||
|
wait(for: [thumbnailExpectation, imageExpectation], timeout: 10.0)
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: OCS
|
//MARK: OCS
|
||||||
|
|
||||||
func testColorHex() {
|
func testColorHex() {
|
||||||
|
@ -691,11 +788,17 @@ final class WebDAVTests: XCTestCase {
|
||||||
wait(for: [expectation], timeout: 10.0)
|
wait(for: [expectation], timeout: 10.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func uploadData(account: SimpleAccount, password: String) -> (name: String, fileName: String, content: String) {
|
@discardableResult
|
||||||
|
private func uploadData(to folder: String = "", account: SimpleAccount, password: String) -> (name: String, fileName: String, content: String) {
|
||||||
let expectation = XCTestExpectation(description: "Upload data")
|
let expectation = XCTestExpectation(description: "Upload data")
|
||||||
|
|
||||||
let name = UUID().uuidString
|
let name = UUID().uuidString
|
||||||
let fileName = name + ".txt"
|
let fileName: String
|
||||||
|
if folder.isEmpty {
|
||||||
|
fileName = name + ".txt"
|
||||||
|
} else {
|
||||||
|
fileName = "\(folder)/\(name).txt"
|
||||||
|
}
|
||||||
let content = UUID().uuidString
|
let content = UUID().uuidString
|
||||||
let data = content.data(using: .utf8)!
|
let data = content.data(using: .utf8)!
|
||||||
|
|
||||||
|
@ -735,6 +838,21 @@ final class WebDAVTests: XCTestCase {
|
||||||
return folder
|
return folder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func downloadData(path file: String, account: SimpleAccount, password: String) {
|
||||||
|
let expectation = XCTestExpectation(description: "Download file from WebDAV")
|
||||||
|
|
||||||
|
try? webDAV.deleteCachedData(forItemAtPath: file, account: account)
|
||||||
|
|
||||||
|
webDAV.download(fileAtPath: file, account: account, password: password) { image, error in
|
||||||
|
XCTAssertNil(error)
|
||||||
|
XCTAssertNotNil(image)
|
||||||
|
|
||||||
|
expectation.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(for: [expectation], timeout: 10.0)
|
||||||
|
}
|
||||||
|
|
||||||
private func downloadImage(imagePath: String, account: SimpleAccount, password: String) {
|
private func downloadImage(imagePath: String, account: SimpleAccount, password: String) {
|
||||||
let expectation = XCTestExpectation(description: "Download image from WebDAV")
|
let expectation = XCTestExpectation(description: "Download image from WebDAV")
|
||||||
|
|
||||||
|
@ -788,7 +906,7 @@ final class WebDAVTests: XCTestCase {
|
||||||
("testCleanupFilesCacheRoot", testCleanupFilesCacheRoot),
|
("testCleanupFilesCacheRoot", testCleanupFilesCacheRoot),
|
||||||
("testCleanupFilesCacheSubdirectory", testCleanupFilesCacheSubdirectory),
|
("testCleanupFilesCacheSubdirectory", testCleanupFilesCacheSubdirectory),
|
||||||
// Disk Cache Cleanup
|
// Disk Cache Cleanup
|
||||||
("testCleanupDiskCacheFile", testCleanupDiskCacheFile),
|
("testCleanupDiskCacheFileRoot", testCleanupDiskCacheFileRoot),
|
||||||
("testCleanupDiskCacheFolder", testCleanupDiskCacheFolder),
|
("testCleanupDiskCacheFolder", testCleanupDiskCacheFolder),
|
||||||
("testCleanupDiskCacheWithGoodFile", testCleanupDiskCacheWithGoodFile),
|
("testCleanupDiskCacheWithGoodFile", testCleanupDiskCacheWithGoodFile),
|
||||||
// Image Cache
|
// Image Cache
|
||||||
|
@ -800,6 +918,7 @@ final class WebDAVTests: XCTestCase {
|
||||||
("testThumbnailCacheURL", testThumbnailCacheURL),
|
("testThumbnailCacheURL", testThumbnailCacheURL),
|
||||||
("testSpecificThumbnailCache", testSpecificThumbnailCache),
|
("testSpecificThumbnailCache", testSpecificThumbnailCache),
|
||||||
("testGeneralThumbnailCache", testGeneralThumbnailCache),
|
("testGeneralThumbnailCache", testGeneralThumbnailCache),
|
||||||
|
("testThumbnailPlaceholder", testThumbnailPlaceholder),
|
||||||
// OCS
|
// OCS
|
||||||
("testTheme", testTheme),
|
("testTheme", testTheme),
|
||||||
("testColorHex", testColorHex)
|
("testColorHex", testColorHex)
|
||||||
|
|
Loading…
Reference in New Issue