Skip to content

Feature: fetch with including specific or all keys #74

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 8 commits into from
Feb 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ do {
print(error)
}

//: All `ParseObjects` have a `ParseRelation` attribute that be used on instances.
//: All `ParseObject`s have a `ParseRelation` attribute that be used on instances.
//: For example, the User has:
let relation = User.current!.relation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ do {
print(error)
}

//: There are other operations: add/remove/delete objects from `ParseObjects`.
//: There are other operations: add/remove/delete objects from `ParseObject`s.
//: In fact, the `users` and `roles` relations from `ParseRoles` used the add/remove operations.
let operations = savedScore.operation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@ struct User: ParseUser {

//: Your custom keys.
var customKey: String?
var score: GameScore?
var targetScore: GameScore?
}

//: Create your own value typed `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

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

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

/*: Save your first customKey value to your `ParseUser`
Expand All @@ -30,11 +53,13 @@ struct User: ParseUser {
If no callbackQueue is specified it returns to main queue.
*/
User.current?.customKey = "myCustom"
User.current?.score = GameScore(score: 12)
User.current?.targetScore = GameScore(score: 100)
User.current?.save { results in

switch results {
case .success(let updatedUser):
print("Successfully save myCustomKey to ParseServer: \(updatedUser)")
print("Successfully save custom fields of User to ParseServer: \(updatedUser)")
case .failure(let error):
print("Failed to update user: \(error)")
}
Expand Down Expand Up @@ -69,6 +94,29 @@ User.login(username: "hello", password: "world") { results in
}
}

//: Looking at the output of user from the previous login, it only has
//: a pointer to the `score`and `targetScore` fields. You can fetch using `include` to
//: get the score.
User.current?.fetch(includeKeys: ["score"]) { result in
switch result {
case .success:
print("Successfully fetched user with score key: \(User.current)")
case .failure(let error):
print("Error fetching score: \(error)")
}
}

//: The `target` score is still missing. You can get all pointer fields at
//: once by including `["*"]`.
User.current?.fetch(includeKeys: ["*"]) { result in
switch result {
case .success:
print("Successfully fetched user with all keys: \(User.current)")
case .failure(let error):
print("Error fetching score: \(error)")
}
}

//: Logging out - synchronously.
do {
try User.logout()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ DispatchQueue.main.async {
case .success(let updatedInstallation):
print("Successfully save myCustomInstallationKey to ParseServer: \(updatedInstallation)")
case .failure(let error):
assertionFailure("Failed to update installation: \(error)")
print("Failed to update installation: \(error)")
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions Sources/ParseSwift/API/API+Commands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ internal extension API {
}
}
urlRequest.httpMethod = method.rawValue

return .success(urlRequest)
}

Expand Down Expand Up @@ -383,14 +382,21 @@ internal extension API.Command {
}

