diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 651f6e84e..9d3a2b38e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: action: 'test' steps: - uses: actions/checkout@v4 - - name: Install SwiftLint + - name: Install Extra Packages run: brew install swiftlint - name: Create and set the default keychain run: | @@ -46,7 +46,7 @@ jobs: security unlock-keychain -p "" temporary security set-keychain-settings -lut 7200 temporary - name: Build-Test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -derivedDataPath DerivedData -destination ${{ matrix.destination }} ${{ matrix.action }} | xcpretty -c + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -derivedDataPath DerivedData -destination ${{ matrix.destination }} ${{ matrix.action }} 2>&1 | xcbeautify --renderer github-actions env: DEVELOPER_DIR: ${{ env.CI_XCODE_LATEST }} - name: Prepare codecov @@ -80,7 +80,7 @@ jobs: - name: Install SwiftLint run: brew install swiftlint - name: Build-Test - run: set -o pipefail && env NSUnbufferedIO=YES swift test --enable-code-coverage | xcpretty -c + run: set -o pipefail && env NSUnbufferedIO=YES swift test --enable-code-coverage 2>&1 | xcbeautify --renderer github-actions env: DEVELOPER_DIR: ${{ env.CI_XCODE_LATEST }} - name: Prepare codecov @@ -108,7 +108,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build-Test - run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -destination platform\=iOS\ Simulator,name\=iPhone\ 13\ Pro\ Max -derivedDataPath DerivedData build | xcpretty -c + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -destination platform\=iOS\ Simulator,name\=iPhone\ 13\ Pro\ Max -derivedDataPath DerivedData build 2>&1 | xcbeautify --renderer github-actions env: DEVELOPER_DIR: ${{ env.CI_XCODE_OLDEST }} @@ -141,11 +141,11 @@ jobs: - uses: actions/checkout@v4 - uses: compnerd/gha-setup-swift@v0.2.3 with: - branch: swift-5.10-release - tag: 5.10-RELEASE + branch: swift-5.10.1-release + tag: 5.10.1-RELEASE - name: Build run: | - swift build -v + swift build --enable-test-discovery -v - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index a8dd3ade4..a5f257823 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,15 @@ # Parse-Swift Changelog ### main -[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.11.2...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift) +[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.11.3...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.11.3 +[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.11.2...5.11.3), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.11.3/documentation/parseswift) + +__Fixes__ +* Remove all double conformance of Hashable and Equatable ([#182](https://github.com/netreconlab/Parse-Swift/pull/182)), thanks to [Corey Baker](https://github.com/cbaker6). + ### 5.11.2 [Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.11.1...5.11.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.11.2/documentation/parseswift) diff --git a/Sources/ParseSwift/API/API+NonParseBodyCommand.swift b/Sources/ParseSwift/API/API+NonParseBodyCommand.swift index 0744b8c1b..77265d773 100644 --- a/Sources/ParseSwift/API/API+NonParseBodyCommand.swift +++ b/Sources/ParseSwift/API/API+NonParseBodyCommand.swift @@ -145,7 +145,7 @@ internal extension API.NonParseBodyCommand { objectsSavedBeforeThisOne: [String: PointerType]?, // swiftlint:disable:next line_length filesSavedBeforeThisOne: [String: ParseFile]?) async throws -> RESTBatchCommandTypeEncodablePointer { - try await yieldIfNotInitialized() + let defaultACL = try? await ParseACL.defaultACL() let batchCommands = try objects.compactMap { (object) -> API.BatchCommand? in guard var objectable = object as? Objectable else { diff --git a/Sources/ParseSwift/Coding/ParseEncoder.swift b/Sources/ParseSwift/Coding/ParseEncoder.swift index 96f949116..19379e439 100644 --- a/Sources/ParseSwift/Coding/ParseEncoder.swift +++ b/Sources/ParseSwift/Coding/ParseEncoder.swift @@ -50,9 +50,11 @@ extension Dictionary: _JSONStringDictionaryEncodableMarker where Key == String, /** An object that encodes Parse instances of a data type as JSON objects. - note: `JSONEncoder` facilitates the encoding of `Encodable` values into JSON. `ParseEncoder` facilitates the encoding of `ParseEncodable` values into JSON. - All Credit to Apple, this is a custom encoder with capability of skipping keys at runtime. - ParseEncoder matches the features of the [Swift 5.4 JSONEncoder ](https://github.com/apple/swift/blob/main/stdlib/public/Darwin/Foundation/JSONEncoder.swift). - Update commits as needed for improvement. + All Credit to Apple for the baseline encoder. The `ParseEncoder` is a custom encoder + with capability of skipping keys at runtime and encoding objects into a digestable format + for a [parse-server](https://github.com/parse-community/parse-server). + `ParseEncoder` matches the features of the [Swift 5.4 JSONEncoder ](https://github.com/apple/swift/blob/main/stdlib/public/Darwin/Foundation/JSONEncoder.swift). + Update comments as needed for improvement. */ public struct ParseEncoder: Sendable { let dateEncodingStrategy: JSONEncoder.DateEncodingStrategy? @@ -109,11 +111,13 @@ public struct ParseEncoder: Sendable { self.outputFormatting = outputFormatting } - func encode(_ value: Encodable, - acl: ParseACL? = nil, - batching: Bool = false, - objectsSavedBeforeThisOne: [String: PointerType]? = nil, - filesSavedBeforeThisOne: [String: ParseFile]? = nil) throws -> Data { + func encode( + _ value: Encodable, + acl: ParseACL? = nil, + batching: Bool = false, + objectsSavedBeforeThisOne: [String: PointerType]? = nil, + filesSavedBeforeThisOne: [String: ParseFile]? = nil + ) throws -> Data { var keysToSkip = SkipKeys.none.keys() if batching { keysToSkip = SkipKeys.object.keys() @@ -137,53 +141,76 @@ public struct ParseEncoder: Sendable { /** Encodes an instance of the indicated `ParseEncodable`. - parameter value: The `ParseEncodable` instance to encode. + - parameter acl: The `ParseACL` to add to the value if it is an `ParseObject`. Defaults to `nil`. - parameter skipKeys: The set of keys to skip during encoding. */ - public func encode(_ value: T, - acl: ParseACL? = nil, - skipKeys: SkipKeys) throws -> Data { - let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: skipKeys.keys()) + public func encode( + _ value: T, + acl: ParseACL? = nil, + skipKeys: SkipKeys + ) throws -> Data { + let encoder = _ParseEncoder( + codingPath: [], + dictionary: NSMutableDictionary(), + skippingKeys: skipKeys.keys() + ) if let dateEncodingStrategy = dateEncodingStrategy { encoder.dateEncodingStrategy = dateEncodingStrategy } if let outputFormatting = outputFormatting { encoder.outputFormatting = outputFormatting } - return try encoder.encodeObject(value, - acl: acl, - collectChildren: false, - uniquePointer: nil, - objectsSavedBeforeThisOne: nil, - filesSavedBeforeThisOne: nil).encoded + let encodedData = try encoder.encodeObject( + value, + acl: acl, + collectChildren: false, + uniquePointer: nil, + objectsSavedBeforeThisOne: nil, + filesSavedBeforeThisOne: nil + ).encoded + + return encodedData } // swiftlint:disable large_tuple - internal func encode(_ value: T, - acl: ParseACL? = nil, - collectChildren: Bool, - objectsSavedBeforeThisOne: [String: PointerType]?, - filesSavedBeforeThisOne: [String: ParseFile]?) throws -> (encoded: Data, - unique: PointerType?, - unsavedChildren: [Encodable]) { + func encode( + _ value: T, + acl: ParseACL? = nil, + collectChildren: Bool, + objectsSavedBeforeThisOne: [String: PointerType]?, + filesSavedBeforeThisOne: [String: ParseFile]? + ) throws -> ( + encoded: Data, + unique: PointerType?, + unsavedChildren: [Encodable] + ) { let keysToSkip: Set! if !Parse.configuration.isRequiringCustomObjectIds { keysToSkip = SkipKeys.object.keys() } else { keysToSkip = SkipKeys.customObjectId.keys() } - let encoder = _ParseEncoder(codingPath: [], dictionary: NSMutableDictionary(), skippingKeys: keysToSkip) + let encoder = _ParseEncoder( + codingPath: [], + dictionary: NSMutableDictionary(), + skippingKeys: keysToSkip + ) if let dateEncodingStrategy = dateEncodingStrategy { encoder.dateEncodingStrategy = dateEncodingStrategy } if let outputFormatting = outputFormatting { encoder.outputFormatting = outputFormatting } - return try encoder.encodeObject(value, - acl: acl, - collectChildren: collectChildren, - uniquePointer: try? value.toPointer(), - objectsSavedBeforeThisOne: objectsSavedBeforeThisOne, - filesSavedBeforeThisOne: filesSavedBeforeThisOne) + let encodedObject = try encoder.encodeObject( + value, + acl: acl, + collectChildren: collectChildren, + uniquePointer: try? value.toPointer(), + objectsSavedBeforeThisOne: objectsSavedBeforeThisOne, + filesSavedBeforeThisOne: filesSavedBeforeThisOne + ) + + return encodedObject } internal func encode(_ value: ParseEncodable, diff --git a/Sources/ParseSwift/LiveQuery/LiveQueryConstants.swift b/Sources/ParseSwift/LiveQuery/LiveQueryConstants.swift index dcd99be38..61389336a 100644 --- a/Sources/ParseSwift/LiveQuery/LiveQueryConstants.swift +++ b/Sources/ParseSwift/LiveQuery/LiveQueryConstants.swift @@ -16,7 +16,7 @@ import Foundation - Updated: The object has been updated, and is still a part of the query. - Deleted: The object has been deleted, and is no longer included in the query. */ -public enum Event: Equatable { +public enum Event: Equatable, Sendable { /// The object has been updated, and is now included in the query. case entered(T) diff --git a/Sources/ParseSwift/LiveQuery/Operations.swift b/Sources/ParseSwift/LiveQuery/Operations.swift index 146fd5189..32178339b 100644 --- a/Sources/ParseSwift/LiveQuery/Operations.swift +++ b/Sources/ParseSwift/LiveQuery/Operations.swift @@ -26,6 +26,6 @@ enum OperationErrorResponse: String, Codable { // An opaque placeholder structed used to ensure that we type-safely create request IDs and do not shoot ourself in // the foot with array indexes. -struct RequestId: Hashable, Equatable, Codable { +struct RequestId: Hashable, Codable { let value: Int } diff --git a/Sources/ParseSwift/Objects/ParseObject.swift b/Sources/ParseSwift/Objects/ParseObject.swift index 9659993b0..d6737c75e 100644 --- a/Sources/ParseSwift/Objects/ParseObject.swift +++ b/Sources/ParseSwift/Objects/ParseObject.swift @@ -44,8 +44,7 @@ public protocol ParseObject: ParseTypeable, Fetchable, Savable, Deletable, - Identifiable, - Hashable { + Identifiable { /** A JSON encoded version of this `ParseObject` before `.set()` or diff --git a/Sources/ParseSwift/ParseConstants.swift b/Sources/ParseSwift/ParseConstants.swift index 1e49476c5..9ce9ae517 100644 --- a/Sources/ParseSwift/ParseConstants.swift +++ b/Sources/ParseSwift/ParseConstants.swift @@ -10,7 +10,7 @@ import Foundation enum ParseConstants { static let sdk = "swift" - static let version = "5.11.2" + static let version = "5.11.3" static let fileManagementDirectory = "parse/" static let fileManagementPrivateDocumentsDirectory = "Private Documents/" static let fileManagementLibraryDirectory = "Library/" diff --git a/Sources/ParseSwift/Protocols/ParseConfig.swift b/Sources/ParseSwift/Protocols/ParseConfig.swift index 0bc63ad03..4ecd6e9bd 100644 --- a/Sources/ParseSwift/Protocols/ParseConfig.swift +++ b/Sources/ParseSwift/Protocols/ParseConfig.swift @@ -94,12 +94,12 @@ extension ParseConfig { } } -internal struct ConfigUpdateBody: ParseTypeable, Decodable where T: ParseConfig { +internal struct ConfigUpdateBody: ParseTypeable where T: ParseConfig { let params: T } // MARK: Current -struct CurrentConfigContainer: Codable, Equatable { +struct CurrentConfigContainer: Codable, Hashable { var currentConfig: T? } diff --git a/Sources/ParseSwift/Protocols/ParsePointerable.swift b/Sources/ParseSwift/Protocols/ParsePointerable.swift index 26b8c8830..e4712f111 100644 --- a/Sources/ParseSwift/Protocols/ParsePointerable.swift +++ b/Sources/ParseSwift/Protocols/ParsePointerable.swift @@ -29,7 +29,7 @@ extension ParsePointer { } } -public protocol ParsePointerObject: ParsePointer, ParseTypeable, Fetchable, Hashable { +public protocol ParsePointerObject: ParsePointer, ParseTypeable, Fetchable { associatedtype Object: ParseObject } diff --git a/Sources/ParseSwift/Protocols/ParseTypeable.swift b/Sources/ParseSwift/Protocols/ParseTypeable.swift index 2bf8bf6a9..4ec8a30b2 100644 --- a/Sources/ParseSwift/Protocols/ParseTypeable.swift +++ b/Sources/ParseSwift/Protocols/ParseTypeable.swift @@ -11,8 +11,8 @@ import Foundation /** A special type that is considered a Parse type. */ -public protocol ParseTypeable: Codable, - Sendable, +public protocol ParseTypeable: ParseEncodable, + Codable, Hashable, CustomDebugStringConvertible, CustomStringConvertible {} diff --git a/Sources/ParseSwift/Types/ParseACL.swift b/Sources/ParseSwift/Types/ParseACL.swift index a401c4b42..8c6479e0f 100644 --- a/Sources/ParseSwift/Types/ParseACL.swift +++ b/Sources/ParseSwift/Types/ParseACL.swift @@ -17,8 +17,7 @@ import Foundation **the public** so that, for example, any user could read a particular object but only a particular set of users could write to that object. */ -public struct ParseACL: ParseTypeable, - Hashable { +public struct ParseACL: ParseTypeable { private static let publicScope = "*" private var acl: [String: [Access: Bool]]? diff --git a/Sources/ParseSwift/Types/ParseAnalytics.swift b/Sources/ParseSwift/Types/ParseAnalytics.swift index 720d0657c..f470f1372 100644 --- a/Sources/ParseSwift/Types/ParseAnalytics.swift +++ b/Sources/ParseSwift/Types/ParseAnalytics.swift @@ -14,7 +14,7 @@ import UIKit /** `ParseAnalytics` provides an interface to Parse's logging and analytics backend. */ -public struct ParseAnalytics: ParseTypeable, Hashable { +public struct ParseAnalytics: ParseTypeable { /// The name of the custom event to report to Parse as having happened. public var name: String diff --git a/Sources/ParseSwift/Types/ParseBytes.swift b/Sources/ParseSwift/Types/ParseBytes.swift index 0c1eed99c..a54712d0f 100644 --- a/Sources/ParseSwift/Types/ParseBytes.swift +++ b/Sources/ParseSwift/Types/ParseBytes.swift @@ -11,7 +11,7 @@ import Foundation /** `ParseBytes` is used to store base 64 data. */ -public struct ParseBytes: ParseTypeable, Hashable { +public struct ParseBytes: ParseTypeable { private let __type: String = "Bytes" // swiftlint:disable:this identifier_name public let base64: String diff --git a/Sources/ParseSwift/Types/ParseGeoPoint.swift b/Sources/ParseSwift/Types/ParseGeoPoint.swift index 526b081ce..c5f39f4db 100644 --- a/Sources/ParseSwift/Types/ParseGeoPoint.swift +++ b/Sources/ParseSwift/Types/ParseGeoPoint.swift @@ -9,7 +9,7 @@ import CoreLocation - warning:Currently, instances of `ParseObject` may only have one key associated with a `ParseGeoPoint` type. */ -public struct ParseGeoPoint: ParseTypeable, Hashable { +public struct ParseGeoPoint: ParseTypeable { private let __type: String = "GeoPoint" // swiftlint:disable:this identifier_name static let earthRadiusMiles = 3958.8 static let earthRadiusKilometers = 6371.0 diff --git a/Sources/ParseSwift/Types/ParseKeychainAccessGroup.swift b/Sources/ParseSwift/Types/ParseKeychainAccessGroup.swift index 13bf15f21..6e4080ab5 100644 --- a/Sources/ParseSwift/Types/ParseKeychainAccessGroup.swift +++ b/Sources/ParseSwift/Types/ParseKeychainAccessGroup.swift @@ -9,7 +9,7 @@ #if !os(Linux) && !os(Android) && !os(Windows) import Foundation -struct ParseKeychainAccessGroup: ParseTypeable, Hashable { +struct ParseKeychainAccessGroup: ParseTypeable { var accessGroup: String? var isSyncingKeychainAcrossDevices = false diff --git a/Sources/ParseSwift/Types/ParsePolygon.swift b/Sources/ParseSwift/Types/ParsePolygon.swift index 0ee84c719..5563f017d 100644 --- a/Sources/ParseSwift/Types/ParsePolygon.swift +++ b/Sources/ParseSwift/Types/ParsePolygon.swift @@ -11,7 +11,7 @@ that may be associated with a key in a `ParseObject` or used as a reference point for geo queries. This allows proximity-based queries on the key. */ -public struct ParsePolygon: ParseTypeable, Hashable { +public struct ParsePolygon: ParseTypeable { private let __type: String = "Polygon" // swiftlint:disable:this identifier_name public let coordinates: [ParseGeoPoint] var isSwappingCoordinates = false diff --git a/Sources/ParseSwift/Types/ParseRelation.swift b/Sources/ParseSwift/Types/ParseRelation.swift index 1b309ee65..74ece8309 100644 --- a/Sources/ParseSwift/Types/ParseRelation.swift +++ b/Sources/ParseSwift/Types/ParseRelation.swift @@ -15,7 +15,7 @@ import Foundation In most cases, you do not need to create an instance of `ParseRelation` directly as it can be indirectly created from any `ParseObject` by using the respective `relation` property. */ -public struct ParseRelation: ParseTypeable, Hashable where T: ParseObject { +public struct ParseRelation: ParseTypeable where T: ParseObject { internal let __type: String = "Relation" // swiftlint:disable:this identifier_name /// The parent `ParseObject` diff --git a/Sources/ParseSwift/Types/ParseSchema.swift b/Sources/ParseSwift/Types/ParseSchema.swift index e7451f7ae..f523aa33c 100644 --- a/Sources/ParseSwift/Types/ParseSchema.swift +++ b/Sources/ParseSwift/Types/ParseSchema.swift @@ -14,7 +14,7 @@ import Foundation use the primary key in server-side applications where the key is kept secure and not exposed to the public. */ -public struct ParseSchema: ParseTypeable, Decodable, Equatable { +public struct ParseSchema: ParseTypeable { /// The class name of the `ParseSchema`. public var className: String diff --git a/Sources/ParseSwift/Types/ParseVersion.swift b/Sources/ParseSwift/Types/ParseVersion.swift index d543161ff..78720404b 100644 --- a/Sources/ParseSwift/Types/ParseVersion.swift +++ b/Sources/ParseSwift/Types/ParseVersion.swift @@ -10,7 +10,7 @@ import Foundation /// `ParseVersion` is used to determine the version of the SDK. The current /// version of the SDK is persisted to the Keychain. -public struct ParseVersion: ParseTypeable, Hashable { +public struct ParseVersion: ParseTypeable { var major: Int var minor: Int diff --git a/Sources/ParseSwift/Types/QueryConstraint.swift b/Sources/ParseSwift/Types/QueryConstraint.swift index 11bd94010..97ae80680 100644 --- a/Sources/ParseSwift/Types/QueryConstraint.swift +++ b/Sources/ParseSwift/Types/QueryConstraint.swift @@ -9,7 +9,7 @@ import Foundation /// Used to constrain a query. -public struct QueryConstraint: ParseTypeable, Hashable { +public struct QueryConstraint: ParseTypeable { enum Comparator: String, CodingKey, Codable, CaseIterable { case lessThan = "$lt" case lessThanOrEqualTo = "$lte"