Skip to content

Commit 3f1c897

Browse files
authored
Add File support (#40)
* WIP: Adding FileManager * WIP * update * Added File saves. Need to fix URLMocker. * Fix metadata and tags for S3 adapter. * Reorganized some of the responses * Support uploading local files * Finished initial saved, starting on downloads. * Add delete file * Make batch deletes look like regular deletes instead of using result enum * Added save from cloud file * fix warnings * Add more documentation * remove file * Fix Swift backwards compatability * Patch URL mocker for ParseFile tests * Fix tearDown error * Remove bard URL/ParseError test * Add async tests * Only allow file streams through save * Add ParseFileManagerTests * Update progress signatures * Add more tests. Update documentation * Additional test * Extend async wait times since some CI servers are slower than others. * Changes before adding tests * Add deep-save test * Add Playgrounds examples and fix bugs * Bug fix: fetching stored files from Parse Server. - Improved testing for embedded ParseObjects and ParseFiles - Improved ParseFile playground * Set callbackQueue for URLSessionDelegate. It still isn't being fired, but should callback to the correct queue in the future. * Removed extra arg in API.Command. - Fixed a bug that overwrites local ACL with nil after a save on a ParseObject - Cleaned up comments and code * Progress updates works (removed WIP warnings). - Update playgrounds for ParseFile - Update documentation * Fix delete error response for ParseObjects (these work a little differently since a no response is considered successful). * remove .DS_Store * Still fixing/testing delete of batch ParseObject
1 parent 739db44 commit 3f1c897

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3972
-785
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ docs/
2020
xcuserdata/
2121

2222
## Other
23+
.DS_Store
2324
*.moved-aside
2425
*.xccheckout
2526
*.xcscmblueprint

.spi.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ builder:
33
configs:
44
- platform: ios
55
scheme: "ParseSwift (iOS)"
6+
- platform: macos-xcodebuild
7+
scheme: "ParseSwift (macOS)"
8+
- platform: macos-xcodebuild-arm
9+
scheme: "ParseSwift (macOS)"
610
- platform: tvos
711
scheme: "ParseSwift (tvOS)"
812
- platform: watchos

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.1
1+
// swift-tools-version:5.0
22

33
import PackageDescription
44

ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,12 @@ do {
235235
[scoreToFetch, score2ToFetch].deleteAll { result in
236236
switch result {
237237
case .success(let deletedScores):
238-
239-
deletedScores.forEach { result in
240-
switch result {
241-
case .success(let deleted):
242-
print("Successfully deleted: \(deleted)")
243-
case .failure(let error):
244-
print("Error deleting: \(error)")
238+
deletedScores.forEach { error in
239+
guard let error = error else {
240+
print("Successfully deleted scores")
241+
return
245242
}
243+
print("Error deleting: \(error)")
246244
}
247245
case .failure(let error):
248246
assertionFailure("Error deleting: \(error)")

ParseSwift.playground/Pages/2 - Finding Objects.xcplaygroundpage/Contents.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ query.limit(2).find(callbackQueue: .main) { results in
3434
scores.forEach { (score) in
3535
guard let createdAt = score.createdAt else { fatalError() }
3636
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
37+
print("Found score: \(score)")
3738
}
3839

3940
case .failure(let error):
@@ -47,6 +48,7 @@ assert(results.count >= 1)
4748
results.forEach { (score) in
4849
guard let createdAt = score.createdAt else { fatalError() }
4950
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
51+
print("Found score: \(score)")
5052
}
5153

5254
// Query first asynchronously (preferred way) - Performs work on background
@@ -59,7 +61,7 @@ query.first { results in
5961
guard let objectId = score.objectId,
6062
let createdAt = score.createdAt else { fatalError() }
6163
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
62-
print(objectId)
64+
print("Found score: \(score)")
6365

6466
case .failure(let error):
6567
assertionFailure("Error querying: \(error)")

ParseSwift.playground/Pages/5 - ACL.xcplaygroundpage/Contents.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ struct GameScore: ParseObject {
3030
//: a custom initializer
3131
init(score: Int) {
3232
self.score = score
33-
self.ACL = try? ParseACL.defaultACL()
3433
}
3534
}
3635

3736
//: Define initial GameScores
38-
let score = GameScore(score: 40)
37+
var score = GameScore(score: 40)
38+
39+
//: Set the ACL to default for your GameScore
40+
score.ACL = try? ParseACL.defaultACL()
3941

4042
/*: Save asynchronously (preferred way) - Performs work on background
4143
queue and returns to designated on designated callbackQueue.
@@ -47,8 +49,10 @@ score.save { result in
4749
assert(savedScore.objectId != nil)
4850
assert(savedScore.createdAt != nil)
4951
assert(savedScore.updatedAt != nil)
50-
assert(savedScore.ACL != nil)
5152
assert(savedScore.score == 40)
53+
assert(savedScore.ACL != nil)
54+
55+
print("Saved score with ACL: \(savedScore)")
5256

5357
case .failure(let error):
5458
assertionFailure("Error saving: \(error)")
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//: [Previous](@previous)
2+
3+
import PlaygroundSupport
4+
import Foundation
5+
import ParseSwift
6+
PlaygroundPage.current.needsIndefiniteExecution = true
7+
8+
initializeParse()
9+
10+
//: Create your own ValueTyped ParseObject
11+
struct GameScore: ParseObject {
12+
//: Those are required for Object
13+
var objectId: String?
14+
var createdAt: Date?
15+
var updatedAt: Date?
16+
var ACL: ParseACL?
17+
18+
//: Your own properties
19+
var score: Int = 0
20+
var profilePicture: ParseFile?
21+
var myData: ParseFile?
22+
23+
//custom initializer
24+
init(score: Int) {
25+
self.score = score
26+
}
27+
28+
init(objectId: String?) {
29+
self.objectId = objectId
30+
}
31+
}
32+
33+
//: Define initial GameScore
34+
var score = GameScore(score: 52)
35+
36+
//: Set the link online for the file
37+
let linkToFile = URL(string: "https://parseplatform.org/img/logo.svg")!
38+
39+
//: Create a new ParseFile for your picture
40+
let profilePic = ParseFile(name: "profile.svg", cloudURL: linkToFile)
41+
42+
//: Set the picture as part of your ParseObject
43+
score.profilePicture = profilePic
44+
45+
/*: Save asynchronously (preferred way) - Performs work on background
46+
queue and returns to designated on designated callbackQueue.
47+
If no callbackQueue is specified it returns to main queue.
48+
*/
49+
score.save { result in
50+
switch result {
51+
case .success(let savedScore):
52+
assert(savedScore.objectId != nil)
53+
assert(savedScore.createdAt != nil)
54+
assert(savedScore.updatedAt != nil)
55+
assert(savedScore.ACL == nil)
56+
assert(savedScore.score == 52)
57+
assert(savedScore.profilePicture != nil)
58+
59+
print("Your profile picture has been successfully saved")
60+
61+
//: To get the contents updated ParseFile, you need to fetch your GameScore
62+
savedScore.fetch { result in
63+
switch result {
64+
case .success(let fetchedScore):
65+
guard let picture = fetchedScore.profilePicture,
66+
let url = fetchedScore.profilePicture?.url else {
67+
return
68+
}
69+
print("The new name of your saved profilePicture is: \(picture.name)")
70+
print("The profilePicture is saved to your Parse Server at: \(url)")
71+
print("The full details of your profilePicture ParseFile are: \(picture)")
72+
73+
//: If you need to download your profilePicture
74+
picture.fetch { result in
75+
switch result {
76+
case .success(let fetchedFile):
77+
print("The file is now saved on your device at: \(fetchedFile.localURL)")
78+
print("The full details of your profilePicture ParseFile are: \(fetchedFile)")
79+
case .failure(let error):
80+
assertionFailure("Error fetching: \(error)")
81+
}
82+
}
83+
84+
case .failure(let error):
85+
assertionFailure("Error fetching: \(error)")
86+
}
87+
}
88+
case .failure(let error):
89+
assertionFailure("Error saving: \(error)")
90+
}
91+
}
92+
93+
/*: Files can also be saved from data. Below is how to do it synchrously, but async is similar to above
94+
Create a new ParseFile for your data
95+
*/
96+
let sampleData = "Hello World".data(using: .utf8)!
97+
let helloFile = ParseFile(name: "hello.txt", data: sampleData)
98+
99+
//: Define another GameScore
100+
var score2 = GameScore(score: 105)
101+
score2.myData = helloFile
102+
103+
//: Save synchronously (not preferred - all operations on main queue)
104+
do {
105+
let savedScore = try score2.save()
106+
print("Your hello file has been successfully saved")
107+
108+
//: To get the contents updated ParseFile, you need to fetch your GameScore
109+
let fetchedScore = try savedScore.fetch()
110+
if var myData = fetchedScore.myData {
111+
112+
guard let url = myData.url else {
113+
fatalError("Error: file should have url.")
114+
}
115+
print("The new name of your saved data is: \(myData.name)")
116+
print("The file is saved to your Parse Server at: \(url)")
117+
print("The full details of your data file are: \(myData)")
118+
119+
//: If you need to download your profilePicture
120+
let fetchedFile = try myData.fetch()
121+
if fetchedFile.localURL != nil {
122+
print("The file is now saved at: \(fetchedFile.localURL!)")
123+
print("The full details of your data ParseFile are: \(fetchedFile)")
124+
125+
/*: If you want to use the data from the file to display the text file or image, you need to retreive
126+
the data from the file.
127+
*/
128+
guard let dataFromParseFile = try? Data(contentsOf: fetchedFile.localURL!) else {
129+
fatalError("Error: couldn't get data from file.")
130+
}
131+
132+
//: Checking to make sure the data saved on the Parse Server is the same as the original
133+
if dataFromParseFile != sampleData {
134+
assertionFailure("Data isn't the same. Something went wrong.")
135+
}
136+
137+
guard let parseFileString = String(data: dataFromParseFile, encoding: .utf8) else {
138+
fatalError("Error: couldn't create String from data.")
139+
}
140+
print("The data saved on parse is: \"\(parseFileString)\"")
141+
} else {
142+
assertionFailure("Error fetching: there should be a localURL")
143+
}
144+
} else {
145+
assertionFailure("Error fetching: there should be a localURL")
146+
}
147+
} catch {
148+
fatalError("Error saving: \(error)")
149+
}
150+
151+
/*: Files can also be saved from files located on your device by using:
152+
let localFile = ParseFile(name: "hello.txt", localURL: URL)
153+
*/
154+
//: [Next](@next)

ParseSwift.playground/contents.xcplayground

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
<page name='6 - Installation'/>
1010
<page name='7 - GeoPoint'/>
1111
<page name='8 - Pointers'/>
12+
<page name='9 - Files'/>
1213
</pages>
1314
</playground>

0 commit comments

Comments
 (0)