// MARK: Fetching
static func fetchCommand<T>(_ object: T) throws -> API.Command<T, T> where T: ParseObject {
static func fetchCommand<T>(_ object: T, include: [String]?) throws -> API.Command<T, T> where T: ParseObject {
guard object.isSaved else {
throw ParseError(code: .unknownError, message: "Cannot Fetch an object without id")
}

var params: [String: String]?
if let includeParams = include {
let joined = includeParams.joined(separator: ",")
params = ["include": joined]
}

return API.Command<T, T>(
method: .GET,
path: object.endpoint
path: object.endpoint,
params: params
) { (data) -> T in
try ParseCoding.jsonDecoder().decode(T.self, from: data)
}
Expand Down
20 changes: 14 additions & 6 deletions Sources/ParseSwift/Objects/ParseInstallation+combine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ public extension ParseInstallation {
/**
Fetches the `ParseInstallation` *aynchronously* with the current data from the server
and sets an error if one occurs. Publishes when complete.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- important: If an object fetched has the same objectId as current, it will automatically update the current.
*/
func fetchPublisher(options: API.Options = []) -> Future<Self, ParseError> {
func fetchPublisher(includeKeys: [String]? = nil,
options: API.Options = []) -> Future<Self, ParseError> {
Future { promise in
self.fetch(options: options,
self.fetch(includeKeys: includeKeys,
options: options,
completion: promise)
}
}
Expand Down Expand Up @@ -66,14 +70,18 @@ public extension Sequence where Element: ParseInstallation {
/**
Fetches a collection of installations *aynchronously* with the current data from the server and sets
an error if one occurs. Publishes when complete.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- important: If an object fetched has the same objectId as current, it will automatically update the current.
*/
func fetchAllPublisher(options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
func fetchAllPublisher(includeKeys: [String]? = nil,
options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
Future { promise in
self.fetchAll(options: options,
self.fetchAll(includeKeys: includeKeys,
options: options,
completion: promise)
}
}
Expand Down
51 changes: 39 additions & 12 deletions Sources/ParseSwift/Objects/ParseInstallation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -338,21 +338,26 @@ extension ParseInstallation {
/**
Fetches the `ParseInstallation` *synchronously* with the current data from the server
and sets an error if one occurs.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- throws: An error of `ParseError` type.
- important: If an object fetched has the same objectId as current, it will automatically update the current.
*/
public func fetch(options: API.Options = []) throws -> Self {
let result: Self = try fetchCommand()
public func fetch(includeKeys: [String]? = nil,
options: API.Options = []) throws -> Self {
let result: Self = try fetchCommand(include: includeKeys)
.execute(options: options, callbackQueue: .main)
Self.updateKeychainIfNeeded([result])
return result
}

/**
Fetches the `ParseInstallation` *asynchronously* and executes the given callback block.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- parameter callbackQueue: The queue to return to after completion. Default
value of .main.
Expand All @@ -361,12 +366,13 @@ extension ParseInstallation {
- important: If an object fetched has the same objectId as current, it will automatically update the current.
*/
public func fetch(
includeKeys: [String]? = nil,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<Self, ParseError>) -> Void
) {
do {
try fetchCommand()
try fetchCommand(include: includeKeys)
.executeAsync(options: options,
callbackQueue: callbackQueue) { result in
callbackQueue.async {
Expand All @@ -387,13 +393,20 @@ extension ParseInstallation {
}
}

func fetchCommand() throws -> API.Command<Self, Self> {
func fetchCommand(include: [String]?) throws -> API.Command<Self, Self> {
guard isSaved else {
throw ParseError(code: .unknownError, message: "Cannot fetch an object without id")
}

var params: [String: String]?
if let includeParams = include {
let joined = includeParams.joined(separator: ",")
params = ["include": joined]
}

return API.Command(method: .GET,
path: endpoint) { (data) -> Self in
path: endpoint,
params: params) { (data) -> Self in
try ParseCoding.jsonDecoder().decode(Self.self, from: data)
}
}
Expand Down Expand Up @@ -756,7 +769,9 @@ public extension Sequence where Element: ParseInstallation {

/**
Fetches a collection of installations *synchronously* all at once and throws an error if necessary.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.

- returns: Returns a Result enum with the object if a fetch was successful or a `ParseError` if it failed.
Expand All @@ -765,12 +780,18 @@ public extension Sequence where Element: ParseInstallation {
- warning: The order in which installations are returned are not guarenteed. You shouldn't expect results in
any particular order.
*/
func fetchAll(options: API.Options = []) throws -> [(Result<Self.Element, ParseError>)] {
func fetchAll(includeKeys: [String]? = nil,
options: API.Options = []) throws -> [(Result<Self.Element, ParseError>)] {

if (allSatisfy { $0.className == Self.Element.className}) {
let uniqueObjectIds = Set(compactMap { $0.objectId })
let query = Self.Element.query(containedIn(key: "objectId", array: [uniqueObjectIds]))
var query = Self.Element.query(containedIn(key: "objectId", array: [uniqueObjectIds]))
.limit(uniqueObjectIds.count)

if let include = includeKeys {
query = query.include(include)
}

let fetchedObjects = try query.find(options: options)
var fetchedObjectsToReturn = [(Result<Self.Element, ParseError>)]()

Expand All @@ -793,7 +814,9 @@ public extension Sequence where Element: ParseInstallation {

/**
Fetches a collection of installations all at once *asynchronously* and executes the completion block when done.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- parameter callbackQueue: The queue to return to after completion. Default value of .main.
- parameter completion: The block to execute.
Expand All @@ -803,13 +826,17 @@ public extension Sequence where Element: ParseInstallation {
any particular order.
*/
func fetchAll(
includeKeys: [String]? = nil,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<[(Result<Element, ParseError>)], ParseError>) -> Void
) {
if (allSatisfy { $0.className == Self.Element.className}) {
let uniqueObjectIds = Set(compactMap { $0.objectId })
let query = Self.Element.query(containedIn(key: "objectId", array: [uniqueObjectIds]))
var query = Self.Element.query(containedIn(key: "objectId", array: [uniqueObjectIds]))
if let include = includeKeys {
query = query.include(include)
}
query.find(options: options, callbackQueue: callbackQueue) { result in
switch result {

Expand Down
20 changes: 14 additions & 6 deletions Sources/ParseSwift/Objects/ParseObject+combine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ public extension ParseObject {
/**
Fetches the `ParseObject` *aynchronously* with the current data from the server and sets an error if one occurs.
Publishes when complete.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- important: If an object fetched has the same objectId as current, it will automatically update the current.
*/
func fetchPublisher(options: API.Options = []) -> Future<Self, ParseError> {
func fetchPublisher(includeKeys: [String]? = nil,
options: API.Options = []) -> Future<Self, ParseError> {
Future { promise in
self.fetch(options: options,
self.fetch(includeKeys: includeKeys,
options: options,
completion: promise)
}
}
Expand Down Expand Up @@ -63,14 +67,18 @@ public extension Sequence where Element: ParseObject {
/**
Fetches a collection of objects *aynchronously* with the current data from the server and sets
an error if one occurs. Publishes when complete.

- parameter includeKeys: The name(s) of the key(s) to include that are
`ParseObject`s. Use `["*"]` to include all keys. This is similar to `include` and
`includeAll` for `Query`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- important: If an object fetched has the same objectId as current, it will automatically update the current.
*/
func fetchAllPublisher(options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
func fetchAllPublisher(includeKeys: [String]? = nil,
options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
Future { promise in
self.fetchAll(options: options,
self.fetchAll(includeKeys: includeKeys,
options: options,
completion: promise)
}
}
Expand Down
Loading