Skip to content

Commit

Permalink
feat: View models now update on the MainActor (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
cbaker6 authored Mar 13, 2023
1 parent 8e7a09e commit 8140c4d
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 295 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
# Parse-Swift Changelog

### main
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.1.1...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.2.0...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
* _Contributing to this repo? Add info about your change here to be included in the next release_

### 5.2.0
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.1.1...5.2.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.2.0/documentation/parseswift)

__New features__
* All Parse-Swift view models now update on the MainActor to make view changes more predictable ([#75](https://github.com/netreconlab/Parse-Swift/pull/75)), thanks to [Corey Baker](https://github.com/cbaker6).

### 5.1.1
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.1.0...5.1.1), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.1.1/documentation/parseswift)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ struct ContentView: View {
TextField("Name", text: $name)
TextField("Points", text: $points)
Button(action: {
//: Code below should normally be in a ViewModel
//: This is just a simple example...
guard let pointsValue = Int(points),
let linkToFile = URL(string: "https://parseplatform.org/img/logo.svg") else {
return
Expand All @@ -111,7 +113,9 @@ struct ContentView: View {
switch result {
case .success:
savedLabel = "Saved score"
self.viewModel.find()
Task {
await self.viewModel.find()
}
case .failure(let error):
savedLabel = "Error: \(error.message)"
}
Expand All @@ -136,14 +140,14 @@ struct ContentView: View {
}
}
Spacer()
}.onAppear(perform: {
viewModel.find()
}).alert(isPresented: $isShowingAction, content: {
}.task {
await viewModel.find()
}.alert(isPresented: $isShowingAction) {
Alert(title: Text("GameScore"),
message: Text(savedLabel),
dismissButton: .default(Text("Ok"), action: {
}))
})
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ struct ContentView: View {
var body: some View {
VStack {

if subscription.subscribed != nil {
if subscription.isSubscribed {
Text("Subscribed to query!")
} else if subscription.unsubscribed != nil {
} else if subscription.isUnsubscribed {
Text("Unsubscribed from query!")
} else if let event = subscription.event {

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,6 @@ You are not limited to a single Live Query Client - you can create multiple inst
## Migrating from Older Versions and SDKs

1. See the [discussion](https://github.com/netreconlab/Parse-Swift/discussions/74) to learn how to migrate from Parse-Swift<sup>OG</sup> 4.15.0+ to 5.1.1+
1. See the [discussion](https://github.com/netreconlab/Parse-Swift/discussions/70) to learn how to migrate from the [Parse-Swift](https://github.com/parse-community/Parse-Swift)
1. See the [discussion](https://github.com/netreconlab/Parse-Swift/discussions/71) to learn how to migrate from the [Parse-SDK-iOS-OSX](https://github.com/parse-community/Parse-SDK-iOS-OSX)
1. See the [discussion](https://github.com/netreconlab/Parse-Swift/discussions/70) to learn how to migrate from [parse-community/Parse-Swift](https://github.com/parse-community/Parse-Swift)
1. See the [discussion](https://github.com/netreconlab/Parse-Swift/discussions/71) to learn how to migrate from [Parse-SDK-iOS-OSX](https://github.com/parse-community/Parse-SDK-iOS-OSX)

28 changes: 18 additions & 10 deletions Sources/ParseSwift/LiveQuery/Subscription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,41 @@ open class Subscription<T: ParseObject>: QueryViewModel<T>, QuerySubscribable {
if newValue != nil {
subscribed = nil
unsubscribed = nil
DispatchQueue.main.async {
self.objectWillChange.send()
}
self.objectWillChange.send()
}
}
}

/// If **true** the LiveQuery subscription is currently active,
/// **false** otherwise.
open var isSubscribed: Bool {
subscribed != nil
}

/// Updates and notifies when a subscription request has been fulfilled and if it is new.
open var subscribed: (query: Query<T>, isNew: Bool)? {
willSet {
if newValue != nil {
unsubscribed = nil
event = nil
DispatchQueue.main.async {
self.objectWillChange.send()
}
self.objectWillChange.send()
}
}
}

/// If **true** the LiveQuery subscription is currently inactive,
/// **false** otherwise.
open var isUnsubscribed: Bool {
unsubscribed != nil
}

/// Updates and notifies when an unsubscribe request has been fulfilled.
open var unsubscribed: Query<T>? {
willSet {
if newValue != nil {
subscribed = nil
event = nil
DispatchQueue.main.async {
self.objectWillChange.send()
}
self.objectWillChange.send()
}
}
}
Expand All @@ -69,7 +75,7 @@ open class Subscription<T: ParseObject>: QueryViewModel<T>, QuerySubscribable {
}

// MARK: QuerySubscribable

@MainActor
open func didReceive(_ eventData: Data) throws {
// Need to decode the event with respect to the `ParseObject`.
let eventMessage = try ParseCoding.jsonDecoder().decode(EventResponse<T>.self, from: eventData)
Expand All @@ -79,10 +85,12 @@ open class Subscription<T: ParseObject>: QueryViewModel<T>, QuerySubscribable {
self.event = (query, event)
}

@MainActor
open func didSubscribe(_ new: Bool) {
self.subscribed = (query, new)
}

@MainActor
open func didUnsubscribe() {
self.unsubscribed = query
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/ParseConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation

enum ParseConstants {
static let sdk = "swift"
static let version = "5.1.1"
static let version = "5.2.1"
static let fileManagementDirectory = "parse/"
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
static let fileManagementLibraryDirectory = "Library/"
Expand Down
8 changes: 5 additions & 3 deletions Sources/ParseSwift/Protocols/CloudObservable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Created by Corey Baker on 7/11/21.
// Copyright © 2021 Network Reconnaissance Lab. All rights reserved.
//
#if canImport(SwiftUI)
#if canImport(Combine)
import Foundation

/**
Expand All @@ -27,12 +27,14 @@ public protocol CloudObservable: ObservableObject {
when the result of its execution.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
*/
func runFunction(options: API.Options)
@MainActor
func runFunction(options: API.Options) async

/**
Starts a Cloud Code Job *asynchronously* and updates the view model with the result and jobStatusId of the job.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
*/
func startJob(options: API.Options)
@MainActor
func startJob(options: API.Options) async
}
#endif
17 changes: 11 additions & 6 deletions Sources/ParseSwift/Protocols/QueryObservable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright © 2021 Network Reconnaissance Lab. All rights reserved.
//

#if canImport(SwiftUI)
#if canImport(Combine)
import Foundation

/**
Expand All @@ -32,7 +32,8 @@ public protocol QueryObservable: ObservableObject {
- parameter options: A set of header options sent to the server. Defaults to an empty set.
*/
func find(options: API.Options)
@MainActor
func find(options: API.Options) async

/**
Retrieves *asynchronously* a complete list of `ParseObject`'s that satisfy this query
Expand All @@ -42,24 +43,27 @@ public protocol QueryObservable: ObservableObject {
- warning: The items are processed in an unspecified order. The query may not have any sort
order, and may not use limit or skip.
*/
@MainActor
func findAll(batchLimit: Int?,
options: API.Options)
options: API.Options) async

/**
Gets an object *asynchronously* and updates the view model when complete.
- warning: This method mutates the query. It will reset the limit to `1`.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
*/
func first(options: API.Options)
@MainActor
func first(options: API.Options) async

/**
Counts objects *synchronously* based on the constructed query and updates the view model
when complete.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
*/
func count(options: API.Options)
@MainActor
func count(options: API.Options) async

/**
Executes an aggregate query *asynchronously* and updates the view model when complete.
Expand All @@ -70,7 +74,8 @@ public protocol QueryObservable: ObservableObject {
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- warning: This has not been tested thoroughly.
*/
@MainActor
func aggregate(_ pipeline: [[String: Encodable]],
options: API.Options)
options: API.Options) async
}
#endif
40 changes: 15 additions & 25 deletions Sources/ParseSwift/Types/CloudViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Created by Corey Baker on 7/11/21.
// Copyright © 2021 Network Reconnaissance Lab. All rights reserved.
//
#if canImport(SwiftUI)
#if canImport(Combine)
import Foundation

/**
Expand All @@ -24,9 +24,7 @@ open class CloudViewModel<T: ParseCloudable>: CloudObservable {
willSet {
if newValue != nil {
self.error = nil
DispatchQueue.main.async {
self.objectWillChange.send()
}
self.objectWillChange.send()
}
}
}
Expand All @@ -36,9 +34,7 @@ open class CloudViewModel<T: ParseCloudable>: CloudObservable {
willSet {
if newValue != nil {
self.results = nil
DispatchQueue.main.async {
self.objectWillChange.send()
}
self.objectWillChange.send()
}
}
}
Expand All @@ -47,27 +43,21 @@ open class CloudViewModel<T: ParseCloudable>: CloudObservable {
self.cloudCode = cloudCode
}

public func runFunction(options: API.Options = []) {
cloudCode.runFunction(options: options) { results in
switch results {

case .success(let results):
self.results = results
case .failure(let error):
self.error = error
}
@MainActor
public func runFunction(options: API.Options = []) async {
do {
self.results = try await cloudCode.runFunction(options: options)
} catch {
self.error = error as? ParseError ?? ParseError(swift: error)
}
}

public func startJob(options: API.Options = []) {
cloudCode.startJob(options: options) { results in
switch results {

case .success(let results):
self.results = results
case .failure(let error):
self.error = error
}
@MainActor
public func startJob(options: API.Options = []) async {
do {
self.results = try await cloudCode.startJob(options: options)
} catch {
self.error = error as? ParseError ?? ParseError(swift: error)
}
}
}
Expand Down
Loading

0 comments on commit 8140c4d

Please sign in to comment.