diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba0445da..6f290ee7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift b/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift index 15ddd13d6..59bd967d5 100644 --- a/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift +++ b/ParseSwift.playground/Pages/10 - Cloud Code.xcplaygroundpage/Contents.swift @@ -13,24 +13,24 @@ 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" @@ -38,13 +38,13 @@ struct TestCloudCode: ParseCloud { 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" } diff --git a/ParseSwift.xcodeproj/project.pbxproj b/ParseSwift.xcodeproj/project.pbxproj index 8b2a3133d..ac7552668 100644 --- a/ParseSwift.xcodeproj/project.pbxproj +++ b/ParseSwift.xcodeproj/project.pbxproj @@ -183,10 +183,10 @@ 703B090326BD9652005A112F /* ParseAnalytics+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090126BD9652005A112F /* ParseAnalytics+async.swift */; }; 703B090426BD9652005A112F /* ParseAnalytics+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090126BD9652005A112F /* ParseAnalytics+async.swift */; }; 703B090526BD9652005A112F /* ParseAnalytics+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090126BD9652005A112F /* ParseAnalytics+async.swift */; }; - 703B090726BD9764005A112F /* ParseCloud+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloud+async.swift */; }; - 703B090826BD9764005A112F /* ParseCloud+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloud+async.swift */; }; - 703B090926BD9764005A112F /* ParseCloud+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloud+async.swift */; }; - 703B090A26BD9764005A112F /* ParseCloud+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloud+async.swift */; }; + 703B090726BD9764005A112F /* ParseCloudable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloudable+async.swift */; }; + 703B090826BD9764005A112F /* ParseCloudable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloudable+async.swift */; }; + 703B090926BD9764005A112F /* ParseCloudable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloudable+async.swift */; }; + 703B090A26BD9764005A112F /* ParseCloudable+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090626BD9764005A112F /* ParseCloudable+async.swift */; }; 703B090C26BD984D005A112F /* ParseConfig+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090B26BD984D005A112F /* ParseConfig+async.swift */; }; 703B090D26BD984D005A112F /* ParseConfig+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090B26BD984D005A112F /* ParseConfig+async.swift */; }; 703B090E26BD984D005A112F /* ParseConfig+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B090B26BD984D005A112F /* ParseConfig+async.swift */; }; @@ -252,10 +252,10 @@ 703B095E26BF481F005A112F /* ParseFacebook+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B095C26BF481F005A112F /* ParseFacebook+async.swift */; }; 703B095F26BF481F005A112F /* ParseFacebook+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B095C26BF481F005A112F /* ParseFacebook+async.swift */; }; 703B096026BF481F005A112F /* ParseFacebook+async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703B095C26BF481F005A112F /* ParseFacebook+async.swift */; }; - 7044C17525C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */; }; - 7044C17625C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */; }; - 7044C17725C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */; }; - 7044C17825C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */; }; + 7044C17525C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloudable+combine.swift */; }; + 7044C17625C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloudable+combine.swift */; }; + 7044C17725C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloudable+combine.swift */; }; + 7044C17825C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C17425C4ECFF0011F6E7 /* ParseCloudable+combine.swift */; }; 7044C18325C4EFC10011F6E7 /* ParseConfig+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C18225C4EFC10011F6E7 /* ParseConfig+combine.swift */; }; 7044C18425C4EFC10011F6E7 /* ParseConfig+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C18225C4EFC10011F6E7 /* ParseConfig+combine.swift */; }; 7044C18525C4EFC10011F6E7 /* ParseConfig+combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C18225C4EFC10011F6E7 /* ParseConfig+combine.swift */; }; @@ -291,9 +291,9 @@ 7044C20625C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C20525C5D6780011F6E7 /* ParseQueryCombineTests.swift */; }; 7044C20725C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C20525C5D6780011F6E7 /* ParseQueryCombineTests.swift */; }; 7044C20825C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C20525C5D6780011F6E7 /* ParseQueryCombineTests.swift */; }; - 7044C21325C5DE490011F6E7 /* ParseCloudCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21225C5DE490011F6E7 /* ParseCloudCombineTests.swift */; }; - 7044C21425C5DE490011F6E7 /* ParseCloudCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21225C5DE490011F6E7 /* ParseCloudCombineTests.swift */; }; - 7044C21525C5DE490011F6E7 /* ParseCloudCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21225C5DE490011F6E7 /* ParseCloudCombineTests.swift */; }; + 7044C21325C5DE490011F6E7 /* ParseCloudableCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21225C5DE490011F6E7 /* ParseCloudableCombineTests.swift */; }; + 7044C21425C5DE490011F6E7 /* ParseCloudableCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21225C5DE490011F6E7 /* ParseCloudableCombineTests.swift */; }; + 7044C21525C5DE490011F6E7 /* ParseCloudableCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21225C5DE490011F6E7 /* ParseCloudableCombineTests.swift */; }; 7044C22025C5E0160011F6E7 /* ParseConfigCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21F25C5E0160011F6E7 /* ParseConfigCombineTests.swift */; }; 7044C22125C5E0160011F6E7 /* ParseConfigCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21F25C5E0160011F6E7 /* ParseConfigCombineTests.swift */; }; 7044C22225C5E0160011F6E7 /* ParseConfigCombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7044C21F25C5E0160011F6E7 /* ParseConfigCombineTests.swift */; }; @@ -865,13 +865,13 @@ 912C9BDC24D3011F009947C3 /* ParseSwift_tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 912C9BDA24D3011F009947C3 /* ParseSwift_tvOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 912C9BE024D302B0009947C3 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EE1F254B820063D731 /* Parse.swift */; }; 912C9BFD24D302B2009947C3 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A82B7EE1F254B820063D731 /* Parse.swift */; }; - 916786E2259B7DDA00BB5B4E /* ParseCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */; }; - 916786E3259B7DDA00BB5B4E /* ParseCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */; }; - 916786E4259B7DDA00BB5B4E /* ParseCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */; }; - 916786E5259B7DDA00BB5B4E /* ParseCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */; }; - 91678706259BC5D400BB5B4E /* ParseCloudTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786EF259BC59600BB5B4E /* ParseCloudTests.swift */; }; - 91678710259BC5D600BB5B4E /* ParseCloudTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786EF259BC59600BB5B4E /* ParseCloudTests.swift */; }; - 9167871A259BC5D600BB5B4E /* ParseCloudTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786EF259BC59600BB5B4E /* ParseCloudTests.swift */; }; + 916786E2259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloudable.swift */; }; + 916786E3259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloudable.swift */; }; + 916786E4259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloudable.swift */; }; + 916786E5259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786E1259B7DDA00BB5B4E /* ParseCloudable.swift */; }; + 91678706259BC5D400BB5B4E /* ParseCloudableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786EF259BC59600BB5B4E /* ParseCloudableTests.swift */; }; + 91678710259BC5D600BB5B4E /* ParseCloudableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786EF259BC59600BB5B4E /* ParseCloudableTests.swift */; }; + 9167871A259BC5D600BB5B4E /* ParseCloudableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 916786EF259BC59600BB5B4E /* ParseCloudableTests.swift */; }; 91679D64268E596300F71809 /* ParseVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91679D63268E596300F71809 /* ParseVersion.swift */; }; 91679D65268E596300F71809 /* ParseVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91679D63268E596300F71809 /* ParseVersion.swift */; }; 91679D66268E596300F71809 /* ParseVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91679D63268E596300F71809 /* ParseVersion.swift */; }; @@ -885,9 +885,9 @@ 917BA42A2703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA4292703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift */; }; 917BA42B2703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA4292703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift */; }; 917BA42C2703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA4292703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift */; }; - 917BA42E2703E20E00F8D747 /* ParseCloudAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA42D2703E20E00F8D747 /* ParseCloudAsyncTests.swift */; }; - 917BA42F2703E20E00F8D747 /* ParseCloudAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA42D2703E20E00F8D747 /* ParseCloudAsyncTests.swift */; }; - 917BA4302703E20E00F8D747 /* ParseCloudAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA42D2703E20E00F8D747 /* ParseCloudAsyncTests.swift */; }; + 917BA42E2703E20E00F8D747 /* ParseCloudableAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA42D2703E20E00F8D747 /* ParseCloudableAsyncTests.swift */; }; + 917BA42F2703E20E00F8D747 /* ParseCloudableAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA42D2703E20E00F8D747 /* ParseCloudableAsyncTests.swift */; }; + 917BA4302703E20E00F8D747 /* ParseCloudableAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA42D2703E20E00F8D747 /* ParseCloudableAsyncTests.swift */; }; 917BA4322703E36800F8D747 /* ParseConfigAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */; }; 917BA4332703E36800F8D747 /* ParseConfigAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */; }; 917BA4342703E36800F8D747 /* ParseConfigAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */; }; @@ -1208,7 +1208,7 @@ 70386A5B25D9A4010048EC1B /* ParseLDAPCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseLDAPCombineTests.swift; sourceTree = ""; }; 703B08FC26BD953B005A112F /* ParseHealth+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseHealth+async.swift"; sourceTree = ""; }; 703B090126BD9652005A112F /* ParseAnalytics+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseAnalytics+async.swift"; sourceTree = ""; }; - 703B090626BD9764005A112F /* ParseCloud+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseCloud+async.swift"; sourceTree = ""; }; + 703B090626BD9764005A112F /* ParseCloudable+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseCloudable+async.swift"; sourceTree = ""; }; 703B090B26BD984D005A112F /* ParseConfig+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseConfig+async.swift"; sourceTree = ""; }; 703B091026BD992E005A112F /* ParseOperation+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseOperation+async.swift"; sourceTree = ""; }; 703B091526BD99BC005A112F /* ParseLiveQuery+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseLiveQuery+async.swift"; sourceTree = ""; }; @@ -1226,7 +1226,7 @@ 703B095226BF47FD005A112F /* ParseTwitter+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseTwitter+async.swift"; sourceTree = ""; }; 703B095726BF480D005A112F /* ParseFacebook+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseFacebook+combine.swift"; sourceTree = ""; }; 703B095C26BF481F005A112F /* ParseFacebook+async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseFacebook+async.swift"; sourceTree = ""; }; - 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseCloud+combine.swift"; sourceTree = ""; }; + 7044C17425C4ECFF0011F6E7 /* ParseCloudable+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseCloudable+combine.swift"; sourceTree = ""; }; 7044C18225C4EFC10011F6E7 /* ParseConfig+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseConfig+combine.swift"; sourceTree = ""; }; 7044C19025C4F5B60011F6E7 /* ParseFile+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseFile+combine.swift"; sourceTree = ""; }; 7044C19E25C4FA870011F6E7 /* ParseOperation+combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseOperation+combine.swift"; sourceTree = ""; }; @@ -1237,7 +1237,7 @@ 7044C1EB25C5CC930011F6E7 /* ParseOperationCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseOperationCombineTests.swift; sourceTree = ""; }; 7044C1F825C5CFAB0011F6E7 /* ParseFileCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileCombineTests.swift; sourceTree = ""; }; 7044C20525C5D6780011F6E7 /* ParseQueryCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseQueryCombineTests.swift; sourceTree = ""; }; - 7044C21225C5DE490011F6E7 /* ParseCloudCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudCombineTests.swift; sourceTree = ""; }; + 7044C21225C5DE490011F6E7 /* ParseCloudableCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudableCombineTests.swift; sourceTree = ""; }; 7044C21F25C5E0160011F6E7 /* ParseConfigCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigCombineTests.swift; sourceTree = ""; }; 7044C22C25C5E4E90011F6E7 /* ParseAnonymousCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAnonymousCombineTests.swift; sourceTree = ""; }; 7044C24225C5EA360011F6E7 /* ParseAppleCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAppleCombineTests.swift; sourceTree = ""; }; @@ -1408,13 +1408,13 @@ 912C9BDA24D3011F009947C3 /* ParseSwift_tvOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParseSwift_tvOS.h; sourceTree = ""; }; 912C9BDB24D3011F009947C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9158916A256A07DD0024BE9A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloud.swift; sourceTree = ""; }; - 916786EF259BC59600BB5B4E /* ParseCloudTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudTests.swift; sourceTree = ""; }; + 916786E1259B7DDA00BB5B4E /* ParseCloudable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudable.swift; sourceTree = ""; }; + 916786EF259BC59600BB5B4E /* ParseCloudableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudableTests.swift; sourceTree = ""; }; 91679D63268E596300F71809 /* ParseVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseVersion.swift; sourceTree = ""; }; 91679D68268F25EA00F71809 /* ParseVersionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseVersionTests.swift; sourceTree = ""; }; 917BA4252703DB4600F8D747 /* ParseQueryAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseQueryAsyncTests.swift; sourceTree = ""; }; 917BA4292703E03F00F8D747 /* ParseAnalyticsAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAnalyticsAsyncTests.swift; sourceTree = ""; }; - 917BA42D2703E20E00F8D747 /* ParseCloudAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudAsyncTests.swift; sourceTree = ""; }; + 917BA42D2703E20E00F8D747 /* ParseCloudableAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCloudableAsyncTests.swift; sourceTree = ""; }; 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigAsyncTests.swift; sourceTree = ""; }; 917BA4352703E4CB00F8D747 /* ParseFileAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileAsyncTests.swift; sourceTree = ""; }; 917BA4392703E6D800F8D747 /* ParseHealthAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseHealthAsyncTests.swift; sourceTree = ""; }; @@ -1608,9 +1608,9 @@ 7085DDB226D1EC7F0033B977 /* ParseAuthenticationCombineTests.swift */, 70A2D81E25B36A7D001BEB7D /* ParseAuthenticationTests.swift */, 91285B172698E66D0051B544 /* ParseBytesTests.swift */, - 917BA42D2703E20E00F8D747 /* ParseCloudAsyncTests.swift */, - 7044C21225C5DE490011F6E7 /* ParseCloudCombineTests.swift */, - 916786EF259BC59600BB5B4E /* ParseCloudTests.swift */, + 917BA42D2703E20E00F8D747 /* ParseCloudableAsyncTests.swift */, + 7044C21225C5DE490011F6E7 /* ParseCloudableCombineTests.swift */, + 916786EF259BC59600BB5B4E /* ParseCloudableTests.swift */, 91F346C2269B88F7005727B6 /* ParseCloudViewModelTests.swift */, 705025982842FD3B008D6624 /* ParseCLPTests.swift */, 917BA4312703E36800F8D747 /* ParseConfigAsyncTests.swift */, @@ -1801,9 +1801,9 @@ F97B45C524D9C6F200F4A88B /* Fetchable.swift */, 705A9A2E25991C1400B3547F /* Fileable.swift */, 70BC988F252A5B5C00FF3074 /* Objectable.swift */, - 916786E1259B7DDA00BB5B4E /* ParseCloud.swift */, - 703B090626BD9764005A112F /* ParseCloud+async.swift */, - 7044C17425C4ECFF0011F6E7 /* ParseCloud+combine.swift */, + 916786E1259B7DDA00BB5B4E /* ParseCloudable.swift */, + 703B090626BD9764005A112F /* ParseCloudable+async.swift */, + 7044C17425C4ECFF0011F6E7 /* ParseCloudable+combine.swift */, 70647E9B259E3A9A004C1004 /* ParseEncodable.swift */, 704E781628CFD8A00075F952 /* ParseFileTransferable.swift */, 70CE0AB6285A83B100DAEA86 /* ParseHookable.swift */, @@ -2449,7 +2449,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1210; - LastUpgradeCheck = 1210; + LastUpgradeCheck = 1400; ORGANIZATIONNAME = "Parse Community"; TargetAttributes = { 4AA807571F794242008CD551 = { @@ -2680,7 +2680,7 @@ files = ( F97B463724D9C74400F4A88B /* Responses.swift in Sources */, 7C55F9EC2860CEA6002A352D /* ParseSpotify+async.swift in Sources */, - 916786E2259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, + 916786E2259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */, 70CE0AC6285FD5A800DAEA86 /* ParseHookFunctionable+combine.swift in Sources */, 91F346B9269B766C005727B6 /* CloudViewModel.swift in Sources */, 709A148C2839A1DB00BF85E5 /* Operation.swift in Sources */, @@ -2693,7 +2693,7 @@ 7044C1C825C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, 707A3BF125B0A4F0000D215C /* ParseAuthentication.swift in Sources */, 70D1BE7325BB43EB00A42E7C /* BaseConfig.swift in Sources */, - 703B090726BD9764005A112F /* ParseCloud+async.swift in Sources */, + 703B090726BD9764005A112F /* ParseCloudable+async.swift in Sources */, 918CED592684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0625D9718C0048EC1B /* Data.swift in Sources */, F97B465F24D9C7B500F4A88B /* KeychainStore.swift in Sources */, @@ -2771,7 +2771,7 @@ 7045769326BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */, 7C55F9E72860CD6B002A352D /* ParseSpotify.swift in Sources */, 7003960925A184EF0052CB31 /* ParseLiveQuery.swift in Sources */, - 7044C17525C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */, + 7044C17525C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */, 705025B32845C302008D6624 /* ParsePushStatus.swift in Sources */, F97B45E224D9C6F200F4A88B /* AnyEncodable.swift in Sources */, 700396EA25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, @@ -2935,9 +2935,9 @@ 917BA43E2703E84000F8D747 /* ParseOperationAsyncTests.swift in Sources */, 7037DAB226384DE1005D7E62 /* TestParseEncoder.swift in Sources */, 7004C24D25B69207005E0AD9 /* ParseRoleTests.swift in Sources */, - 917BA42E2703E20E00F8D747 /* ParseCloudAsyncTests.swift in Sources */, + 917BA42E2703E20E00F8D747 /* ParseCloudableAsyncTests.swift in Sources */, 70D41D6728B0235100613510 /* MigrateObjCSDKTests.swift in Sources */, - 91678706259BC5D400BB5B4E /* ParseCloudTests.swift in Sources */, + 91678706259BC5D400BB5B4E /* ParseCloudableTests.swift in Sources */, 70386A5C25D9A4020048EC1B /* ParseLDAPCombineTests.swift in Sources */, 70D41D6B28B294C100613510 /* MigrateObjCSDKCombineTests.swift in Sources */, 70D1BD8725B8C37200A42E7C /* ParseRelationTests.swift in Sources */, @@ -2972,7 +2972,7 @@ 70F79A732639DEA000731C46 /* ParseHealthTests.swift in Sources */, 7044C22D25C5E4E90011F6E7 /* ParseAnonymousCombineTests.swift in Sources */, 9194657824F16E330070296B /* ParseACLTests.swift in Sources */, - 7044C21325C5DE490011F6E7 /* ParseCloudCombineTests.swift in Sources */, + 7044C21325C5DE490011F6E7 /* ParseCloudableCombineTests.swift in Sources */, 70A2D86B25B3ADB6001BEB7D /* ParseAnonymousTests.swift in Sources */, 7044C1EC25C5CC930011F6E7 /* ParseOperationCombineTests.swift in Sources */, 70C7DC1E24D20E530050419B /* ParseUserTests.swift in Sources */, @@ -2992,7 +2992,7 @@ files = ( F97B463824D9C74400F4A88B /* Responses.swift in Sources */, 7C55F9ED2860CEA6002A352D /* ParseSpotify+async.swift in Sources */, - 916786E3259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, + 916786E3259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */, 70CE0AC7285FD5A800DAEA86 /* ParseHookFunctionable+combine.swift in Sources */, 91F346BA269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148D2839A1DB00BF85E5 /* Operation.swift in Sources */, @@ -3005,7 +3005,7 @@ 7044C1C925C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, 707A3BF225B0A4F0000D215C /* ParseAuthentication.swift in Sources */, 70D1BE7425BB43EB00A42E7C /* BaseConfig.swift in Sources */, - 703B090826BD9764005A112F /* ParseCloud+async.swift in Sources */, + 703B090826BD9764005A112F /* ParseCloudable+async.swift in Sources */, 918CED5A2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0725D9718C0048EC1B /* Data.swift in Sources */, F97B466024D9C7B500F4A88B /* KeychainStore.swift in Sources */, @@ -3082,7 +3082,7 @@ 7045769426BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */, 7003960A25A184EF0052CB31 /* ParseLiveQuery.swift in Sources */, 7C55F9E82860CD6B002A352D /* ParseSpotify.swift in Sources */, - 7044C17625C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */, + 7044C17625C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */, F97B45E324D9C6F200F4A88B /* AnyEncodable.swift in Sources */, 705025B42845C302008D6624 /* ParsePushStatus.swift in Sources */, 700396EB25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, @@ -3256,9 +3256,9 @@ 917BA4402703E84000F8D747 /* ParseOperationAsyncTests.swift in Sources */, 7037DAB426384DE1005D7E62 /* TestParseEncoder.swift in Sources */, 7004C26125B6920B005E0AD9 /* ParseRoleTests.swift in Sources */, - 917BA4302703E20E00F8D747 /* ParseCloudAsyncTests.swift in Sources */, + 917BA4302703E20E00F8D747 /* ParseCloudableAsyncTests.swift in Sources */, 70D41D6928B0235100613510 /* MigrateObjCSDKTests.swift in Sources */, - 9167871A259BC5D600BB5B4E /* ParseCloudTests.swift in Sources */, + 9167871A259BC5D600BB5B4E /* ParseCloudableTests.swift in Sources */, 70386A5E25D9A4020048EC1B /* ParseLDAPCombineTests.swift in Sources */, 70D41D6D28B294C100613510 /* MigrateObjCSDKCombineTests.swift in Sources */, 70D1BD8925B8C37200A42E7C /* ParseRelationTests.swift in Sources */, @@ -3293,7 +3293,7 @@ 70F79A872639DEA200731C46 /* ParseHealthTests.swift in Sources */, 7044C22F25C5E4E90011F6E7 /* ParseAnonymousCombineTests.swift in Sources */, 709B985A2556ECAA00507778 /* ParseObjectBatchTests.swift in Sources */, - 7044C21525C5DE490011F6E7 /* ParseCloudCombineTests.swift in Sources */, + 7044C21525C5DE490011F6E7 /* ParseCloudableCombineTests.swift in Sources */, 70A2D86D25B3ADB6001BEB7D /* ParseAnonymousTests.swift in Sources */, 7044C1EE25C5CC930011F6E7 /* ParseOperationCombineTests.swift in Sources */, 709B98582556ECAA00507778 /* AnyEncodableTests.swift in Sources */, @@ -3380,9 +3380,9 @@ 917BA43F2703E84000F8D747 /* ParseOperationAsyncTests.swift in Sources */, 7037DAB326384DE1005D7E62 /* TestParseEncoder.swift in Sources */, 7004C25725B6920A005E0AD9 /* ParseRoleTests.swift in Sources */, - 917BA42F2703E20E00F8D747 /* ParseCloudAsyncTests.swift in Sources */, + 917BA42F2703E20E00F8D747 /* ParseCloudableAsyncTests.swift in Sources */, 70D41D6828B0235100613510 /* MigrateObjCSDKTests.swift in Sources */, - 91678710259BC5D600BB5B4E /* ParseCloudTests.swift in Sources */, + 91678710259BC5D600BB5B4E /* ParseCloudableTests.swift in Sources */, 70386A5D25D9A4020048EC1B /* ParseLDAPCombineTests.swift in Sources */, 70D41D6C28B294C100613510 /* MigrateObjCSDKCombineTests.swift in Sources */, 70D1BD8825B8C37200A42E7C /* ParseRelationTests.swift in Sources */, @@ -3417,7 +3417,7 @@ 70F79A7D2639DEA100731C46 /* ParseHealthTests.swift in Sources */, 7044C22E25C5E4E90011F6E7 /* ParseAnonymousCombineTests.swift in Sources */, 70F2E2B4254F283000B2EA5C /* ParseQueryTests.swift in Sources */, - 7044C21425C5DE490011F6E7 /* ParseCloudCombineTests.swift in Sources */, + 7044C21425C5DE490011F6E7 /* ParseCloudableCombineTests.swift in Sources */, 70A2D86C25B3ADB6001BEB7D /* ParseAnonymousTests.swift in Sources */, 7044C1ED25C5CC930011F6E7 /* ParseOperationCombineTests.swift in Sources */, 70F2E2BA254F283000B2EA5C /* ParseInstallationTests.swift in Sources */, @@ -3437,7 +3437,7 @@ files = ( F97B45D524D9C6F200F4A88B /* AnyDecodable.swift in Sources */, 7C55F9EF2860CEA6002A352D /* ParseSpotify+async.swift in Sources */, - 916786E5259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, + 916786E5259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */, 70CE0AC9285FD5A800DAEA86 /* ParseHookFunctionable+combine.swift in Sources */, 91F346BC269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148F2839A1DB00BF85E5 /* Operation.swift in Sources */, @@ -3451,7 +3451,7 @@ 7044C1CB25C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, 707A3BF425B0A4F0000D215C /* ParseAuthentication.swift in Sources */, 70D1BE7625BB43EB00A42E7C /* BaseConfig.swift in Sources */, - 703B090A26BD9764005A112F /* ParseCloud+async.swift in Sources */, + 703B090A26BD9764005A112F /* ParseCloudable+async.swift in Sources */, 918CED5C2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0925D9718C0048EC1B /* Data.swift in Sources */, 70B4E0C42762F313004C9757 /* QueryWhere.swift in Sources */, @@ -3528,7 +3528,7 @@ 7045769626BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */, 7C55F9EA2860CD6B002A352D /* ParseSpotify.swift in Sources */, 7003960C25A184EF0052CB31 /* ParseLiveQuery.swift in Sources */, - 7044C17825C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */, + 7044C17825C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */, 705025B62845C302008D6624 /* ParsePushStatus.swift in Sources */, 700396ED25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, 9116F67226A35D620082F6D6 /* URLCache.swift in Sources */, @@ -3625,7 +3625,7 @@ files = ( F97B45D424D9C6F200F4A88B /* AnyDecodable.swift in Sources */, 7C55F9EE2860CEA6002A352D /* ParseSpotify+async.swift in Sources */, - 916786E4259B7DDA00BB5B4E /* ParseCloud.swift in Sources */, + 916786E4259B7DDA00BB5B4E /* ParseCloudable.swift in Sources */, 70CE0AC8285FD5A800DAEA86 /* ParseHookFunctionable+combine.swift in Sources */, 91F346BB269B766D005727B6 /* CloudViewModel.swift in Sources */, 709A148E2839A1DB00BF85E5 /* Operation.swift in Sources */, @@ -3639,7 +3639,7 @@ 7044C1CA25C5B2B10011F6E7 /* ParseAuthentication+combine.swift in Sources */, 707A3BF325B0A4F0000D215C /* ParseAuthentication.swift in Sources */, 70D1BE7525BB43EB00A42E7C /* BaseConfig.swift in Sources */, - 703B090926BD9764005A112F /* ParseCloud+async.swift in Sources */, + 703B090926BD9764005A112F /* ParseCloudable+async.swift in Sources */, 918CED5B2684C74000CFDC83 /* ParseLiveQuery+combine.swift in Sources */, 70386A0825D9718C0048EC1B /* Data.swift in Sources */, 70B4E0C32762F313004C9757 /* QueryWhere.swift in Sources */, @@ -3716,7 +3716,7 @@ 7045769526BD8F8100F86F71 /* ParseInstallation+async.swift in Sources */, 7C55F9E92860CD6B002A352D /* ParseSpotify.swift in Sources */, 7003960B25A184EF0052CB31 /* ParseLiveQuery.swift in Sources */, - 7044C17725C4ECFF0011F6E7 /* ParseCloud+combine.swift in Sources */, + 7044C17725C4ECFF0011F6E7 /* ParseCloudable+combine.swift in Sources */, 705025B52845C302008D6624 /* ParsePushStatus.swift in Sources */, 700396EC25A3892D0052CB31 /* LiveQuerySocketDelegate.swift in Sources */, 9116F67126A35D620082F6D6 /* URLCache.swift in Sources */, @@ -3974,8 +3974,10 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 13.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Debug; }; @@ -4033,9 +4035,11 @@ SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 13.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 6.0; }; name = Release; }; @@ -4154,6 +4158,7 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -4185,6 +4190,7 @@ buildSettings = { CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -4321,6 +4327,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_GENERATE_TEST_COVERAGE_FILES = YES; INFOPLIST_FILE = ParseSwiftTestsmacOS/Info.plist; @@ -4348,6 +4355,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; GCC_GENERATE_TEST_COVERAGE_FILES = YES; INFOPLIST_FILE = ParseSwiftTestsmacOS/Info.plist; diff --git a/ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (iOS).xcscheme b/ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (iOS).xcscheme index d31479fb7..04abb245f 100644 --- a/ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (iOS).xcscheme +++ b/ParseSwift.xcodeproj/xcshareddata/xcschemes/ParseSwift (iOS).xcscheme @@ -1,6 +1,6 @@ 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) diff --git a/Sources/ParseSwift/Parse.swift b/Sources/ParseSwift/Parse.swift index 7ded22d05..65619ab78 100644 --- a/Sources/ParseSwift/Parse.swift +++ b/Sources/ParseSwift/Parse.swift @@ -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, @@ -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, diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index f94e08c36..2cb03a300 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -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/" diff --git a/Sources/ParseSwift/Protocols/CloudObservable.swift b/Sources/ParseSwift/Protocols/CloudObservable.swift index cbc5c801d..366838c30 100644 --- a/Sources/ParseSwift/Protocols/CloudObservable.swift +++ b/Sources/ParseSwift/Protocols/CloudObservable.swift @@ -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. diff --git a/Sources/ParseSwift/Protocols/ParseCloud+async.swift b/Sources/ParseSwift/Protocols/ParseCloudable+async.swift similarity index 93% rename from Sources/ParseSwift/Protocols/ParseCloud+async.swift rename to Sources/ParseSwift/Protocols/ParseCloudable+async.swift index 9cb70124c..83bd93b65 100644 --- a/Sources/ParseSwift/Protocols/ParseCloud+async.swift +++ b/Sources/ParseSwift/Protocols/ParseCloudable+async.swift @@ -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. @@ -9,7 +9,7 @@ #if compiler(>=5.5.2) && canImport(_Concurrency) import Foundation -public extension ParseCloud { +public extension ParseCloudable { // MARK: Aysnc/Await diff --git a/Sources/ParseSwift/Protocols/ParseCloud+combine.swift b/Sources/ParseSwift/Protocols/ParseCloudable+combine.swift similarity index 95% rename from Sources/ParseSwift/Protocols/ParseCloud+combine.swift rename to Sources/ParseSwift/Protocols/ParseCloudable+combine.swift index 641606256..96130a3a5 100644 --- a/Sources/ParseSwift/Protocols/ParseCloud+combine.swift +++ b/Sources/ParseSwift/Protocols/ParseCloudable+combine.swift @@ -1,5 +1,5 @@ // -// ParseCloud+combine.swift +// ParseCloudable+combine.swift // ParseSwift // // Created by Corey Baker on 1/29/21. @@ -10,7 +10,7 @@ import Foundation import Combine -public extension ParseCloud { +public extension ParseCloudable { // MARK: Combine diff --git a/Sources/ParseSwift/Protocols/ParseCloud.swift b/Sources/ParseSwift/Protocols/ParseCloudable.swift similarity index 88% rename from Sources/ParseSwift/Protocols/ParseCloud.swift rename to Sources/ParseSwift/Protocols/ParseCloudable.swift index 6b4a56fc8..ca1916beb 100644 --- a/Sources/ParseSwift/Protocols/ParseCloud.swift +++ b/Sources/ParseSwift/Protocols/ParseCloudable.swift @@ -1,5 +1,5 @@ // -// ParseCloud.swift +// ParseCloudable.swift // ParseSwift // // Created by Corey Baker on 12/29/20. @@ -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 /** @@ -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. @@ -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. diff --git a/Sources/ParseSwift/Storage/ParseFileManager.swift b/Sources/ParseSwift/Storage/ParseFileManager.swift index 4542cf4ec..bde16c2ee 100644 --- a/Sources/ParseSwift/Storage/ParseFileManager.swift +++ b/Sources/ParseSwift/Storage/ParseFileManager.swift @@ -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?() { @@ -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) { @@ -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) + } +} diff --git a/Sources/ParseSwift/Storage/ParseKeyValueStore.swift b/Sources/ParseSwift/Storage/ParseKeyValueStore.swift index 182c0e639..5b4139b01 100644 --- a/Sources/ParseSwift/Storage/ParseKeyValueStore.swift +++ b/Sources/ParseSwift/Storage/ParseKeyValueStore.swift @@ -1,5 +1,5 @@ // -// ParseKeyValueStore.swift +// ParsePrimitiveStorable.swift // // // Created by Pranjal Satija on 7/19/20. @@ -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 @@ -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]() @@ -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) { diff --git a/Sources/ParseSwift/Storage/ParseStorage.swift b/Sources/ParseSwift/Storage/ParseStorage.swift index e0e40d298..b9510546a 100644 --- a/Sources/ParseSwift/Storage/ParseStorage.swift +++ b/Sources/ParseSwift/Storage/ParseStorage.swift @@ -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 } @@ -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) diff --git a/Sources/ParseSwift/Types/CloudViewModel.swift b/Sources/ParseSwift/Types/CloudViewModel.swift index 2967d1eab..8eae595e3 100644 --- a/Sources/ParseSwift/Types/CloudViewModel.swift +++ b/Sources/ParseSwift/Types/CloudViewModel.swift @@ -14,7 +14,7 @@ import Foundation [documentation](https://developer.apple.com/documentation/combine/observableobject) for more details. */ -open class CloudViewModel: CloudObservable { +open class CloudViewModel: CloudObservable { public typealias CloudCodeType = T public var cloudCode: T @@ -73,7 +73,7 @@ open class CloudViewModel: CloudObservable { } // MARK: CloudCodeViewModel -public extension ParseCloud { +public extension ParseCloudable { /** Creates a view model for this CloudCode. Suitable for `ObjectObserved` diff --git a/Sources/ParseSwift/Types/ParseConfiguration.swift b/Sources/ParseSwift/Types/ParseConfiguration.swift index 1b67d293a..1283fd87f 100644 --- a/Sources/ParseSwift/Types/ParseConfiguration.swift +++ b/Sources/ParseSwift/Types/ParseConfiguration.swift @@ -166,7 +166,7 @@ public struct ParseConfiguration { 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, diff --git a/Sources/ParseSwift/Types/ParseFile+async.swift b/Sources/ParseSwift/Types/ParseFile+async.swift index d08d43c2d..b5eb6a2f5 100644 --- a/Sources/ParseSwift/Types/ParseFile+async.swift +++ b/Sources/ParseSwift/Types/ParseFile+async.swift @@ -31,6 +31,7 @@ public extension ParseFile { } /** + Fetches a file with given url *asynchronously*. Fetches a file with given url *asynchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. - parameter progress: A block that will be called when file updates it is progress. diff --git a/Sources/ParseSwift/Types/ParseFile.swift b/Sources/ParseSwift/Types/ParseFile.swift index 9a089e093..e88cc6865 100644 --- a/Sources/ParseSwift/Types/ParseFile.swift +++ b/Sources/ParseSwift/Types/ParseFile.swift @@ -149,6 +149,59 @@ public struct ParseFile: Fileable, Savable, Fetchable, Deletable, Hashable { } } +// MARK: Helper Methods (internal) +extension ParseFile { + func setDefaultOptions(_ options: API.Options) -> API.Options { + var options = options + if let mimeType = mimeType { + options.insert(.mimeType(mimeType)) + } else { + options.insert(.removeMimeType) + } + if let metadata = metadata { + options.insert(.metadata(metadata)) + } + if let tags = tags { + options.insert(.tags(tags)) + } + options = options.union(self.options) + return options + } + + func checkDownloadsForFile(options: API.Options) throws -> ParseFile? { + var cachePolicy: URLRequest.CachePolicy = ParseSwift.configuration.requestCachePolicy + var shouldBreak = false + for option in options { + switch option { + case .cachePolicy(let policy): + cachePolicy = policy + shouldBreak = true + default: + continue + } + if shouldBreak { + break + } + } + switch cachePolicy { + case .useProtocolCachePolicy, .returnCacheDataElseLoad: + return try createLocalParseFileIfExists() + case .returnCacheDataDontLoad: + return try? createLocalParseFileIfExists() + default: + throw ParseError(code: .unknownError, + message: "Policy defines to load from remote") + } + } + + func createLocalParseFileIfExists() throws -> Self { + let fileLocation = try ParseFileManager.fileExists(self) + var mutableFile = self + mutableFile.localURL = fileLocation + return mutableFile + } +} + // MARK: Coding extension ParseFile { public init(from decoder: Decoder) throws { @@ -276,21 +329,8 @@ extension ParseFile { stream: InputStream, callbackQueue: DispatchQueue = .main, progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil) throws { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) - } - options = options.union(self.options) - return try uploadFileCommand() - .executeStream(options: options, + try uploadFileCommand() + .executeStream(options: setDefaultOptions(options), callbackQueue: callbackQueue, uploadProgress: progress, stream: stream) @@ -303,19 +343,7 @@ extension ParseFile { - returns: A saved `ParseFile`. */ public func save(options: API.Options = []) throws -> ParseFile { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) - } - options = options.union(self.options) + let options = setDefaultOptions(options) if isDownloadNeeded { let fetched = try fetch(options: options) return try fetched.uploadFileCommand().execute(options: options) @@ -367,19 +395,7 @@ extension ParseFile { public func save(options: API.Options = [], callbackQueue: DispatchQueue = .main, progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)?) throws -> ParseFile { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) - } - options = options.union(self.options) + let options = setDefaultOptions(options) if isDownloadNeeded { let fetched = try fetch(options: options) return try fetched @@ -441,19 +457,7 @@ extension ParseFile { callbackQueue: DispatchQueue = .main, progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil, completion: @escaping (Result) -> Void) { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) - } - options = options.union(self.options) + let options = setDefaultOptions(options) if isDownloadNeeded { fetch(options: options) { result in switch result { @@ -507,6 +511,7 @@ extension ParseFile { // MARK: Fetching extension ParseFile { + /** Fetches a file with given url *synchronously*. - parameter options: A set of header options sent to the server. Defaults to an empty set. @@ -518,21 +523,8 @@ extension ParseFile { public func fetch(options: API.Options = [], stream: InputStream, callbackQueue: DispatchQueue = .main) throws { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) - } - options = options.union(self.options) - return try downloadFileCommand() - .executeStream(options: options, + try downloadFileCommand() + .executeStream(options: setDefaultOptions(options), callbackQueue: callbackQueue, stream: stream) } @@ -547,21 +539,23 @@ extension ParseFile { public func fetch(includeKeys: [String]? = nil, options: API.Options = [], callbackQueue: DispatchQueue) throws -> ParseFile { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) + let options = setDefaultOptions(options) + do { + guard let file = try checkDownloadsForFile(options: options) else { + throw ParseError(code: .unsavedFileFailure, + message: "File not downloaded") + } + return file + } catch { + let defaultError = ParseError(code: .unknownError, + message: error.localizedDescription) + let parseError = error as? ParseError ?? defaultError + guard parseError.code != .unsavedFileFailure else { + throw parseError + } + return try downloadFileCommand() + .execute(options: options) } - options = options.union(self.options) - return try downloadFileCommand() - .execute(options: options) } /** @@ -620,23 +614,25 @@ extension ParseFile { public func fetch(options: API.Options = [], callbackQueue: DispatchQueue = .main, progress: @escaping ((URLSessionDownloadTask, Int64, Int64, Int64) -> Void)) throws -> ParseFile { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) + let options = setDefaultOptions(options) + do { + guard let file = try checkDownloadsForFile(options: options) else { + throw ParseError(code: .unsavedFileFailure, + message: "File not downloaded") + } + return file + } catch { + let defaultError = ParseError(code: .unknownError, + message: error.localizedDescription) + let parseError = error as? ParseError ?? defaultError + guard parseError.code != .unsavedFileFailure else { + throw parseError + } + return try downloadFileCommand() + .execute(options: options, + notificationQueue: callbackQueue, + downloadProgress: progress) } - options = options.union(self.options) - return try downloadFileCommand() - .execute(options: options, - notificationQueue: callbackQueue, - downloadProgress: progress) } /** @@ -685,24 +681,31 @@ extension ParseFile { callbackQueue: DispatchQueue = .main, progress: ((URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? = nil, completion: @escaping (Result) -> Void) { - var options = options - if let mimeType = mimeType { - options.insert(.mimeType(mimeType)) - } else { - options.insert(.removeMimeType) - } - if let metadata = metadata { - options.insert(.metadata(metadata)) - } - if let tags = tags { - options.insert(.tags(tags)) - } - options = options.union(self.options) - downloadFileCommand() - .executeAsync(options: options, - callbackQueue: callbackQueue, - downloadProgress: progress) { result in - completion(result) + let options = setDefaultOptions(options) + do { + guard let file = try checkDownloadsForFile(options: options) else { + throw ParseError(code: .unsavedFileFailure, + message: "File not downloaded") + } + callbackQueue.async { + completion(.success(file)) + } + } catch { + let defaultError = ParseError(code: .unknownError, + message: error.localizedDescription) + let parseError = error as? ParseError ?? defaultError + guard parseError.code != .unsavedFileFailure else { + callbackQueue.async { + completion(.failure(parseError)) + } + return + } + downloadFileCommand() + .executeAsync(options: options, + callbackQueue: callbackQueue, + downloadProgress: progress) { result in + completion(result) + } } } diff --git a/Tests/ParseSwiftTests/IOS13Tests.swift b/Tests/ParseSwiftTests/IOS13Tests.swift index e8b82928f..928eb3057 100644 --- a/Tests/ParseSwiftTests/IOS13Tests.swift +++ b/Tests/ParseSwiftTests/IOS13Tests.swift @@ -76,13 +76,11 @@ class IOS13Tests: XCTestCase { // swiftlint:disable:this type_body_length #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { + guard let fileManager = ParseFileManager() else { throw ParseError(code: .unknownError, message: "Should have initialized file manage") } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill() diff --git a/Tests/ParseSwiftTests/ParseCloudAsyncTests.swift b/Tests/ParseSwiftTests/ParseCloudableAsyncTests.swift similarity index 93% rename from Tests/ParseSwiftTests/ParseCloudAsyncTests.swift rename to Tests/ParseSwiftTests/ParseCloudableAsyncTests.swift index ae3eef40b..ae94b1027 100644 --- a/Tests/ParseSwiftTests/ParseCloudAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseCloudableAsyncTests.swift @@ -1,5 +1,5 @@ // -// ParseCloudAsyncTests.swift +// ParseCloudableAsyncTests.swift // ParseSwift // // Created by Corey Baker on 9/28/21. @@ -14,8 +14,8 @@ import FoundationNetworking import XCTest @testable import ParseSwift -class ParseCloudAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length - struct Cloud: ParseCloud { +class ParseCloudableAsyncTests: XCTestCase { // swiftlint:disable:this type_body_length + struct Cloud: ParseCloudable { typealias ReturnType = String? // swiftlint:disable:this nesting // These are required by ParseObject diff --git a/Tests/ParseSwiftTests/ParseCloudCombineTests.swift b/Tests/ParseSwiftTests/ParseCloudableCombineTests.swift similarity index 95% rename from Tests/ParseSwiftTests/ParseCloudCombineTests.swift rename to Tests/ParseSwiftTests/ParseCloudableCombineTests.swift index f3a7a098c..9bca421ab 100644 --- a/Tests/ParseSwiftTests/ParseCloudCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseCloudableCombineTests.swift @@ -1,5 +1,5 @@ // -// ParseCloudCombineTests.swift +// ParseCloudableCombineTests.swift // ParseSwift // // Created by Corey Baker on 1/30/21. @@ -13,9 +13,9 @@ import XCTest import Combine @testable import ParseSwift -class ParseCloudCombineTests: XCTestCase { // swiftlint:disable:this type_body_length +class ParseCloudableCombineTests: XCTestCase { // swiftlint:disable:this type_body_length - struct Cloud: ParseCloud { + struct Cloud: ParseCloudable { typealias ReturnType = String? // swiftlint:disable:this nesting // These are required by ParseObject diff --git a/Tests/ParseSwiftTests/ParseCloudTests.swift b/Tests/ParseSwiftTests/ParseCloudableTests.swift similarity index 98% rename from Tests/ParseSwiftTests/ParseCloudTests.swift rename to Tests/ParseSwiftTests/ParseCloudableTests.swift index d13b3181d..1dac83906 100644 --- a/Tests/ParseSwiftTests/ParseCloudTests.swift +++ b/Tests/ParseSwiftTests/ParseCloudableTests.swift @@ -1,5 +1,5 @@ // -// ParseCloudTests.swift +// ParseCloudableTests.swift // ParseSwift // // Created by Corey Baker on 12/29/20. @@ -10,16 +10,16 @@ import Foundation import XCTest @testable import ParseSwift -class ParseCloudTests: XCTestCase { // swiftlint:disable:this type_body_length +class ParseCloudableTests: XCTestCase { // swiftlint:disable:this type_body_length - struct Cloud: ParseCloud { + struct Cloud: ParseCloudable { typealias ReturnType = String? // swiftlint:disable:this nesting // These are required by ParseObject var functionJobName: String } - struct Cloud2: ParseCloud { + struct Cloud2: ParseCloudable { typealias ReturnType = String? // swiftlint:disable:this nesting // These are required by ParseObject diff --git a/Tests/ParseSwiftTests/ParseFileAsyncTests.swift b/Tests/ParseSwiftTests/ParseFileAsyncTests.swift index a70396646..01749a0b3 100644 --- a/Tests/ParseSwiftTests/ParseFileAsyncTests.swift +++ b/Tests/ParseSwiftTests/ParseFileAsyncTests.swift @@ -47,9 +47,8 @@ class ParseFileAsyncTests: XCTestCase { // swiftlint:disable:this type_body_leng #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { - throw ParseError(code: .unknownError, message: "Should have initialized file manage") + guard let fileManager = ParseFileManager() else { + throw ParseError(code: .unknownError, message: "Should have initialized file manager") } let directory = URL(fileURLWithPath: temporaryDirectory, isDirectory: true) let expectation1 = XCTestExpectation(description: "Delete files1") @@ -61,8 +60,7 @@ class ParseFileAsyncTests: XCTestCase { // swiftlint:disable:this type_body_leng XCTFail(error.localizedDescription) expectation1.fulfill() } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill() @@ -100,6 +98,69 @@ class ParseFileAsyncTests: XCTestCase { // swiftlint:disable:this type_body_leng XCTAssertEqual(fetched.name, response.name) XCTAssertEqual(fetched.url, response.url) XCTAssertNotNil(fetched.localURL) + + // Remove URL mocker so we can check cache + MockURLProtocol.removeAll() + let fetchedFileCached = try await parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTAssertEqual(fetchedFileCached, fetched) + } + + @MainActor + func testFetchLoadFromRemote() async throws { + + // swiftlint:disable:next line_length + guard let parseFileURL = URL(string: "http://localhost:1337/1/files/applicationId/d3a37aed0672a024595b766f97133615_logo.svg") else { + XCTFail("Should create URL") + return + } + var parseFile = ParseFile(name: "d3a37aed0672a024595b766f97133615_logo.svg", cloudURL: parseFileURL) + parseFile.url = parseFileURL + + let response = FileUploadResponse(name: "d3a37aed0672a024595b766f97133615_logo.svg", + url: parseFileURL) + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(response) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + + let fetched = try await parseFile.fetch(options: [.cachePolicy(.reloadIgnoringLocalAndRemoteCacheData)]) + XCTAssertEqual(fetched.name, response.name) + XCTAssertEqual(fetched.url, response.url) + XCTAssertNotNil(fetched.localURL) + + // Remove URL mocker so we can check cache + MockURLProtocol.removeAll() + let fetchedFileCached = try await parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTAssertEqual(fetchedFileCached, fetched) + } + + @MainActor + func testFetchLoadFromCacheNoCache() async throws { + + // swiftlint:disable:next line_length + guard let parseFileURL = URL(string: "http://localhost:1337/1/files/applicationId/d3a37aed0672a024595b766f97133615_logo.svg") else { + XCTFail("Should create URL") + return + } + var parseFile = ParseFile(name: "d3a37aed0672a024595b766f97133615_logo.svg", cloudURL: parseFileURL) + parseFile.url = parseFileURL + + do { + _ = try await parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTFail("Should have thrown error") + } catch { + guard let parseError = error as? ParseError else { + XCTFail("Should have casted") + return + } + XCTAssertEqual(parseError.code, .unsavedFileFailure) + } } @MainActor diff --git a/Tests/ParseSwiftTests/ParseFileCombineTests.swift b/Tests/ParseSwiftTests/ParseFileCombineTests.swift index 73c504174..25a4bf5a4 100644 --- a/Tests/ParseSwiftTests/ParseFileCombineTests.swift +++ b/Tests/ParseSwiftTests/ParseFileCombineTests.swift @@ -47,8 +47,7 @@ class ParseFileCombineTests: XCTestCase { // swiftlint:disable:this type_body_le #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { + guard let fileManager = ParseFileManager() else { throw ParseError(code: .unknownError, message: "Should have initialized file manage") } let directory = URL(fileURLWithPath: temporaryDirectory, isDirectory: true) @@ -61,8 +60,7 @@ class ParseFileCombineTests: XCTestCase { // swiftlint:disable:this type_body_le XCTFail(error.localizedDescription) expectation1.fulfill() } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill() diff --git a/Tests/ParseSwiftTests/ParseFileTests.swift b/Tests/ParseSwiftTests/ParseFileTests.swift index 7c08a8948..079896621 100644 --- a/Tests/ParseSwiftTests/ParseFileTests.swift +++ b/Tests/ParseSwiftTests/ParseFileTests.swift @@ -49,8 +49,7 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { + guard let fileManager = ParseFileManager() else { throw ParseError(code: .unknownError, message: "Should have initialized file manage") } let directory = URL(fileURLWithPath: temporaryDirectory, isDirectory: true) @@ -63,8 +62,7 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length XCTFail(error.localizedDescription) expectation1.fulfill() } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill() @@ -1125,14 +1123,61 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length // Remove URL mocker so we can check cache MockURLProtocol.removeAll() + let fetchedFileCached = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTAssertEqual(fetchedFileCached, fetchedFile) + } + + func testFetchFileLoadFromRemote() throws { + // swiftlint:disable:next line_length + guard let parseFileURL = URL(string: "http://localhost:1337/1/files/applicationId/d3a37aed0672a024595b766f97133615_logo.svg") else { + XCTFail("Should create URL") + return + } + var parseFile = ParseFile(name: "d3a37aed0672a024595b766f97133615_logo.svg", cloudURL: parseFileURL) + parseFile.url = parseFileURL + + let response = FileUploadResponse(name: "d3a37aed0672a024595b766f97133615_logo.svg", + url: parseFileURL) + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(response) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + + let fetchedFile = try parseFile.fetch(options: [.cachePolicy(.reloadIgnoringLocalAndRemoteCacheData)]) + XCTAssertEqual(fetchedFile.name, response.name) + XCTAssertEqual(fetchedFile.url, response.url) + XCTAssertNotNil(fetchedFile.localURL) + + // Remove URL mocker so we can check cache + MockURLProtocol.removeAll() + let fetchedFileCached = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTAssertEqual(fetchedFileCached, fetchedFile) + } + + func testFetchFileLoadFromCacheNoCache() throws { + // swiftlint:disable:next line_length + guard let parseFileURL = URL(string: "http://localhost:1337/1/files/applicationId/d3a37aed0672a024595b766f97133615_logo.svg") else { + XCTFail("Should create URL") + return + } + var parseFile = ParseFile(name: "d3a37aed0672a024595b766f97133615_logo.svg", cloudURL: parseFileURL) + parseFile.url = parseFileURL + do { _ = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTFail("Should have thrown error") } catch { guard let parseError = error as? ParseError else { XCTFail("Should have casted") return } - XCTAssertTrue(parseError.message.contains("No response")) + XCTAssertEqual(parseError.code, .unsavedFileFailure) } } @@ -1169,15 +1214,8 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length // Remove URL mocker so we can check cache MockURLProtocol.removeAll() - do { - _ = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) - } catch { - guard let parseError = error as? ParseError else { - XCTFail("Should have casted") - return - } - XCTAssertTrue(parseError.message.contains("No response")) - } + let fetchedFileCached = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) + XCTAssertEqual(fetchedFileCached, fetchedFile) } func testFetchFileProgress() throws { @@ -1209,6 +1247,80 @@ class ParseFileTests: XCTestCase { // swiftlint:disable:this type_body_length XCTAssertEqual(fetchedFile.name, response.name) XCTAssertEqual(fetchedFile.url, response.url) XCTAssertNotNil(fetchedFile.localURL) + + // Remove URL mocker so we can check cache + MockURLProtocol.removeAll() + // swiftlint:disable:next line_length + let fetchedFileCached = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) { (_, _, totalDownloaded, totalExpected) in + let currentProgess = Double(totalDownloaded)/Double(totalExpected) * 100 + XCTAssertGreaterThan(currentProgess, -1) + } + XCTAssertEqual(fetchedFileCached, fetchedFile) + } + + func testFetchFileProgressLoadFromRemote() throws { + // swiftlint:disable:next line_length + guard let parseFileURL = URL(string: "http://localhost:1337/1/files/applicationId/d3a37aed0672a024595b766f97133615_logo.svg") else { + XCTFail("Should create URL") + return + } + var parseFile = ParseFile(name: "d3a37aed0672a024595b766f97133615_logo.svg", cloudURL: parseFileURL) + parseFile.url = parseFileURL + + let response = FileUploadResponse(name: "d3a37aed0672a024595b766f97133615_logo.svg", + url: parseFileURL) + let encoded: Data! + do { + encoded = try ParseCoding.jsonEncoder().encode(response) + } catch { + XCTFail("Should encode/decode. Error \(error)") + return + } + MockURLProtocol.mockRequests { _ in + return MockURLResponse(data: encoded, statusCode: 200, delay: 0.0) + } + // swiftlint:disable:next line_length + let fetchedFile = try parseFile.fetch(options: [.cachePolicy(.reloadIgnoringLocalAndRemoteCacheData)]) { (_, _, totalDownloaded, totalExpected) in + let currentProgess = Double(totalDownloaded)/Double(totalExpected) * 100 + XCTAssertGreaterThan(currentProgess, -1) + } + XCTAssertEqual(fetchedFile.name, response.name) + XCTAssertEqual(fetchedFile.url, response.url) + XCTAssertNotNil(fetchedFile.localURL) + + // Remove URL mocker so we can check cache + MockURLProtocol.removeAll() + // swiftlint:disable:next line_length + let fetchedFileCached = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) { (_, _, totalDownloaded, totalExpected) in + let currentProgess = Double(totalDownloaded)/Double(totalExpected) * 100 + XCTAssertGreaterThan(currentProgess, -1) + } + XCTAssertEqual(fetchedFileCached, fetchedFile) + } + + func testFetchFileProgressFromCacheNoCache() throws { + // swiftlint:disable:next line_length + guard let parseFileURL = URL(string: "http://localhost:1337/1/files/applicationId/d3a37aed0672a024595b766f97133615_logo.svg") else { + XCTFail("Should create URL") + return + } + var parseFile = ParseFile(name: "d3a37aed0672a024595b766f97133615_logo.svg", cloudURL: parseFileURL) + parseFile.url = parseFileURL + + do { + // swiftlint:disable:next line_length + _ = try parseFile.fetch(options: [.cachePolicy(.returnCacheDataDontLoad)]) { (_, _, totalDownloaded, totalExpected) in + let currentProgess = Double(totalDownloaded)/Double(totalExpected) * 100 + XCTAssertGreaterThan(currentProgess, -1) + } + XCTFail("Should have thrown error") + } catch { + guard let parseError = error as? ParseError else { + XCTFail("Should have casted") + return + } + XCTAssertEqual(parseError.code, .unsavedFileFailure) + } } func testDeleteFileAysnc() throws { diff --git a/Tests/ParseSwiftTests/ParseFileTransferableTests.swift b/Tests/ParseSwiftTests/ParseFileTransferableTests.swift index 6f5eb50f2..491040fe9 100644 --- a/Tests/ParseSwiftTests/ParseFileTransferableTests.swift +++ b/Tests/ParseSwiftTests/ParseFileTransferableTests.swift @@ -115,8 +115,7 @@ class ParseFileTransferableTests: XCTestCase { #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { + guard let fileManager = ParseFileManager() else { throw ParseError(code: .unknownError, message: "Should have initialized file manage") } let directory = URL(fileURLWithPath: temporaryDirectory, isDirectory: true) @@ -129,8 +128,7 @@ class ParseFileTransferableTests: XCTestCase { XCTFail(error.localizedDescription) expectation1.fulfill() } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill() diff --git a/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift b/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift index 10c03ff75..86af471bb 100644 --- a/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift @@ -139,13 +139,11 @@ class ParseObjectCustomObjectIdTests: XCTestCase { // swiftlint:disable:this typ #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { + guard let fileManager = ParseFileManager() else { throw ParseError(code: .unknownError, message: "Should have initialized file manage") } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill() diff --git a/Tests/ParseSwiftTests/ParseObjectTests.swift b/Tests/ParseSwiftTests/ParseObjectTests.swift index 9e64b6482..22d0d73cd 100644 --- a/Tests/ParseSwiftTests/ParseObjectTests.swift +++ b/Tests/ParseSwiftTests/ParseObjectTests.swift @@ -338,13 +338,11 @@ class ParseObjectTests: XCTestCase { // swiftlint:disable:this type_body_length #endif try ParseStorage.shared.deleteAll() - guard let fileManager = ParseFileManager(), - let defaultDirectoryPath = fileManager.defaultDataDirectoryPath else { + guard let fileManager = ParseFileManager() else { throw ParseError(code: .unknownError, message: "Should have initialized file manage") } - let directory2 = defaultDirectoryPath - .appendingPathComponent(ParseConstants.fileDownloadsDirectory, isDirectory: true) + let directory2 = try ParseFileManager.downloadDirectory() let expectation2 = XCTestExpectation(description: "Delete files2") fileManager.removeDirectoryContents(directory2) { _ in expectation2.fulfill()