Skip to content

Commit

Permalink
Add File support (#40)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
cbaker6 authored Dec 30, 2020
1 parent 739db44 commit 3f1c897
Show file tree
Hide file tree
Showing 41 changed files with 3,972 additions and 785 deletions.
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

0 comments on commit 3f1c897

Please sign in to comment.