Skip to content

Add File support #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Dec 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
85c41f2
WIP: Adding FileManager
cbaker6 Dec 21, 2020
71eaacb
WIP
cbaker6 Dec 22, 2020
c90c37d
update
cbaker6 Dec 22, 2020
00d3e73
Merge branch 'main'
cbaker6 Dec 22, 2020
df0e808
Added File saves. Need to fix URLMocker.
cbaker6 Dec 24, 2020
193d942
Fix metadata and tags for S3 adapter.
cbaker6 Dec 24, 2020
65b5dad
Reorganized some of the responses
cbaker6 Dec 24, 2020
dccfaff
Support uploading local files
cbaker6 Dec 24, 2020
b9ef81c
Finished initial saved, starting on downloads.
cbaker6 Dec 24, 2020
dd15525
Add delete file
cbaker6 Dec 24, 2020
7dc2b42
Make batch deletes look like regular deletes instead of using result …
cbaker6 Dec 24, 2020
68763c4
Added save from cloud file
cbaker6 Dec 25, 2020
1bc3ee2
fix warnings
cbaker6 Dec 25, 2020
4572e80
Merge branch 'main'
cbaker6 Dec 25, 2020
d5d7be7
Add more documentation
cbaker6 Dec 25, 2020
86f0822
remove file
cbaker6 Dec 25, 2020
9fa8bed
Fix Swift backwards compatability
cbaker6 Dec 25, 2020
d0d8366
Patch URL mocker for ParseFile tests
cbaker6 Dec 26, 2020
65cd88c
Fix tearDown error
cbaker6 Dec 26, 2020
6da8c0b
Remove bard URL/ParseError test
cbaker6 Dec 26, 2020
091e8db
Add async tests
cbaker6 Dec 26, 2020
9e0b75a
Only allow file streams through save
cbaker6 Dec 26, 2020
d42c7c7
Add ParseFileManagerTests
cbaker6 Dec 27, 2020
9430b71
Update progress signatures
cbaker6 Dec 27, 2020
e02313b
Add more tests. Update documentation
cbaker6 Dec 27, 2020
9f918ac
Additional test
cbaker6 Dec 27, 2020
7f92f9b
Extend async wait times since some CI servers are slower than others.
cbaker6 Dec 27, 2020
eeab2f9
Changes before adding tests
cbaker6 Dec 27, 2020
585fc96
Add deep-save test
cbaker6 Dec 28, 2020
26fa61f
Add Playgrounds examples and fix bugs
cbaker6 Dec 28, 2020
4e69096
Bug fix: fetching stored files from Parse Server.
cbaker6 Dec 28, 2020
4b5dece
Set callbackQueue for URLSessionDelegate. It still isn't being fired,…
cbaker6 Dec 28, 2020
0666fb8
Removed extra arg in API.Command.
cbaker6 Dec 28, 2020
0e67d6a
Progress updates works (removed WIP warnings).
cbaker6 Dec 28, 2020
8c220e0
Fix delete error response for ParseObjects (these work a little diffe…
cbaker6 Dec 30, 2020
8e11d05
remove .DS_Store
cbaker6 Dec 30, 2020
46c2dba
Still fixing/testing delete of batch ParseObject
cbaker6 Dec 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ docs/
xcuserdata/

## Other
.DS_Store
*.moved-aside
*.xccheckout
*.xcscmblueprint
Expand Down
4 changes: 4 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ builder:
configs:
- platform: ios
scheme: "ParseSwift (iOS)"
- platform: macos-xcodebuild
scheme: "ParseSwift (macOS)"
- platform: macos-xcodebuild-arm
scheme: "ParseSwift (macOS)"
- platform: tvos
scheme: "ParseSwift (tvOS)"
- platform: watchos
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.1
// swift-tools-version:5.0

import PackageDescription

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,12 @@ do {
[scoreToFetch, score2ToFetch].deleteAll { result in
switch result {
case .success(let deletedScores):

deletedScores.forEach { result in
switch result {
case .success(let deleted):
print("Successfully deleted: \(deleted)")
case .failure(let error):
print("Error deleting: \(error)")
deletedScores.forEach { error in
guard let error = error else {
print("Successfully deleted scores")
return
}
print("Error deleting: \(error)")
}
case .failure(let error):
assertionFailure("Error deleting: \(error)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ query.limit(2).find(callbackQueue: .main) { results in
scores.forEach { (score) in
guard let createdAt = score.createdAt else { fatalError() }
assert(createdAt.timeIntervalSince1970 > afterDate.timeIntervalSince1970, "date should be ok")
print("Found score: \(score)")
}

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

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

case .failure(let error):
assertionFailure("Error querying: \(error)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ struct GameScore: ParseObject {
//: a custom initializer
init(score: Int) {
self.score = score
self.ACL = try? ParseACL.defaultACL()
}
}

//: Define initial GameScores
let score = GameScore(score: 40)
var score = GameScore(score: 40)

//: Set the ACL to default for your GameScore
score.ACL = try? ParseACL.defaultACL()

/*: Save asynchronously (preferred way) - Performs work on background
queue and returns to designated on designated callbackQueue.
Expand All @@ -47,8 +49,10 @@ score.save { result in
assert(savedScore.objectId != nil)
assert(savedScore.createdAt != nil)
assert(savedScore.updatedAt != nil)
assert(savedScore.ACL != nil)
assert(savedScore.score == 40)
assert(savedScore.ACL != nil)

print("Saved score with ACL: \(savedScore)")

case .failure(let error):
assertionFailure("Error saving: \(error)")
Expand Down
154 changes: 154 additions & 0 deletions ParseSwift.playground/Pages/9 - Files.xcplaygroundpage/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//: [Previous](@previous)

import PlaygroundSupport
import Foundation
import ParseSwift
PlaygroundPage.current.needsIndefiniteExecution = true

initializeParse()

//: Create your own ValueTyped ParseObject
struct GameScore: ParseObject {
//: Those are required for Object
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?

//: Your own properties
var score: Int = 0
var profilePicture: ParseFile?
var myData: ParseFile?

//custom initializer
init(score: Int) {
self.score = score
}

init(objectId: String?) {
self.objectId = objectId
}
}

//: Define initial GameScore
var score = GameScore(score: 52)

//: Set the link online for the file
let linkToFile = URL(string: "https://parseplatform.org/img/logo.svg")!

//: Create a new ParseFile for your picture
let profilePic = ParseFile(name: "profile.svg", cloudURL: linkToFile)

//: Set the picture as part of your ParseObject
score.profilePicture = profilePic

/*: Save asynchronously (preferred way) - Performs work on background
queue and returns to designated on designated callbackQueue.
If no callbackQueue is specified it returns to main queue.
*/
score.save { result in
switch result {
case .success(let savedScore):
assert(savedScore.objectId != nil)
assert(savedScore.createdAt != nil)
assert(savedScore.updatedAt != nil)
assert(savedScore.ACL == nil)
assert(savedScore.score == 52)
assert(savedScore.profilePicture != nil)

print("Your profile picture has been successfully saved")

//: To get the contents updated ParseFile, you need to fetch your GameScore
savedScore.fetch { result in
switch result {
case .success(let fetchedScore):
guard let picture = fetchedScore.profilePicture,
let url = fetchedScore.profilePicture?.url else {
return
}
print("The new name of your saved profilePicture is: \(picture.name)")
print("The profilePicture is saved to your Parse Server at: \(url)")
print("The full details of your profilePicture ParseFile are: \(picture)")

//: If you need to download your profilePicture
picture.fetch { result in
switch result {
case .success(let fetchedFile):
print("The file is now saved on your device at: \(fetchedFile.localURL)")
print("The full details of your profilePicture ParseFile are: \(fetchedFile)")
case .failure(let error):
assertionFailure("Error fetching: \(error)")
}
}

case .failure(let error):
assertionFailure("Error fetching: \(error)")
}
}
case .failure(let error):
assertionFailure("Error saving: \(error)")
}
}

/*: Files can also be saved from data. Below is how to do it synchrously, but async is similar to above
Create a new ParseFile for your data
*/
let sampleData = "Hello World".data(using: .utf8)!
let helloFile = ParseFile(name: "hello.txt", data: sampleData)

//: Define another GameScore
var score2 = GameScore(score: 105)
score2.myData = helloFile

//: Save synchronously (not preferred - all operations on main queue)
do {
let savedScore = try score2.save()
print("Your hello file has been successfully saved")

//: To get the contents updated ParseFile, you need to fetch your GameScore
let fetchedScore = try savedScore.fetch()
if var myData = fetchedScore.myData {

guard let url = myData.url else {
fatalError("Error: file should have url.")
}
print("The new name of your saved data is: \(myData.name)")
print("The file is saved to your Parse Server at: \(url)")
print("The full details of your data file are: \(myData)")

//: If you need to download your profilePicture
let fetchedFile = try myData.fetch()
if fetchedFile.localURL != nil {
print("The file is now saved at: \(fetchedFile.localURL!)")
print("The full details of your data ParseFile are: \(fetchedFile)")

/*: If you want to use the data from the file to display the text file or image, you need to retreive
the data from the file.
*/
guard let dataFromParseFile = try? Data(contentsOf: fetchedFile.localURL!) else {
fatalError("Error: couldn't get data from file.")
}

//: Checking to make sure the data saved on the Parse Server is the same as the original
if dataFromParseFile != sampleData {
assertionFailure("Data isn't the same. Something went wrong.")
}

guard let parseFileString = String(data: dataFromParseFile, encoding: .utf8) else {
fatalError("Error: couldn't create String from data.")
}
print("The data saved on parse is: \"\(parseFileString)\"")
} else {
assertionFailure("Error fetching: there should be a localURL")
}
} else {
assertionFailure("Error fetching: there should be a localURL")
}
} catch {
fatalError("Error saving: \(error)")
}

/*: Files can also be saved from files located on your device by using:
let localFile = ParseFile(name: "hello.txt", localURL: URL)
*/
//: [Next](@next)
1 change: 1 addition & 0 deletions ParseSwift.playground/contents.xcplayground
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
<page name='6 - Installation'/>
<page name='7 - GeoPoint'/>
<page name='8 - Pointers'/>
<page name='9 - Files'/>
</pages>
</playground>
Loading