Skip to content

Commit

Permalink
feat: add ParseFile caching using download folder (#416)
Browse files Browse the repository at this point in the history
* deprecate ParseCloud for ParseCloudable

* update project

* more pros updates

* add ParseFileManager helpers

* add caching and tests
  • Loading branch information
cbaker6 authored Sep 15, 2022
1 parent e54a22c commit 085f5ad
Show file tree
Hide file tree
Showing 33 changed files with 513 additions and 272 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Parse-Swift Changelog

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

### 4.14.0
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.13.1...4.14.0)

__New features__
- 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).

### 4.13.1
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.13.0...4.13.1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,38 @@ import ParseSwift
PlaygroundPage.current.needsIndefiniteExecution = true
initializeParse()

//: Create your own value typed `ParseCloud` type.
struct Hello: ParseCloud {
//: Create your own value typed `ParseCloudable` type.
struct Hello: ParseCloudable {

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

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

//: Create another `ParseCloud` type.
struct TestCloudCode: ParseCloud {
//: Create another `ParseCloudable` type.
struct TestCloudCode: ParseCloudable {

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

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

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

//: Create another `ParseCloud` type.
struct TestCloudCodeError: ParseCloud {
//: Create another `ParseCloudable` type.
struct TestCloudCodeError: ParseCloudable {

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

//: These are required by `ParseCloud`, you can set the default value to make it easier
//: These are required by `ParseCloudable`, you can set the default value to make it easier
//: to use.
var functionJobName: String = "testCloudCodeError"
}
Expand Down
118 changes: 63 additions & 55 deletions ParseSwift.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1210"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1210"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1210"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1210"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
6 changes: 2 additions & 4 deletions Sources/ParseSwift/API/API+Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,10 @@ internal extension API.Command {
parseURL: object.url,
otherURL: object.cloudURL) { (data) -> ParseFile in
let tempFileLocation = try ParseCoding.jsonDecoder().decode(URL.self, from: data)
guard let fileManager = ParseFileManager(),
let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else {
guard let fileManager = ParseFileManager() else {
throw ParseError(code: .unknownError, message: "Cannot create fileManager")
}
let downloadDirectoryPath = defaultDirectoryPath
.appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true)
let downloadDirectoryPath = try ParseFileManager.downloadDirectory()
try fileManager.createDirectoryIfNeeded(downloadDirectoryPath.relativePath)
let fileNameURL = URL(fileURLWithPath: object.name)
let fileLocation = downloadDirectoryPath.appendingPathComponent(fileNameURL.lastPathComponent)
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal func initialize(applicationId: String,
usingTransactions: Bool = false,
usingEqualQueryConstraint: Bool = false,
usingPostForQuery: Bool = false,
keyValueStore: ParseKeyValueStore? = nil,
keyValueStore: ParsePrimitiveStorable? = nil,
requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
cacheMemoryCapacity: Int = 512_000,
cacheDiskCapacity: Int = 10_000_000,
Expand Down Expand Up @@ -230,7 +230,7 @@ public func initialize(
usingTransactions: Bool = false,
usingEqualQueryConstraint: Bool = false,
usingPostForQuery: Bool = false,
keyValueStore: ParseKeyValueStore? = nil,
keyValueStore: ParsePrimitiveStorable? = nil,
requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy,
cacheMemoryCapacity: Int = 512_000,
cacheDiskCapacity: Int = 10_000_000,
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/ParseConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

enum ParseConstants {
static let sdk = "swift"
static let version = "4.13.1"
static let version = "4.14.0"
static let fileManagementDirectory = "parse/"
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
static let fileManagementLibraryDirectory = "Library/"
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Protocols/CloudObservable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Foundation
public protocol CloudObservable: ObservableObject {

/// The `ParseObject` associated with this view model.
associatedtype CloudCodeType: ParseCloud
associatedtype CloudCodeType: ParseCloudable

/**
Creates a new view model that can be used to handle updates.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// ParseCloud+async.swift
// ParseCloud+async
// ParseCloudable+async.swift
// ParseCloudable+async
//
// Created by Corey Baker on 8/6/21.
// Copyright © 2021 Parse Community. All rights reserved.
Expand All @@ -9,7 +9,7 @@
#if compiler(>=5.5.2) && canImport(_Concurrency)
import Foundation

public extension ParseCloud {
public extension ParseCloudable {

// MARK: Aysnc/Await

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// ParseCloud+combine.swift
// ParseCloudable+combine.swift
// ParseSwift
//
// Created by Corey Baker on 1/29/21.
Expand All @@ -10,7 +10,7 @@
import Foundation
import Combine

public extension ParseCloud {
public extension ParseCloudable {

// MARK: Combine

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// ParseCloud.swift
// ParseCloudable.swift
// ParseSwift
//
// Created by Corey Baker on 12/29/20.
Expand All @@ -15,7 +15,15 @@ public protocol ParseCloudTypeable: ParseEncodable {}
An object should be instantiated for each function and job type. When conforming to
`ParseCloud`, any properties added will be passed as parameters to your Cloud Function or Job.
*/
public protocol ParseCloud: ParseCloudTypeable, Hashable {
@available(*, deprecated, renamed: "ParseCloudable")
public typealias ParseCloud = ParseCloudable

/**
Objects that conform to the `ParseCloudable` protocol are able to call Parse Cloud Functions and Jobs.
An object should be instantiated for each function and job type. When conforming to
`ParseCloudable`, any properties added will be passed as parameters to your Cloud Function or Job.
*/
public protocol ParseCloudable: ParseCloudTypeable, Hashable {

associatedtype ReturnType: Decodable
/**
Expand All @@ -26,7 +34,7 @@ public protocol ParseCloud: ParseCloudTypeable, Hashable {
}

// MARK: Functions
extension ParseCloud {
extension ParseCloudable {

/**
Calls a Cloud Code function *synchronously* and returns a result of it is execution.
Expand Down Expand Up @@ -65,7 +73,7 @@ extension ParseCloud {
}

// MARK: Jobs
extension ParseCloud {
extension ParseCloudable {
/**
Starts a Cloud Code Job *synchronously* and returns a result with the jobStatusId of the job.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
Expand Down
63 changes: 55 additions & 8 deletions Sources/ParseSwift/Storage/ParseFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,6 @@ public struct ParseFileManager {
#endif
}

func dataItemPathForPathComponent(_ component: String) -> URL? {
guard var path = self.defaultDataDirectoryPath else {
return nil
}
path.appendPathComponent(component)
return path
}

/// Creates an instance of `ParseFileManager`.
/// - returns: If an instance cannot be created, nil is returned.
public init?() {
Expand All @@ -100,6 +92,17 @@ public struct ParseFileManager {

applicationGroupIdentifer = nil
}
}

// MARK: Helper Methods (Internal)
extension ParseFileManager {
func dataItemPathForPathComponent(_ component: String) -> URL? {
guard var path = self.defaultDataDirectoryPath else {
return nil
}
path.appendPathComponent(component)
return path
}

func createDirectoryIfNeeded(_ path: String) throws {
if !FileManager.default.fileExists(atPath: path) {
Expand Down Expand Up @@ -206,3 +209,47 @@ public struct ParseFileManager {
}
}
}

// MARK: Helper Methods (External)
public extension ParseFileManager {

/**
The download directory for all `ParseFile`'s.
- returns: The download directory.
- throws: An error of type `ParseError`.
*/
static func downloadDirectory() throws -> URL {
guard let fileManager = ParseFileManager(),
let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else {
throw ParseError(code: .unknownError, message: "Cannot create ParseFileManager")
}
return defaultDirectoryPath
.appendingPathComponent(ParseConstants.fileDownloadsDirectory,
isDirectory: true)
}

/**
Check if a file exists in the Swift SDK download directory.
- parameter name: The name of the file to check.
- returns: The location of the file.
- throws: An error of type `ParseError`.
*/
static func fileExists(_ name: String) throws -> URL {
let fileName = URL(fileURLWithPath: name).lastPathComponent
let fileLocation = try downloadDirectory().appendingPathComponent(fileName).relativePath
guard FileManager.default.fileExists(atPath: fileLocation) else {
throw ParseError(code: .unknownError, message: "File does not exist")
}
return URL(fileURLWithPath: fileLocation, isDirectory: false)
}

/**
Check if a `ParseFile` exists in the Swift SDK download directory.
- parameter file: The `ParseFile` to check.
- returns: The location of the file.
- throws: An error of type `ParseError`.
*/
static func fileExists(_ file: ParseFile) throws -> URL {
try fileExists(file.name)
}
}
15 changes: 11 additions & 4 deletions Sources/ParseSwift/Storage/ParseKeyValueStore.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// ParseKeyValueStore.swift
// ParsePrimitiveStorable.swift
//
//
// Created by Pranjal Satija on 7/19/20.
Expand All @@ -11,7 +11,14 @@ import Foundation
A store that supports key/value storage. It should be able
to handle any object that conforms to encodable and decodable.
*/
public protocol ParseKeyValueStore {
@available(*, deprecated, renamed: "ParsePrimitiveStorable")
public typealias ParseKeyValueStore = ParsePrimitiveStorable

/**
A store that supports key/value storage. It should be able
to handle any object that conforms to encodable and decodable.
*/
public protocol ParsePrimitiveStorable {
/// Delete an object from the store.
/// - parameter key: The unique key value of the object.
mutating func delete(valueFor key: String) throws
Expand All @@ -31,7 +38,7 @@ public protocol ParseKeyValueStore {
/// A `ParseKeyValueStore` that lives in memory for unit testing purposes.
/// It works by encoding / decoding all values just like a real `Codable` store would
/// but it stores all values as `Data` blobs in memory.
struct InMemoryKeyValueStore: ParseKeyValueStore {
struct InMemoryKeyValueStore: ParsePrimitiveStorable {
var decoder = ParseCoding.jsonDecoder()
var encoder = ParseCoding.jsonEncoder()
var storage = [String: Data]()
Expand All @@ -58,7 +65,7 @@ struct InMemoryKeyValueStore: ParseKeyValueStore {
#if !os(Linux) && !os(Android) && !os(Windows)

// MARK: KeychainStore + ParseKeyValueStore
extension KeychainStore: ParseKeyValueStore {
extension KeychainStore: ParsePrimitiveStorable {

func delete(valueFor key: String) throws {
if !removeObject(forKey: key) {
Expand Down
6 changes: 3 additions & 3 deletions Sources/ParseSwift/Storage/ParseStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
struct ParseStorage {
public static var shared = ParseStorage()

private var backingStore: ParseKeyValueStore!
private var backingStore: ParsePrimitiveStorable!

mutating func use(_ store: ParseKeyValueStore) {
mutating func use(_ store: ParsePrimitiveStorable) {
self.backingStore = store
}

Expand All @@ -36,7 +36,7 @@ struct ParseStorage {
}

// MARK: ParseKeyValueStore
extension ParseStorage: ParseKeyValueStore {
extension ParseStorage: ParsePrimitiveStorable {
public mutating func delete(valueFor key: String) throws {
requireBackingStore()
return try backingStore.delete(valueFor: key)
Expand Down
Loading

0 comments on commit 085f5ad

Please sign in to comment.