Skip to content

Commit 085f5ad

Browse files
authored
feat: add ParseFile caching using download folder (#416)
* deprecate ParseCloud for ParseCloudable * update project * more pros updates * add ParseFileManager helpers * add caching and tests
1 parent e54a22c commit 085f5ad

33 files changed

+513
-272
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# Parse-Swift Changelog
22

33
### main
4-
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.13.1...main)
4+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.14.0...main)
55
* _Contributing to this repo? Add info about your change here to be included in the next release_
66

7+
### 4.14.0
8+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.13.1...4.14.0)
9+
10+
__New features__
11+
- Add file caching using the Parse download folder ([#416](https://github.com/parse-community/Parse-Swift/pull/416)), thanks to [Corey Baker](https://github.com/cbaker6).
12+
713
### 4.13.1
814
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.13.0...4.13.1)
915

ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,38 +13,38 @@ import ParseSwift
1313
PlaygroundPage.current.needsIndefiniteExecution = true
1414
initializeParse()
1515

16-
//: Create your own value typed `ParseCloud` type.
17-
struct Hello: ParseCloud {
16+
//: Create your own value typed `ParseCloudable` type.
17+
struct Hello: ParseCloudable {
1818

1919
//: Return type of your Cloud Function
2020
typealias ReturnType = String
2121

22-
//: These are required by `ParseCloud`, you can set the default value to make it easier
22+
//: These are required by `ParseCloudable`, you can set the default value to make it easier
2323
//: to use.
2424
var functionJobName: String = "hello"
2525
}
2626

27-
//: Create another `ParseCloud` type.
28-
struct TestCloudCode: ParseCloud {
27+
//: Create another `ParseCloudable` type.
28+
struct TestCloudCode: ParseCloudable {
2929

3030
//: Return type of your Cloud Function
3131
typealias ReturnType = [String: Int]
3232

33-
//: These are required by `ParseCloud`, you can set the default value to make it easier
33+
//: These are required by `ParseCloudable`, you can set the default value to make it easier
3434
//: to use.
3535
var functionJobName: String = "testCloudCode"
3636

3737
//: If your cloud function takes arguments, they can be passed by creating properties:
3838
var argument1: [String: Int]
3939
}
4040

41-
//: Create another `ParseCloud` type.
42-
struct TestCloudCodeError: ParseCloud {
41+
//: Create another `ParseCloudable` type.
42+
struct TestCloudCodeError: ParseCloudable {
4343

4444
//: Return type of your Cloud Function
4545
typealias ReturnType = String
4646

47-
//: These are required by `ParseCloud`, you can set the default value to make it easier
47+
//: These are required by `ParseCloudable`, you can set the default value to make it easier
4848
//: to use.
4949
var functionJobName: String = "testCloudCodeError"
5050
}

ParseSwift.xcodeproj/project.pbxproj

Lines changed: 63 additions & 55 deletions
Large diffs are not rendered by default.

ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (iOS).xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1210"
3+
LastUpgradeVersion = "1400"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (macOS).xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1210"
3+
LastUpgradeVersion = "1400"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (tvOS).xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1210"
3+
LastUpgradeVersion = "1400"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (watchOS).xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1210"
3+
LastUpgradeVersion = "1400"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

ParseSwift.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1300"
3+
LastUpgradeVersion = "1400"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

ParseSwift.xcodeproj/xcshareddata/xcschemes/TestHostTV.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1300"
3+
LastUpgradeVersion = "1400"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Sources/ParseSwift/API/API+Command.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,10 @@ internal extension API.Command {
358358
parseURL: object.url,
359359
otherURL: object.cloudURL) { (data) -> ParseFile in
360360
let tempFileLocation = try ParseCoding.jsonDecoder().decode(URL.self, from: data)
361-
guard let fileManager = ParseFileManager(),
362-
let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else {
361+
guard let fileManager = ParseFileManager() else {
363362
throw ParseError(code: .unknownError, message: "Cannot create fileManager")
364363
}
365-
let downloadDirectoryPath = defaultDirectoryPath
366-
.appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true)
364+
let downloadDirectoryPath = try ParseFileManager.downloadDirectory()
367365
try fileManager.createDirectoryIfNeeded(downloadDirectoryPath.relativePath)
368366
let fileNameURL = URL(fileURLWithPath: object.name)
369367
let fileLocation = downloadDirectoryPath.appendingPathComponent(fileNameURL.lastPathComponent)

Sources/ParseSwift/Parse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ internal func initialize(applicationId: String,
2121
usingTransactions: Bool = false,
2222
usingEqualQueryConstraint: Bool = false,
2323
usingPostForQuery: Bool = false,
24-
keyValueStore: ParseKeyValueStore? = nil,
24+
keyValueStore: ParsePrimitiveStorable? = nil,
2525
requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
2626
cacheMemoryCapacity: Int = 512_000,
2727
cacheDiskCapacity: Int = 10_000_000,
@@ -230,7 +230,7 @@ public func initialize(
230230
usingTransactions: Bool = false,
231231
usingEqualQueryConstraint: Bool = false,
232232
usingPostForQuery: Bool = false,
233-
keyValueStore: ParseKeyValueStore? = nil,
233+
keyValueStore: ParsePrimitiveStorable? = nil,
234234
requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
235235
cacheMemoryCapacity: Int = 512_000,
236236
cacheDiskCapacity: Int = 10_000_000,

Sources/ParseSwift/ParseConstants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010

1111
enum ParseConstants {
1212
static let sdk = "swift"
13-
static let version = "4.13.1"
13+
static let version = "4.14.0"
1414
static let fileManagementDirectory = "parse/"
1515
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
1616
static let fileManagementLibraryDirectory = "Library/"

Sources/ParseSwift/Protocols/CloudObservable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Foundation
1515
public protocol CloudObservable: ObservableObject {
1616

1717
/// The `ParseObject` associated with this view model.
18-
associatedtype CloudCodeType: ParseCloud
18+
associatedtype CloudCodeType: ParseCloudable
1919

2020
/**
2121
Creates a new view model that can be used to handle updates.

Sources/ParseSwift/Protocols/ParseCloud+async.swift renamed to Sources/ParseSwift/Protocols/ParseCloudable+async.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
2-
// ParseCloud+async.swift
3-
// ParseCloud+async
2+
// ParseCloudable+async.swift
3+
// ParseCloudable+async
44
//
55
// Created by Corey Baker on 8/6/21.
66
// Copyright © 2021 Parse Community. All rights reserved.
@@ -9,7 +9,7 @@
99
#if compiler(>=5.5.2) && canImport(_Concurrency)
1010
import Foundation
1111

12-
public extension ParseCloud {
12+
public extension ParseCloudable {
1313

1414
// MARK: Aysnc/Await
1515

Sources/ParseSwift/Protocols/ParseCloud+combine.swift renamed to Sources/ParseSwift/Protocols/ParseCloudable+combine.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// ParseCloud+combine.swift
2+
// ParseCloudable+combine.swift
33
// ParseSwift
44
//
55
// Created by Corey Baker on 1/29/21.
@@ -10,7 +10,7 @@
1010
import Foundation
1111
import Combine
1212

13-
public extension ParseCloud {
13+
public extension ParseCloudable {
1414

1515
// MARK: Combine
1616

Sources/ParseSwift/Protocols/ParseCloud.swift renamed to Sources/ParseSwift/Protocols/ParseCloudable.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// ParseCloud.swift
2+
// ParseCloudable.swift
33
// ParseSwift
44
//
55
// Created by Corey Baker on 12/29/20.
@@ -15,7 +15,15 @@ public protocol ParseCloudTypeable: ParseEncodable {}
1515
An object should be instantiated for each function and job type. When conforming to
1616
`ParseCloud`, any properties added will be passed as parameters to your Cloud Function or Job.
1717
*/
18-
public protocol ParseCloud: ParseCloudTypeable, Hashable {
18+
@available(*, deprecated, renamed: "ParseCloudable")
19+
public typealias ParseCloud = ParseCloudable
20+
21+
/**
22+
Objects that conform to the `ParseCloudable` protocol are able to call Parse Cloud Functions and Jobs.
23+
An object should be instantiated for each function and job type. When conforming to
24+
`ParseCloudable`, any properties added will be passed as parameters to your Cloud Function or Job.
25+
*/
26+
public protocol ParseCloudable: ParseCloudTypeable, Hashable {
1927

2028
associatedtype ReturnType: Decodable
2129
/**
@@ -26,7 +34,7 @@ public protocol ParseCloud: ParseCloudTypeable, Hashable {
2634
}
2735

2836
// MARK: Functions
29-
extension ParseCloud {
37+
extension ParseCloudable {
3038

3139
/**
3240
Calls a Cloud Code function *synchronously* and returns a result of it is execution.
@@ -65,7 +73,7 @@ extension ParseCloud {
6573
}
6674

6775
// MARK: Jobs
68-
extension ParseCloud {
76+
extension ParseCloudable {
6977
/**
7078
Starts a Cloud Code Job *synchronously* and returns a result with the jobStatusId of the job.
7179
- parameter options: A set of header options sent to the server. Defaults to an empty set.

Sources/ParseSwift/Storage/ParseFileManager.swift

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,6 @@ public struct ParseFileManager {
7676
#endif
7777
}
7878

79-
func dataItemPathForPathComponent(_ component: String) -> URL? {
80-
guard var path = self.defaultDataDirectoryPath else {
81-
return nil
82-
}
83-
path.appendPathComponent(component)
84-
return path
85-
}
86-
8779
/// Creates an instance of `ParseFileManager`.
8880
/// - returns: If an instance cannot be created, nil is returned.
8981
public init?() {
@@ -100,6 +92,17 @@ public struct ParseFileManager {
10092

10193
applicationGroupIdentifer = nil
10294
}
95+
}
96+
97+
// MARK: Helper Methods (Internal)
98+
extension ParseFileManager {
99+
func dataItemPathForPathComponent(_ component: String) -> URL? {
100+
guard var path = self.defaultDataDirectoryPath else {
101+
return nil
102+
}
103+
path.appendPathComponent(component)
104+
return path
105+
}
103106

104107
func createDirectoryIfNeeded(_ path: String) throws {
105108
if !FileManager.default.fileExists(atPath: path) {
@@ -206,3 +209,47 @@ public struct ParseFileManager {
206209
}
207210
}
208211
}
212+
213+
// MARK: Helper Methods (External)
214+
public extension ParseFileManager {
215+
216+
/**
217+
The download directory for all `ParseFile`'s.
218+
- returns: The download directory.
219+
- throws: An error of type `ParseError`.
220+
*/
221+
static func downloadDirectory() throws -> URL {
222+
guard let fileManager = ParseFileManager(),
223+
let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else {
224+
throw ParseError(code: .unknownError, message: "Cannot create ParseFileManager")
225+
}
226+
return defaultDirectoryPath
227+
.appendingPathComponent(ParseConstants.fileDownloadsDirectory,
228+
isDirectory: true)
229+
}
230+
231+
/**
232+
Check if a file exists in the Swift SDK download directory.
233+
- parameter name: The name of the file to check.
234+
- returns: The location of the file.
235+
- throws: An error of type `ParseError`.
236+
*/
237+
static func fileExists(_ name: String) throws -> URL {
238+
let fileName = URL(fileURLWithPath: name).lastPathComponent
239+
let fileLocation = try downloadDirectory().appendingPathComponent(fileName).relativePath
240+
guard FileManager.default.fileExists(atPath: fileLocation) else {
241+
throw ParseError(code: .unknownError, message: "File does not exist")
242+
}
243+
return URL(fileURLWithPath: fileLocation, isDirectory: false)
244+
}
245+
246+
/**
247+
Check if a `ParseFile` exists in the Swift SDK download directory.
248+
- parameter file: The `ParseFile` to check.
249+
- returns: The location of the file.
250+
- throws: An error of type `ParseError`.
251+
*/
252+
static func fileExists(_ file: ParseFile) throws -> URL {
253+
try fileExists(file.name)
254+
}
255+
}

Sources/ParseSwift/Storage/ParseKeyValueStore.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// ParseKeyValueStore.swift
2+
// ParsePrimitiveStorable.swift
33
//
44
//
55
// Created by Pranjal Satija on 7/19/20.
@@ -11,7 +11,14 @@ import Foundation
1111
A store that supports key/value storage. It should be able
1212
to handle any object that conforms to encodable and decodable.
1313
*/
14-
public protocol ParseKeyValueStore {
14+
@available(*, deprecated, renamed: "ParsePrimitiveStorable")
15+
public typealias ParseKeyValueStore = ParsePrimitiveStorable
16+
17+
/**
18+
A store that supports key/value storage. It should be able
19+
to handle any object that conforms to encodable and decodable.
20+
*/
21+
public protocol ParsePrimitiveStorable {
1522
/// Delete an object from the store.
1623
/// - parameter key: The unique key value of the object.
1724
mutating func delete(valueFor key: String) throws
@@ -31,7 +38,7 @@ public protocol ParseKeyValueStore {
3138
/// A `ParseKeyValueStore` that lives in memory for unit testing purposes.
3239
/// It works by encoding / decoding all values just like a real `Codable` store would
3340
/// but it stores all values as `Data` blobs in memory.
34-
struct InMemoryKeyValueStore: ParseKeyValueStore {
41+
struct InMemoryKeyValueStore: ParsePrimitiveStorable {
3542
var decoder = ParseCoding.jsonDecoder()
3643
var encoder = ParseCoding.jsonEncoder()
3744
var storage = [String: Data]()
@@ -58,7 +65,7 @@ struct InMemoryKeyValueStore: ParseKeyValueStore {
5865
#if !os(Linux) && !os(Android) && !os(Windows)
5966

6067
// MARK: KeychainStore + ParseKeyValueStore
61-
extension KeychainStore: ParseKeyValueStore {
68+
extension KeychainStore: ParsePrimitiveStorable {
6269

6370
func delete(valueFor key: String) throws {
6471
if !removeObject(forKey: key) {

Sources/ParseSwift/Storage/ParseStorage.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
struct ParseStorage {
1010
public static var shared = ParseStorage()
1111

12-
private var backingStore: ParseKeyValueStore!
12+
private var backingStore: ParsePrimitiveStorable!
1313

14-
mutating func use(_ store: ParseKeyValueStore) {
14+
mutating func use(_ store: ParsePrimitiveStorable) {
1515
self.backingStore = store
1616
}
1717

@@ -36,7 +36,7 @@ struct ParseStorage {
3636
}
3737

3838
// MARK: ParseKeyValueStore
39-
extension ParseStorage: ParseKeyValueStore {
39+
extension ParseStorage: ParsePrimitiveStorable {
4040
public mutating func delete(valueFor key: String) throws {
4141
requireBackingStore()
4242
return try backingStore.delete(valueFor: key)

0 commit comments

Comments
 (0)