diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4abfe0a47..390b1f690 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: env: CI_XCODE_OLDEST: '/Applications/Xcode_14.2.app/Contents/Developer' CI_XCODE_14: '/Applications/Xcode_14.3.1.app/Contents/Developer' - CI_XCODE_LATEST: '/Applications/Xcode_15.4.app/Contents/Developer' + CI_XCODE_LATEST: '/Applications/Xcode_16.2.app/Contents/Developer' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -19,21 +19,21 @@ concurrency: jobs: test: timeout-minutes: 25 - runs-on: macos-14 + runs-on: macos-15 strategy: matrix: - destination: ['platform=iOS\ Simulator,OS=17.5,name=iPhone\ 15\ Pro\ Max', 'platform\=tvOS\ Simulator,OS=17.5,name\=Apple\ TV', 'platform=watchOS\ Simulator,name=Apple\ Watch\ Series\ 9\ \(41mm\)', 'platform=macOS', 'platform=visionOS\ Simulator,OS=1.2,name=Apple\ Vision\ Pro'] + destination: ['platform=iOS\ Simulator,OS=18.2,name=iPhone\ 16\ Pro\ Max', 'platform\=tvOS\ Simulator,OS=18.2,name\=Apple\ TV', 'platform=watchOS\ Simulator,name=Apple\ Watch\ Series\ 10\ \(42mm\)', 'platform=macOS', 'platform=visionOS\ Simulator,OS=2.2,name=Apple\ Vision\ Pro'] action: ['test', 'build'] exclude: - - destination: 'platform=iOS\ Simulator,OS=17.5,name=iPhone\ 15\ Pro\ Max' + - destination: 'platform=iOS\ Simulator,OS=18.2,name=iPhone\ 16\ Pro\ Max' action: 'build' - - destination: 'platform\=tvOS\ Simulator,OS=17.5,name\=Apple\ TV' + - destination: 'platform\=tvOS\ Simulator,OS=18.2,name\=Apple\ TV' action: 'build' - destination: 'platform=macOS' action: 'build' - - destination: 'platform=visionOS\ Simulator,OS=1.2,name=Apple\ Vision\ Pro' + - destination: 'platform=visionOS\ Simulator,OS=2.2,name=Apple\ Vision\ Pro' action: 'test' - - destination: 'platform=watchOS\ Simulator,name=Apple\ Watch\ Series\ 9\ \(41mm\)' + - destination: 'platform=watchOS\ Simulator,name=Apple\ Watch\ Series\ 10\ \(42mm\)' action: 'test' steps: - uses: actions/checkout@v4 @@ -55,6 +55,7 @@ jobs: with: format: lcov search-paths: ./DerivedData + ignore-conversion-failures: true env: DEVELOPER_DIR: ${{ env.CI_XCODE_LATEST }} - name: Upload coverage to Codecov @@ -68,7 +69,7 @@ jobs: spm-test: timeout-minutes: 25 - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Create and set the default keychain @@ -89,6 +90,7 @@ jobs: with: format: lcov search-paths: ./.build + ignore-conversion-failures: true env: DEVELOPER_DIR: ${{ env.CI_XCODE_LATEST }} - name: Upload coverage to Codecov @@ -104,11 +106,11 @@ jobs: xcode-test-5_7: timeout-minutes: 25 needs: linux - runs-on: macos-12 + runs-on: macos-13 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 2>&1 | xcbeautify --renderer github-actions + run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace Parse.xcworkspace -scheme ParseSwift -destination platform\=iOS\ Simulator,name\=iPhone\ 14\ Pro\ Max -derivedDataPath DerivedData build 2>&1 | xcbeautify --renderer github-actions env: DEVELOPER_DIR: ${{ env.CI_XCODE_OLDEST }} @@ -156,7 +158,7 @@ jobs: docs: timeout-minutes: 10 needs: linux - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Generate Docs @@ -166,7 +168,7 @@ jobs: cocoapods: needs: linux - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Update Framework Version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb211ec90..d3f34b0ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,11 +4,11 @@ on: types: [published] env: CI_XCODE_14: '/Applications/Xcode_14.3.1.app/Contents/Developer' - CI_XCODE_LATEST: '/Applications/Xcode_15.4.app/Contents/Developer' + CI_XCODE_LATEST: '/Applications/Xcode_16.1.app/Contents/Developer' jobs: cocoapods: - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Get release version @@ -24,7 +24,7 @@ jobs: DEVELOPER_DIR: ${{ env.CI_XCODE_LATEST }} docs: - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 - name: Get release version diff --git a/.swiftlint.yml b/.swiftlint.yml index bc875a404..223f29c23 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -3,6 +3,7 @@ disabled_rules: - identifier_name - blanket_disable_command - non_optional_string_data_conversion + - optional_data_string_conversion excluded: # paths to ignore during linting. Takes precedence over `included`. - Tests/ParseSwiftTests/ParseEncoderTests - DerivedData diff --git a/Sources/ParseSwift/API/API+Command.swift b/Sources/ParseSwift/API/API+Command.swift index e65003857..1d7bec4e7 100644 --- a/Sources/ParseSwift/API/API+Command.swift +++ b/Sources/ParseSwift/API/API+Command.swift @@ -100,7 +100,7 @@ internal extension API { allowIntermediateResponses: Bool = false, uploadProgress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil, downloadProgress: ((URLSessionDownloadTask, Int64, Int64, Int64) -> Void)? = nil, - completion: @escaping(Result) -> Void) async { + completion: @escaping (Result) -> Void) async { let currentNotificationQueue: DispatchQueue! if let notificationQueue = notificationQueue { currentNotificationQueue = notificationQueue @@ -257,7 +257,7 @@ internal extension API { batching: Bool = false, childObjects: [String: PointerType]? = nil, childFiles: [String: ParseFile]? = nil, - completion: @escaping(Result) -> Void) { + completion: @escaping (Result) -> Void) { let params = self.params?.getURLQueryItems() Task { do { diff --git a/Sources/ParseSwift/API/API+NonParseBodyCommand.swift b/Sources/ParseSwift/API/API+NonParseBodyCommand.swift index 77265d773..c5ac841d7 100644 --- a/Sources/ParseSwift/API/API+NonParseBodyCommand.swift +++ b/Sources/ParseSwift/API/API+NonParseBodyCommand.swift @@ -37,7 +37,7 @@ internal extension API { func execute(options: API.Options, callbackQueue: DispatchQueue, allowIntermediateResponses: Bool = false, - completion: @escaping(Result) -> Void) async { + completion: @escaping (Result) -> Void) async { switch await self.prepareURLRequest(options: options) { case .success(let urlRequest): diff --git a/Sources/ParseSwift/Extensions/URLSession.swift b/Sources/ParseSwift/Extensions/URLSession.swift index 78cba485c..6edaebdeb 100644 --- a/Sources/ParseSwift/Extensions/URLSession.swift +++ b/Sources/ParseSwift/Extensions/URLSession.swift @@ -143,7 +143,7 @@ internal extension URLSession { attempts: Int = 1, allowIntermediateResponses: Bool, mapper: @escaping (Data) async throws -> U, - completion: @escaping(Result) -> Void + completion: @escaping (Result) -> Void ) async { do { let (responseData, urlResponse) = try await dataTask(for: request) @@ -288,7 +288,7 @@ internal extension URLSession { from file: URL?, progress: ((URLSessionTask, Int64, Int64, Int64) -> Void)?, mapper: @escaping (Data) async throws -> U, - completion: @escaping(Result) -> Void + completion: @escaping (Result) -> Void ) { var task: URLSessionTask? if let data = data { @@ -354,7 +354,7 @@ internal extension URLSession { with request: URLRequest, progress: ((URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?, mapper: @escaping (Data) async throws -> U, - completion: @escaping(Result) -> Void + completion: @escaping (Result) -> Void ) async { let task = downloadTask(with: request) { (location, urlResponse, responseError) in Task { @@ -374,7 +374,7 @@ internal extension URLSession { func downloadTask( with request: URLRequest, mapper: @escaping (Data) async throws -> U, - completion: @escaping(Result) -> Void + completion: @escaping (Result) -> Void ) { Task { do { diff --git a/Sources/ParseSwift/Storage/ParseFileManager.swift b/Sources/ParseSwift/Storage/ParseFileManager.swift index 357d2b2b1..8a2dadbad 100644 --- a/Sources/ParseSwift/Storage/ParseFileManager.swift +++ b/Sources/ParseSwift/Storage/ParseFileManager.swift @@ -112,7 +112,7 @@ extension ParseFileManager { } } - func writeString(_ string: String, filePath: URL, completion: @escaping(Error?) -> Void) { + func writeString(_ string: String, filePath: URL, completion: @escaping (Error?) -> Void) { synchronizationQueue.async { do { guard let data = string.data(using: .utf8) else { @@ -127,7 +127,7 @@ extension ParseFileManager { } } - func writeData(_ data: Data, filePath: URL, completion: @escaping(Error?) -> Void) { + func writeData(_ data: Data, filePath: URL, completion: @escaping (Error?) -> Void) { synchronizationQueue.async { do { try data.write(to: filePath, options: self.defaultDataWritingOptions) @@ -138,7 +138,7 @@ extension ParseFileManager { } } - func copyItem(_ fromPath: URL, toPath: URL, completion: @escaping(Error?) -> Void) { + func copyItem(_ fromPath: URL, toPath: URL, completion: @escaping (Error?) -> Void) { synchronizationQueue.async { do { try FileManager.default.copyItem(at: fromPath, to: toPath) @@ -149,7 +149,7 @@ extension ParseFileManager { } } - func moveItem(_ fromPath: URL, toPath: URL, completion: @escaping(Error?) -> Void) { + func moveItem(_ fromPath: URL, toPath: URL, completion: @escaping (Error?) -> Void) { synchronizationQueue.async { if fromPath != toPath { do { @@ -164,7 +164,7 @@ extension ParseFileManager { } } - func moveContentsOfDirectory(_ fromPath: URL, toPath: URL, completion: @escaping(Error?) -> Void) { + func moveContentsOfDirectory(_ fromPath: URL, toPath: URL, completion: @escaping (Error?) -> Void) { synchronizationQueue.async { do { if fromPath == toPath { diff --git a/Sources/ParseSwift/Types/ParsePushPayload/Firebase/ParsePushFirebaseNotification.swift b/Sources/ParseSwift/Types/ParsePushPayload/Firebase/ParsePushFirebaseNotification.swift index bae64cfd7..ec48117fb 100644 --- a/Sources/ParseSwift/Types/ParsePushPayload/Firebase/ParsePushFirebaseNotification.swift +++ b/Sources/ParseSwift/Types/ParsePushPayload/Firebase/ParsePushFirebaseNotification.swift @@ -128,8 +128,8 @@ public struct ParsePushFirebaseNotification: ParseTypeable { enum CodingKeys: String, CodingKey { case titleLocKey = "title_loc_key" case titleLocArgs = "title_loc_args" - case bodyLocKey = "body_loc-key" - case bodyLocArgs = "body-loc-args" + case bodyLocKey = "body_loc_key" + case bodyLocArgs = "body_loc_args" case clickAction = "click_action" case androidChannelId = "android_channel_id" case title, icon, body, sound, badge, tag, diff --git a/Tests/ParseSwiftTests/ParsePushPayloadAnyTests.swift b/Tests/ParseSwiftTests/ParsePushPayloadAnyTests.swift index 9a03432bf..580e8f85c 100644 --- a/Tests/ParseSwiftTests/ParsePushPayloadAnyTests.swift +++ b/Tests/ParseSwiftTests/ParsePushPayloadAnyTests.swift @@ -115,7 +115,7 @@ class ParsePushPayloadAnyTests: XCTestCase { XCTAssertEqual(decodedAny2, applePayload) #if !os(Linux) && !os(Android) && !os(Windows) XCTAssertEqual(fcmPayload.description, - "{\"collapseKey\":\"nope\",\"data\":{\"help\":\"you\"},\"delayWhileIdle\":false,\"dryRun\":false,\"notification\":{\"android_channel_id\":\"you\",\"badge\":\"no\",\"body\":\"android\",\"body_loc-key\":\"cousin\",\"body-loc-args\":[\"mother\"],\"click_action\":\"to\",\"color\":\"blue\",\"icon\":\"world\",\"image\":\"icon\",\"sound\":\"yes\",\"subtitle\":\"trip\",\"tag\":\"it\",\"title\":\"hello\",\"title_loc_args\":[\"arg\"],\"title_loc_key\":\"it\"},\"restrictedPackageName\":\"geez\",\"title\":\"peace\",\"uri\":\"https:\\/\\/parse.org\"}") + "{\"collapseKey\":\"nope\",\"data\":{\"help\":\"you\"},\"delayWhileIdle\":false,\"dryRun\":false,\"notification\":{\"android_channel_id\":\"you\",\"badge\":\"no\",\"body\":\"android\",\"body_loc_args\":[\"mother\"],\"body_loc_key\":\"cousin\",\"click_action\":\"to\",\"color\":\"blue\",\"icon\":\"world\",\"image\":\"icon\",\"sound\":\"yes\",\"subtitle\":\"trip\",\"tag\":\"it\",\"title\":\"hello\",\"title_loc_args\":[\"arg\"],\"title_loc_key\":\"it\"},\"restrictedPackageName\":\"geez\",\"title\":\"peace\",\"uri\":\"https:\\/\\/parse.org\"}") #endif } @@ -230,7 +230,7 @@ class ParsePushPayloadAnyTests: XCTestCase { XCTAssertEqual(decoded2, fcmPayload) #if !os(Linux) && !os(Android) && !os(Windows) XCTAssertEqual(fcmPayload.description, - "{\"collapseKey\":\"nope\",\"contentAvailable\":true,\"data\":{\"help\":\"you\"},\"delayWhileIdle\":false,\"dryRun\":false,\"mutableContent\":true,\"notification\":{\"android_channel_id\":\"you\",\"badge\":\"no\",\"body\":\"android\",\"body_loc-key\":\"cousin\",\"body-loc-args\":[\"mother\"],\"click_action\":\"to\",\"color\":\"blue\",\"icon\":\"world\",\"image\":\"icon\",\"sound\":\"yes\",\"subtitle\":\"trip\",\"tag\":\"it\",\"title\":\"hello\",\"title_loc_args\":[\"arg\"],\"title_loc_key\":\"it\"},\"priority\":\"high\",\"restrictedPackageName\":\"geez\",\"title\":\"peace\",\"uri\":\"https:\\/\\/parse.org\"}") + "{\"collapseKey\":\"nope\",\"contentAvailable\":true,\"data\":{\"help\":\"you\"},\"delayWhileIdle\":false,\"dryRun\":false,\"mutableContent\":true,\"notification\":{\"android_channel_id\":\"you\",\"badge\":\"no\",\"body\":\"android\",\"body_loc_args\":[\"mother\"],\"body_loc_key\":\"cousin\",\"click_action\":\"to\",\"color\":\"blue\",\"icon\":\"world\",\"image\":\"icon\",\"sound\":\"yes\",\"subtitle\":\"trip\",\"tag\":\"it\",\"title\":\"hello\",\"title_loc_args\":[\"arg\"],\"title_loc_key\":\"it\"},\"priority\":\"high\",\"restrictedPackageName\":\"geez\",\"title\":\"peace\",\"uri\":\"https:\\/\\/parse.org\"}") #endif } } diff --git a/Tests/ParseSwiftTests/ParsePushPayloadFirebaseTests.swift b/Tests/ParseSwiftTests/ParsePushPayloadFirebaseTests.swift index 82dae0e1c..dda0c3801 100644 --- a/Tests/ParseSwiftTests/ParsePushPayloadFirebaseTests.swift +++ b/Tests/ParseSwiftTests/ParsePushPayloadFirebaseTests.swift @@ -73,7 +73,7 @@ class ParsePushPayloadFirebaseTests: XCTestCase { XCTAssertEqual(fcmPayload, decoded) #if !os(Linux) && !os(Android) && !os(Windows) XCTAssertEqual(fcmPayload.description, - "{\"collapseKey\":\"nope\",\"contentAvailable\":true,\"data\":{\"help\":\"you\"},\"delayWhileIdle\":false,\"dryRun\":false,\"mutableContent\":true,\"notification\":{\"android_channel_id\":\"you\",\"badge\":\"no\",\"body\":\"android\",\"body_loc-key\":\"cousin\",\"body-loc-args\":[\"mother\"],\"click_action\":\"to\",\"color\":\"blue\",\"icon\":\"world\",\"image\":\"icon\",\"sound\":\"yes\",\"subtitle\":\"trip\",\"tag\":\"it\",\"title\":\"hello\",\"title_loc_args\":[\"arg\"],\"title_loc_key\":\"it\"},\"priority\":\"high\",\"restrictedPackageName\":\"geez\",\"title\":\"peace\",\"uri\":\"https:\\/\\/parse.org\"}") + "{\"collapseKey\":\"nope\",\"contentAvailable\":true,\"data\":{\"help\":\"you\"},\"delayWhileIdle\":false,\"dryRun\":false,\"mutableContent\":true,\"notification\":{\"android_channel_id\":\"you\",\"badge\":\"no\",\"body\":\"android\",\"body_loc_args\":[\"mother\"],\"body_loc_key\":\"cousin\",\"click_action\":\"to\",\"color\":\"blue\",\"icon\":\"world\",\"image\":\"icon\",\"sound\":\"yes\",\"subtitle\":\"trip\",\"tag\":\"it\",\"title\":\"hello\",\"title_loc_args\":[\"arg\"],\"title_loc_key\":\"it\"},\"priority\":\"high\",\"restrictedPackageName\":\"geez\",\"title\":\"peace\",\"uri\":\"https:\\/\\/parse.org\"}") #endif } }