diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 64a43717c6..6f1937abc4 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -16,44 +16,17 @@ jobs: - uses: ruby/setup-ruby@v1 with: bundler-cache: true - - run: sudo xcode-select -switch /Applications/Xcode_16.4.app + - run: sudo xcode-select -switch /Applications/Xcode_26.0.app - run: bundle exec sh build.sh verify-docs swiftlint: runs-on: macos-15 name: Check swiftlint steps: - uses: actions/checkout@v4 - - run: sudo xcode-select -switch /Applications/Xcode_16.4.app + - run: sudo xcode-select -switch /Applications/Xcode_26.0.app - run: brew install swiftlint - run: sh build.sh verify-swiftlint - osx-15_3: - runs-on: macos-14 - name: Test osx on Xcode 15.3 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx - - osx-15_4: - runs-on: macos-14 - name: Test osx on Xcode 15.4 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx - - osx-16_2: - runs-on: macos-15 - name: Test osx on Xcode 16.2 - env: - DEVELOPER_DIR: '/Applications/Xcode_16.2.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx - osx-16_3: runs-on: macos-15 name: Test osx on Xcode 16.3 @@ -72,59 +45,41 @@ jobs: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr osx - osx-encryption-16_4: + osx-26_0: runs-on: macos-15 - name: Test osx-encryption on Xcode 16.4 + name: Test osx on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx-encryption + - run: sh -x build.sh ci-pr osx - swiftpm-15_3: - runs-on: macos-14 - name: Test swiftpm on Xcode 15.3 + osx-encryption-26_0: + runs-on: macos-15 + name: Test osx-encryption on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr swiftpm + - run: sh -x build.sh ci-pr osx-encryption - swiftpm-16_4: + swiftpm-16_3: runs-on: macos-15 - name: Test swiftpm on Xcode 16.4 + name: Test swiftpm on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr swiftpm - swiftpm-debug-15_3: - runs-on: macos-14 - name: Test swiftpm-debug on Xcode 15.3 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr swiftpm-debug - - swiftpm-debug-15_4: - runs-on: macos-14 - name: Test swiftpm-debug on Xcode 15.4 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr swiftpm-debug - - swiftpm-debug-16_2: + swiftpm-26_0: runs-on: macos-15 - name: Test swiftpm-debug on Xcode 16.2 + name: Test swiftpm on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.2.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr swiftpm-debug + - run: sh -x build.sh ci-pr swiftpm swiftpm-debug-16_3: runs-on: macos-15 @@ -144,140 +99,122 @@ jobs: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr swiftpm-debug - swiftpm-address-16_4: + swiftpm-debug-26_0: runs-on: macos-15 - name: Test swiftpm-address on Xcode 16.4 + name: Test swiftpm-debug on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh ci-pr swiftpm-debug + + swiftpm-address-26_0: + runs-on: macos-15 + name: Test swiftpm-address on Xcode 26.0 + env: + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr swiftpm-address - swiftpm-thread-16_4: + swiftpm-thread-26_0: runs-on: macos-15 - name: Test swiftpm-thread on Xcode 16.4 + name: Test swiftpm-thread on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr swiftpm-thread - ios-static-15_3: - runs-on: macos-14 - name: Test ios-static on Xcode 15.3 + ios-static-16_3: + runs-on: macos-15 + name: Test ios-static on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios-static - ios-static-16_4: + ios-static-26_0: runs-on: macos-15 - name: Test ios-static on Xcode 16.4 + name: Test ios-static on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios-static - ios-15_3: - runs-on: macos-14 - name: Test ios on Xcode 15.3 + ios-16_3: + runs-on: macos-15 + name: Test ios on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios - ios-16_4: + ios-26_0: runs-on: macos-15 - name: Test ios on Xcode 16.4 + name: Test ios on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios - watchos-15_3: - runs-on: macos-14 - name: Test watchos on Xcode 15.3 + watchos-16_3: + runs-on: macos-15 + name: Test watchos on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr watchos - watchos-16_4: + watchos-26_0: runs-on: macos-15 - name: Test watchos on Xcode 16.4 + name: Test watchos on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr watchos - tvos-15_3: - runs-on: macos-14 - name: Test tvos on Xcode 15.3 + tvos-16_3: + runs-on: macos-15 + name: Test tvos on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr tvos - tvos-16_4: + tvos-26_0: runs-on: macos-15 - name: Test tvos on Xcode 16.4 + name: Test tvos on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr tvos - visionos-15_3: - runs-on: macos-14 - name: Test visionos on Xcode 15.3 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr visionos - - visionos-16_4: + visionos-16_3: runs-on: macos-15 - name: Test visionos on Xcode 16.4 + name: Test visionos on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr visionos - osx-swift-15_3: - runs-on: macos-14 - name: Test osx-swift on Xcode 15.3 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx-swift - - osx-swift-15_4: - runs-on: macos-14 - name: Test osx-swift on Xcode 15.4 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx-swift - - osx-swift-16_2: + visionos-26_0: runs-on: macos-15 - name: Test osx-swift on Xcode 16.2 + name: Test visionos on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.2.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr osx-swift + - run: sh -x build.sh ci-pr visionos osx-swift-16_3: runs-on: macos-15 @@ -297,141 +234,123 @@ jobs: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr osx-swift - ios-swift-15_3: - runs-on: macos-14 - name: Test ios-swift on Xcode 15.3 + osx-swift-26_0: + runs-on: macos-15 + name: Test osx-swift on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh ci-pr osx-swift + + ios-swift-16_3: + runs-on: macos-15 + name: Test ios-swift on Xcode 16.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios-swift - ios-swift-16_4: + ios-swift-26_0: runs-on: macos-15 - name: Test ios-swift on Xcode 16.4 + name: Test ios-swift on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios-swift - tvos-swift-15_3: - runs-on: macos-14 - name: Test tvos-swift on Xcode 15.3 + tvos-swift-16_3: + runs-on: macos-15 + name: Test tvos-swift on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr tvos-swift - tvos-swift-16_4: + tvos-swift-26_0: runs-on: macos-15 - name: Test tvos-swift on Xcode 16.4 + name: Test tvos-swift on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr tvos-swift - osx-swift-evolution-16_4: + osx-swift-evolution-26_0: runs-on: macos-15 - name: Test osx-swift-evolution on Xcode 16.4 + name: Test osx-swift-evolution on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr osx-swift-evolution - ios-swift-evolution-16_4: + ios-swift-evolution-26_0: runs-on: macos-15 - name: Test ios-swift-evolution on Xcode 16.4 + name: Test ios-swift-evolution on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios-swift-evolution - tvos-swift-evolution-16_4: + tvos-swift-evolution-26_0: runs-on: macos-15 - name: Test tvos-swift-evolution on Xcode 16.4 + name: Test tvos-swift-evolution on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr tvos-swift-evolution - catalyst-15_3: - runs-on: macos-14 - name: Test catalyst on Xcode 15.3 + catalyst-16_3: + runs-on: macos-15 + name: Test catalyst on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr catalyst - catalyst-16_4: + catalyst-26_0: runs-on: macos-15 - name: Test catalyst on Xcode 16.4 + name: Test catalyst on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr catalyst - catalyst-swift-15_3: - runs-on: macos-14 - name: Test catalyst-swift on Xcode 15.3 + catalyst-swift-16_3: + runs-on: macos-15 + name: Test catalyst-swift on Xcode 16.3 env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_16.3.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr catalyst-swift - catalyst-swift-16_4: + catalyst-swift-26_0: runs-on: macos-15 - name: Test catalyst-swift on Xcode 16.4 + name: Test catalyst-swift on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr catalyst-swift - xcframework-16_4: + xcframework-26_0: runs-on: macos-15 - name: Test xcframework on Xcode 16.4 + name: Test xcframework on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr xcframework - cocoapods-osx-15_3: - runs-on: macos-14 - name: Test cocoapods-osx on Xcode 15.3 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr cocoapods-osx - - cocoapods-osx-15_4: - runs-on: macos-14 - name: Test cocoapods-osx on Xcode 15.4 - env: - DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr cocoapods-osx - - cocoapods-osx-16_2: - runs-on: macos-15 - name: Test cocoapods-osx on Xcode 16.2 - env: - DEVELOPER_DIR: '/Applications/Xcode_16.2.app/Contents/Developer' - steps: - - uses: actions/checkout@v4 - - run: sh -x build.sh ci-pr cocoapods-osx - cocoapods-osx-16_3: runs-on: macos-15 name: Test cocoapods-osx on Xcode 16.3 @@ -450,56 +369,65 @@ jobs: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr cocoapods-osx - cocoapods-ios-static-16_4: + cocoapods-osx-26_0: runs-on: macos-15 - name: Test cocoapods-ios-static on Xcode 16.4 + name: Test cocoapods-osx on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh ci-pr cocoapods-osx + + cocoapods-ios-static-26_0: + runs-on: macos-15 + name: Test cocoapods-ios-static on Xcode 26.0 + env: + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr cocoapods-ios-static - cocoapods-ios-16_4: + cocoapods-ios-26_0: runs-on: macos-15 - name: Test cocoapods-ios on Xcode 16.4 + name: Test cocoapods-ios on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr cocoapods-ios - cocoapods-watchos-16_4: + cocoapods-watchos-26_0: runs-on: macos-15 - name: Test cocoapods-watchos on Xcode 16.4 + name: Test cocoapods-watchos on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr cocoapods-watchos - cocoapods-tvos-16_4: + cocoapods-tvos-26_0: runs-on: macos-15 - name: Test cocoapods-tvos on Xcode 16.4 + name: Test cocoapods-tvos on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr cocoapods-tvos - cocoapods-catalyst-16_4: + cocoapods-catalyst-26_0: runs-on: macos-15 - name: Test cocoapods-catalyst on Xcode 16.4 + name: Test cocoapods-catalyst on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr cocoapods-catalyst - ios-swiftui-16_4: + ios-swiftui-26_0: runs-on: macos-15 - name: Test ios-swiftui on Xcode 16.4 + name: Test ios-swiftui on Xcode 26.0 env: - DEVELOPER_DIR: '/Applications/Xcode_16.4.app/Contents/Developer' + DEVELOPER_DIR: '/Applications/Xcode_26.0.app/Contents/Developer' steps: - uses: actions/checkout@v4 - run: sh -x build.sh ci-pr ios-swiftui diff --git a/.github/workflows/master-push.yml b/.github/workflows/master-push.yml index 9908d6d6d1..cda0bf94e6 100644 --- a/.github/workflows/master-push.yml +++ b/.github/workflows/master-push.yml @@ -6,10 +6,10 @@ on: - "master" - "release/**" env: - XCODE_VERSION: "['15.3', '15.4', '16.2', '16.3', '16.4']" + XCODE_VERSION: "['16.3', '16.4', '26.0.1']" PLATFORM: "['ios', 'osx', 'watchos', 'tvos', 'catalyst', 'visionos']" - RELEASE_VERSION: '16.4' - DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer + RELEASE_VERSION: '26.0.1' + DEVELOPER_DIR: /Applications/Xcode_26.0.1.app/Contents/Developer jobs: prepare: runs-on: ubuntu-latest @@ -68,21 +68,15 @@ jobs: xcode-version: ${{ fromJSON(needs.prepare.outputs.XCODE_VERSIONS_MATRIX) }} configuration: [swift, static] include: - - xcode-version: 15.3 - xcode-version-tag: 15.3 - os: macos-14 - - xcode-version: 15.4 - xcode-version-tag: 15.4 - os: macos-14 - - xcode-version: 16.2 - xcode-version-tag: 16.2 - os: macos-15 - xcode-version: 16.3 xcode-version-tag: 16.3 os: macos-15 - xcode-version: 16.4 xcode-version-tag: 16.4 os: macos-15 + - xcode-version: 26.0.1 + xcode-version-tag: 26.0.1 + os: macos-15 exclude: - platform: osx configuration: static @@ -99,9 +93,6 @@ jobs: DEVELOPER_DIR: /Applications/Xcode_${{matrix.xcode-version}}.app/Contents/Developer steps: - uses: actions/checkout@v4 - - name: Download visionOS - if: matrix.platform == 'visionos' && matrix.os == 'macos-14' - run: xcodebuild -downloadPlatform visionOS - run: sh build.sh ${{matrix.platform}}-${{matrix.configuration}} - run: tar cf build.tar build/*/${{matrix.platform}} - name: Upload framework @@ -213,16 +204,12 @@ jobs: matrix: xcode-version: ${{ fromJSON(needs.prepare.outputs.XCODE_VERSIONS_MATRIX) }} include: - - xcode-version: 15.3 - os: macos-14 - - xcode-version: 15.4 - os: macos-14 - - xcode-version: 16.2 - os: macos-15 - xcode-version: 16.3 os: macos-15 - xcode-version: 16.4 os: macos-15 + - xcode-version: 26.0.1 + os: macos-15 env: PLATFORM: 'osx' DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode-version }}.app/Contents/Developer diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 52925ac9e0..1143342f40 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -1,8 +1,8 @@ name: Publish release on: workflow_dispatch env: - XCODE_VERSION: "['15.3', '15.4', '16', '16.1', '16.2', '16.3']" - TEST_XCODE_VERSION: '16.3' + XCODE_VERSION: "['16.3', '16.4', '26.0.1']" + TEST_XCODE_VERSION: '26.0.1' jobs: prepare: runs-on: ubuntu-latest @@ -32,22 +32,6 @@ jobs: tag: "v${{ needs.prepare.outputs.VERSION }}" tag_exists_error: false message: "" - publish-docs: - runs-on: macos-15 - name: Publish docs to S3 Bucket - needs: tag-release - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_DOCS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_DOCS_SECRET_ACCESS_KEY }} - GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - run: brew install s3cmd - - name: Publish docs - run: bundle exec sh -x build.sh publish-docs ${{ github.sha }} create-release: runs-on: macos-15 name: Create github release @@ -145,41 +129,3 @@ jobs: max_attempts: 10 retry_wait_seconds: 60 retry_on: error - post-slack-release: - runs-on: macos-15 - name: Publish to release Slack channel - needs: [create-release, prepare, publish-cocoapods, update-checker, publish-docs] - env: - WEBHOOK_URL: ${{ secrets.SLACK_RELEASES_WEBHOOK }} - steps: - - uses: actions/checkout@v4 - - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - - name: Prepare Changelog - run: bundle exec ./build.sh prepare-publish-changelog - - name: 'Post to #realm-releases' - uses: realm/ci-actions/release-to-slack@a2191a6cbf2f5b50aa71026dd068582dbd5016cc - with: - changelog: ExtractedChangelog/CHANGELOG.md - sdk: Swift - webhook-url: ${{ env.WEBHOOK_URL }} - version: ${{ needs.prepare.outputs.VERSION }} - add-empty-changelog: - runs-on: macos-15 - permissions: - contents: write - name: Add empty changelog and commits/push it - needs: [post-slack-release] - env: - GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - name: Add template to changelog - run: | - sh build.sh add-empty-changelog - - name: Auto-commit CHANGELOG.md - uses: stefanzweifel/git-auto-commit-action@v4 - with: - file_pattern: 'CHANGELOG.md' - commit_message: 'Add an empty changelog section' diff --git a/CHANGELOG.md b/CHANGELOG.md index df057b616e..1c7b83a081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,7 @@ x.y.z Release notes (yyyy-MM-dd) ============================================================= -### Enhancements -* None. - -### Fixed -* ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?) -* None. - - -### Compatibility -* Realm Studio: 15.0.0 or later. -* APIs are backwards compatible with all previous releases in the 10.x.y series. -* Carthage release for Swift is built with Xcode 16.4.0. -* CocoaPods: 1.10 or later. -* Xcode: 15.3.0-26 beta 1. - -### Internal -* Upgraded realm-core from ? to ? +Add Xcode 26.0.1 binaries and drop support for Xcode < 16.3. 10.54.5 Release notes (2025-06-15) ============================================================= diff --git a/Gemfile b/Gemfile index b3bb1e1371..d8d9876a3b 100644 --- a/Gemfile +++ b/Gemfile @@ -5,4 +5,4 @@ gem 'fileutils' gem 'jazzy' gem 'jwt' gem 'octokit' -gem 'pathname' +gem 'pathname', '0.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 0580766ca4..0b423fc30d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,8 +5,9 @@ GEM base64 nkf rexml - activesupport (7.2.0) + activesupport (7.2.2.2) base64 + benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) @@ -22,13 +23,14 @@ GEM httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) - base64 (0.2.0) - bigdecimal (3.1.8) + base64 (0.3.0) + benchmark (0.4.1) + bigdecimal (3.3.1) claide (1.1.0) - cocoapods (1.15.2) + cocoapods (1.16.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.15.2) + cocoapods-core (= 1.16.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -42,8 +44,8 @@ GEM molinillo (~> 0.8.0) nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) - xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.15.2) + xcodeproj (>= 1.27.0, < 2.0) + cocoapods-core (1.16.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -63,27 +65,29 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.3.4) - connection_pool (2.4.1) - drb (2.2.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.4) + drb (2.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.17.0) ffi (>= 1.15.0) - faraday (2.10.1) - faraday-net_http (>= 2.0, < 3.2) + faraday (2.14.0) + faraday-net_http (>= 2.0, < 3.5) + json logger - faraday-net_http (3.1.1) - net-http - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86_64-darwin) - fileutils (1.7.2) + faraday-net_http (3.4.1) + net-http (>= 0.5.0) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86_64-darwin) + fileutils (1.7.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - httpclient (2.8.3) - i18n (1.14.5) + httpclient (2.9.0) + mutex_m + i18n (1.14.7) concurrent-ruby (~> 1.0) - jazzy (0.15.1) + jazzy (0.15.3) cocoapods (~> 1.5) mustache (~> 1.1) open4 (~> 1.3) @@ -93,54 +97,53 @@ GEM sassc (~> 2.1) sqlite3 (~> 1.3) xcinvoke (~> 0.3.0) - json (2.7.2) - jwt (2.8.2) + json (2.15.1) + jwt (3.1.2) base64 liferaft (0.0.6) - logger (1.6.0) - minitest (5.25.1) + logger (1.7.0) + minitest (5.26.0) molinillo (0.8.0) mustache (1.1.1) - nanaimo (0.3.0) + mutex_m (0.3.0) + nanaimo (0.4.0) nap (1.1.0) - net-http (0.4.1) + net-http (0.6.0) uri netrc (0.11.0) nkf (0.2.0) - octokit (9.1.0) + octokit (10.0.0) faraday (>= 1, < 3) sawyer (~> 0.9) open4 (1.3.4) pathname (0.3.0) public_suffix (4.0.7) - redcarpet (3.6.0) - rexml (3.3.5) - strscan - rouge (4.3.0) + redcarpet (3.6.1) + rexml (3.4.4) + rouge (4.6.1) ruby-macho (2.5.1) sassc (2.4.0) ffi (~> 1.9) sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - securerandom (0.3.1) + securerandom (0.4.1) sqlite3 (1.7.3-arm64-darwin) sqlite3 (1.7.3-x86_64-darwin) - strscan (3.1.0) typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uri (0.13.0) + uri (1.0.4) xcinvoke (0.3.0) liferaft (~> 0.0.6) - xcodeproj (1.25.0) + xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) PLATFORMS arm64-darwin-22 @@ -153,7 +156,7 @@ DEPENDENCIES jazzy jwt octokit - pathname + pathname (= 0.3.0) BUNDLED WITH 2.5.11 diff --git a/Realm.podspec b/Realm.podspec index 9d4a21e3d8..56a66b59a9 100644 --- a/Realm.podspec +++ b/Realm.podspec @@ -15,8 +15,6 @@ Pod::Spec.new do |s| s.author = { 'Realm' => 'realm-help@mongodb.com' } s.library = 'c++', 'z', 'compression' s.requires_arc = true - s.social_media_url = 'https://twitter.com/realm' - s.documentation_url = "https://docs.mongodb.com/realm/sdk/swift" s.license = { :type => 'Apache 2.0', :file => 'LICENSE' } public_header_files = 'include/Realm.h', diff --git a/Realm/ObjectServerTests/AsyncSyncTests.swift b/Realm/ObjectServerTests/AsyncSyncTests.swift index 07ec125c4a..2b4a6db49d 100644 --- a/Realm/ObjectServerTests/AsyncSyncTests.swift +++ b/Realm/ObjectServerTests/AsyncSyncTests.swift @@ -279,11 +279,7 @@ class AsyncAwaitSyncTests: SwiftSyncTestCase { let configuration = try configuration() func isolatedOpen(_ actor: isolated CustomExecutorActor) async throws { -#if compiler(<6) - _ = try await Realm(configuration: configuration, actor: actor, downloadBeforeOpen: .always) -#else _ = try await Realm.open(configuration: configuration, downloadBeforeOpen: .always) -#endif } @@ -905,11 +901,7 @@ class AsyncFlexibleSyncTests: SwiftSyncTestCase { let user = try await createUser() var config = user.flexibleSyncConfiguration() config.objectTypes = [SwiftPerson.self] -#if compiler(<6) - let realm = try await Realm(configuration: config, actor: CustomGlobalActor.shared) -#else let realm = try await Realm.open(configuration: config) -#endif let name = self.name let results1 = try await realm.objects(SwiftPerson.self) .where { $0.firstName == name && $0.age > 8 }.subscribe(waitForSync: .onCreation) diff --git a/Realm/ObjectServerTests/SwiftAsymmetricSyncServerTests.swift b/Realm/ObjectServerTests/SwiftAsymmetricSyncServerTests.swift index 8313ec3b28..a8671fa2ad 100644 --- a/Realm/ObjectServerTests/SwiftAsymmetricSyncServerTests.swift +++ b/Realm/ObjectServerTests/SwiftAsymmetricSyncServerTests.swift @@ -225,31 +225,6 @@ extension SwiftAsymmetricSyncTests { try await checkCountInMongo(15, type: SwiftObjectAsymmetric.self) } - @MainActor - func testPropertyTypesAsymmetricObject() async throws { - let collection = try await setupCollection(SwiftObjectAsymmetric.self) - let realm = try await openRealm() - - try realm.write { - realm.create(SwiftObjectAsymmetric.self, - value: SwiftObjectAsymmetric(string: "name_\(#function)", - int: 15, bool: true)) - } - waitForUploads(for: realm) - - try await checkCountInMongo(1, type: SwiftObjectAsymmetric.self) - - let document = try await collection.find(filter: [:])[0] - XCTAssertEqual(document["string"]??.stringValue, "name_\(#function)") - XCTAssertEqual(document["int"]??.int64Value, 15) - XCTAssertEqual(document["bool"]??.boolValue, true) - XCTAssertEqual(document["double"]??.doubleValue, 1.1) - XCTAssertEqual(document["long"]??.int64Value, 1) - XCTAssertEqual(document["decimal"]??.decimal128Value, Decimal128(1)) - XCTAssertEqual(document["uuid"]??.uuidValue, UUID(uuidString: "85d4fbee-6ec6-47df-bfa1-615931903d7e")!) - XCTAssertEqual(document["objectId"]??.objectIdValue, ObjectId("6058f12682b2fbb1f334ef1d")) - } - @MainActor func testCreateHugeAsymmetricObject() async throws { _ = try await setupCollection(HugeObjectAsymmetric.self) @@ -265,36 +240,5 @@ extension SwiftAsymmetricSyncTests { try await checkCountInMongo(2, type: HugeObjectAsymmetric.self) } - - @MainActor - func testCreateCustomAsymmetricObject() async throws { - let collection = try await setupCollection(SwiftCustomColumnAsymmetricObject.self) - let realm = try await openRealm() - - let objectId = ObjectId.generate() - let valuesDictionary: [String: Any] = ["id": objectId, - "boolCol": false, - "intCol": 1234, - "doubleCol": 1234.1234, - "stringCol": "$%&/("] - - // Create Asymmetric Objects - try realm.write { - realm.create(SwiftCustomColumnAsymmetricObject.self, value: valuesDictionary) - } - waitForUploads(for: realm) - - try await checkCountInMongo(1, type: SwiftCustomColumnAsymmetricObject.self) - - let filter: Document = ["_id": .objectId(objectId)] - let document = try await collection.findOneDocument(filter: filter) - XCTAssertNotNil(document) - - XCTAssertEqual(document!["_id"], AnyBSON(objectId)) - XCTAssertEqual(document!["custom_boolCol"], AnyBSON(false)) - XCTAssertEqual(document!["custom_intCol"], AnyBSON(1234)) - XCTAssertEqual(document!["custom_doubleCol"], AnyBSON(1234.1234)) - XCTAssertEqual(document!["custom_stringCol"], AnyBSON("$%&/(")) - } } #endif // os(macOS) diff --git a/Realm/ObjectServerTests/SwiftSyncTestCase.swift b/Realm/ObjectServerTests/SwiftSyncTestCase.swift index bf592a78bc..171641128a 100644 --- a/Realm/ObjectServerTests/SwiftSyncTestCase.swift +++ b/Realm/ObjectServerTests/SwiftSyncTestCase.swift @@ -229,15 +229,6 @@ open class SwiftSyncTestCase: RLMSyncTestCase { // These are async versions of the synchronous functions defined above. // They should function identically other than being async rather than using // expecatations to synchronously await things. -#if compiler(<6) - public func basicCredentials(usernameSuffix: String = "", app: App? = nil) async throws -> Credentials { - let email = "\(randomString(10))\(usernameSuffix)" - let password = "abcdef" - let credentials = Credentials.emailPassword(email: email, password: password) - try await (app ?? self.app).emailPasswordAuth.registerUser(email: email, password: password) - return credentials - } -#else public func basicCredentials( usernameSuffix: String = "", app: App? = nil, _isolation: isolated (any Actor)? = #isolation ) async throws -> Credentials { @@ -247,7 +238,6 @@ open class SwiftSyncTestCase: RLMSyncTestCase { try await (app ?? self.app).emailPasswordAuth.registerUser(email: email, password: password) return credentials } -#endif @MainActor @nonobjc public func openRealm() async throws -> Realm { @@ -268,17 +258,10 @@ open class SwiftSyncTestCase: RLMSyncTestCase { }.value } -#if compiler(<6) - public func createUser(app: App? = nil) async throws -> User { - let credentials = try await basicCredentials(app: app) - return try await (app ?? self.app).login(credentials: credentials) - } -#else public func createUser(app: App? = nil, _isolation: isolated (any Actor)? = #isolation) async throws -> User { let credentials = try await basicCredentials(app: app) return try await (app ?? self.app).login(credentials: credentials) } -#endif } @available(macOS 10.15, watchOS 6.0, iOS 13.0, tvOS 13.0, *) diff --git a/Realm/RLMMigration.h b/Realm/RLMMigration.h index 4a6652a19c..214adbc370 100644 --- a/Realm/RLMMigration.h +++ b/Realm/RLMMigration.h @@ -72,7 +72,7 @@ typedef void (^RLMObjectMigrationBlock)(RLMObject * __nullable oldObject, RLMObj properties. */ - (void)enumerateObjects:(NSString *)className - block:(__attribute__((noescape)) RLMObjectMigrationBlock)block NS_REFINED_FOR_SWIFT; + block:(__attribute__((noescape, swift_attr("@nonSendable"))) RLMObjectMigrationBlock)block NS_REFINED_FOR_SWIFT; /** Creates and returns an `RLMObject` instance of type `className` in the Realm being migrated. diff --git a/Realm/Swift/RLMSupport.swift b/Realm/Swift/RLMSupport.swift index ab7544100c..cdfc965aa1 100644 --- a/Realm/Swift/RLMSupport.swift +++ b/Realm/Swift/RLMSupport.swift @@ -97,17 +97,10 @@ extension _RLMDictionaryIterator where Self: RLMCollection { // Sequence conformance for RLMArray, RLMDictionary, RLMSet and RLMResults is provided by RLMCollection's // `makeIterator()` implementation. -#if compiler(<6.0) -extension RLMArray: Sequence, _RLMCollectionIterator { } -extension RLMDictionary: Sequence, _RLMDictionaryIterator {} -extension RLMSet: Sequence, _RLMCollectionIterator {} -extension RLMResults: Sequence, _RLMCollectionIterator {} -#else extension RLMArray: @retroactive Sequence, _RLMCollectionIterator { } extension RLMDictionary: @retroactive Sequence, _RLMDictionaryIterator {} extension RLMSet: @retroactive Sequence, _RLMCollectionIterator {} extension RLMResults: @retroactive Sequence, _RLMCollectionIterator {} -#endif /** This struct enables sequence-style enumeration for RLMObjects in Swift via `RLMCollection.makeIterator` diff --git a/Realm/Tests/Swift/SwiftRealmTests.swift b/Realm/Tests/Swift/SwiftRealmTests.swift index a70db81864..0d3b2d6d56 100644 --- a/Realm/Tests/Swift/SwiftRealmTests.swift +++ b/Realm/Tests/Swift/SwiftRealmTests.swift @@ -24,7 +24,7 @@ import RealmTestSupport #endif @available(iOS 13.0, *) // For @MainActor -class SwiftRLMRealmTests: RLMTestCase, @unchecked Sendable { +class SwiftRLMRealmTests: RLMTestCase { // No models @@ -78,8 +78,9 @@ class SwiftRLMRealmTests: RLMTestCase, @unchecked Sendable { notificationFired.fulfill() } + nonisolated(unsafe) let unsafeSelf = self dispatchAsync { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() realm.beginWriteTransaction() _ = SwiftRLMStringObject.create(in: realm, withValue: ["string"]) try! realm.commitWriteTransaction() @@ -130,8 +131,9 @@ class SwiftRLMRealmTests: RLMTestCase, @unchecked Sendable { updateComplete.fulfill() } + nonisolated(unsafe) let unsafeSelf = self dispatchAsync { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() try! realm.transaction { var obj = SwiftRLMIntObject() obj.intCol = 2; @@ -157,8 +159,9 @@ class SwiftRLMRealmTests: RLMTestCase, @unchecked Sendable { notificationFired.fulfill() } + nonisolated(unsafe) let unsafeSelf = self dispatchAsync { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() let obj = SwiftRLMStringObject(value: ["string"]) realm.beginWriteTransaction() realm.add(obj) @@ -218,8 +221,9 @@ class SwiftRLMRealmTests: RLMTestCase, @unchecked Sendable { } } + nonisolated(unsafe) let unsafeSelf = self dispatchAsync { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() realm.beginWriteTransaction() _ = StringObject.create(in: realm, withValue: ["string"]) try! realm.commitWriteTransaction() @@ -244,8 +248,9 @@ class SwiftRLMRealmTests: RLMTestCase, @unchecked Sendable { notificationFired.fulfill() } + nonisolated(unsafe) let unsafeSelf = self dispatchAsync { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() let obj = StringObject(value: ["string"]) try! realm.transaction { realm.add(obj) diff --git a/Realm/Tests/Swift/SwiftSchemaTests.swift b/Realm/Tests/Swift/SwiftSchemaTests.swift index 9d01c1eb5c..bf5b375c1f 100644 --- a/Realm/Tests/Swift/SwiftSchemaTests.swift +++ b/Realm/Tests/Swift/SwiftSchemaTests.swift @@ -151,11 +151,7 @@ class OnlyComputedNoBacklinksProps: FakeObject { } class RequiresObjcName: RLMObject { -#if compiler(>=5.10) nonisolated(unsafe) static var enable = false -#else - static var enable = false -#endif override class func _realmIgnoreClass() -> Bool { return !enable } diff --git a/RealmSwift.podspec b/RealmSwift.podspec index 4b1f2a38e9..6a3178164d 100644 --- a/RealmSwift.podspec +++ b/RealmSwift.podspec @@ -13,8 +13,6 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/realm/realm-swift.git', :tag => "v#{s.version}" } s.author = { 'Realm' => 'realm-help@mongodb.com' } s.requires_arc = true - s.social_media_url = 'https://twitter.com/realm' - s.documentation_url = "https://docs.mongodb.com/realm/sdk/swift" s.license = { :type => 'Apache 2.0', :file => 'LICENSE' } s.ios.deployment_target = '12.0' s.osx.deployment_target = '10.13' diff --git a/RealmSwift/App.swift b/RealmSwift/App.swift index b10f97d2bb..da7583be29 100644 --- a/RealmSwift/App.swift +++ b/RealmSwift/App.swift @@ -405,11 +405,7 @@ extension App { return AppPublisher(self, scheduler: DispatchQueue.main) } } -#if compiler(>=6) extension App: @retroactive ObservableObject {} -#else -extension App: ObservableObject {} -#endif @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) internal func promisify(_ fn: @escaping (@escaping @Sendable (Error?) -> Void) -> Void) -> Future { diff --git a/RealmSwift/Combine.swift b/RealmSwift/Combine.swift index 4049597d45..50adbfc24e 100644 --- a/RealmSwift/Combine.swift +++ b/RealmSwift/Combine.swift @@ -647,17 +647,10 @@ extension ObjectBase: RealmSubscribable { } } -#if compiler(>=6) @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension Object: @retroactive ObservableObject {} @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension EmbeddedObject: @retroactive ObservableObject {} -#else -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension Object: ObservableObject {} -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension EmbeddedObject: ObservableObject {} -#endif // MARK: - List diff --git a/RealmSwift/EmbeddedObject.swift b/RealmSwift/EmbeddedObject.swift index 6cb13f1fbe..de479d6e02 100644 --- a/RealmSwift/EmbeddedObject.swift +++ b/RealmSwift/EmbeddedObject.swift @@ -217,93 +217,6 @@ extension EmbeddedObject: _RealmCollectionValueInsideOptional { return _observe(on: queue, block) } -#if compiler(<6) - /** - Registers a block to be called each time the object changes. - - The block will be asynchronously called on the given actor's executor after - each write transaction which deletes the object or modifies any of the managed - properties of the object, including self-assignments that set a property to its - existing value. The block is passed a copy of the object isolated to the - requested actor which can be safely used on that actor along with information - about what changed. - - For write transactions performed on different threads or in different - processes, the block will be called when the managing Realm is - (auto)refreshed to a version including the changes, while for local write - transactions it will be called at some point in the future after the write - transaction is committed. - - Only objects which are managed by a Realm can be observed in this way. You - must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - By default, only direct changes to the object's properties will produce - notifications, and not changes to linked objects. Note that this is different - from collection change notifications. If a non-nil, non-empty keypath array is - passed in, only changes to the properties identified by those keypaths will - produce change notifications. The keypaths may traverse link properties to - receive information about changes to linked objects. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - parameter actor: The actor to isolate notifications to. - - parameter block: The block to call with information about changes to the object. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [String]? = nil, on actor: A, - _ block: @Sendable @escaping (isolated A, ObjectChange) -> Void - ) async -> NotificationToken { - await with(self, on: actor) { actor, obj in - await obj._observe(keyPaths: keyPaths, on: actor, block) - } - } - - /** - Registers a block to be called each time the object changes. - - The block will be asynchronously called on the given actor's executor after - each write transaction which deletes the object or modifies any of the managed - properties of the object, including self-assignments that set a property to its - existing value. The block is passed a copy of the object isolated to the - requested actor which can be safely used on that actor along with information - about what changed. - - For write transactions performed on different threads or in different - processes, the block will be called when the managing Realm is - (auto)refreshed to a version including the changes, while for local write - transactions it will be called at some point in the future after the write - transaction is committed. - - Only objects which are managed by a Realm can be observed in this way. You - must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - By default, only direct changes to the object's properties will produce - notifications, and not changes to linked objects. Note that this is different - from collection change notifications. If a non-nil, non-empty keypath array is - passed in, only changes to the properties identified by those keypaths will - produce change notifications. The keypaths may traverse link properties to - receive information about changes to linked objects. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - parameter actor: The actor to isolate notifications to. - - parameter block: The block to call with information about changes to the object. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, ObjectChange) -> Void - ) async -> NotificationToken { - await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) - } -#else /** Registers a block to be called each time the object changes. @@ -387,7 +300,6 @@ extension EmbeddedObject: _RealmCollectionValueInsideOptional { ) async -> NotificationToken { await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) } -#endif // MARK: Dynamic list diff --git a/RealmSwift/Error.swift b/RealmSwift/Error.swift index 5028edb100..f9b352b03e 100644 --- a/RealmSwift/Error.swift +++ b/RealmSwift/Error.swift @@ -48,11 +48,7 @@ extension Realm.Error { // MARK: Equatable -#if compiler(>=6) extension Realm.Error: @retroactive Equatable {} -#else -extension Realm.Error: Equatable {} -#endif // FIXME: we should not be defining this but it's a breaking change to remove /// Returns a Boolean indicating whether the errors are identical. diff --git a/RealmSwift/Impl/ObjcBridgeable.swift b/RealmSwift/Impl/ObjcBridgeable.swift index 5f1cfb6d5f..beaf028c1e 100644 --- a/RealmSwift/Impl/ObjcBridgeable.swift +++ b/RealmSwift/Impl/ObjcBridgeable.swift @@ -207,11 +207,7 @@ extension RLMSwiftCollectionBase { return lhs.isEqual(rhs) } } -#if compiler(>=6) extension RLMSwiftCollectionBase: @retroactive Equatable {} -#else -extension RLMSwiftCollectionBase: Equatable {} -#endif extension Projection: BuiltInObjcBridgeable { public static func _rlmFromObjc(_ value: Any) -> Self? { diff --git a/RealmSwift/Impl/RealmCollectionImpl.swift b/RealmSwift/Impl/RealmCollectionImpl.swift index f513d9c6df..0804df2c95 100644 --- a/RealmSwift/Impl/RealmCollectionImpl.swift +++ b/RealmSwift/Impl/RealmCollectionImpl.swift @@ -120,20 +120,6 @@ extension RealmCollectionImpl { return collection.addNotificationBlock(wrapped, keyPaths: keyPaths, queue: queue) } -#if compiler(<6) - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [String]?, on actor: A, - _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void - ) async -> NotificationToken { - await with(self, on: actor) { actor, collection in - collection.observe(keyPaths: keyPaths, on: nil) { change in - actor.invokeIsolated(block, change) - } - } - } -#else @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) public func observe( keyPaths: [String]?, on actor: A, @@ -146,7 +132,6 @@ extension RealmCollectionImpl { } } } -#endif public var isFrozen: Bool { return collection.isFrozen @@ -183,36 +168,6 @@ extension Optional: OptionalProtocol { // `with(object, on: actor) { object, actor in ... }` hands the object over // to the given actor and then invokes the callback within the actor. -#if compiler(<6) -@available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) -@_unsafeInheritExecutor -internal func with( - _ value: Value, on actor: A, - _ block: @Sendable @escaping (isolated A, Value) async throws -> NotificationToken -) async rethrows -> NotificationToken { - if value.realm == nil { - fatalError("Change notifications are only supported for managed objects") - } - - let tsr = ThreadSafeReference(to: value) - let config = Unchecked(wrappedValue: value.realm!.rlmRealm.configurationSharingSchema()) - return try await actor.invoke { actor in - if Task.isCancelled { - return nil - } - let scheduler = RLMScheduler.actor(actor, invoke: actor.invoke, verify: actor.verifier()) - let realm = Realm(try! RLMRealm(configuration: config.wrappedValue, confinedTo: scheduler)) - guard let value = tsr.resolve(in: realm) else { - return nil - } - // This is safe but 5.10's sendability checking can't prove it - // nonisolated(unsafe) can't be applied to a let in guard so we need - // a second variable - nonisolated(unsafe) let v = value - return try await block(actor, v) - } ?? NotificationToken() -} -#else @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) internal func with( _ value: Value, @@ -238,4 +193,3 @@ internal func with( return try await block(actor, value) } ?? NotificationToken() } -#endif diff --git a/RealmSwift/Map.swift b/RealmSwift/Map.swift index d65e6b3780..b876ffa095 100644 --- a/RealmSwift/Map.swift +++ b/RealmSwift/Map.swift @@ -520,184 +520,6 @@ public final class Map: RLMSwiftColle return rlmDictionary.addNotificationBlock(wrapped, keyPaths: keyPaths, queue: queue) } -#if compiler(<6) - /** - Registers a block to be called each time the map changes. - - The block will be asynchronously called on the actor with the initial map, and - then called again after each write transaction which changes either which keys - are present in the map or the values of any of the objects. - - The `change` parameter that is passed to the block reports, in the form of keys - within the map, which of the key-value pairs were added, removed, or modified - during each write transaction. - - Notifications are delivered to a function isolated to the given actor, on that - actors executor. If the actor is performing blocking work, multiple - notifications may be coalesced into a single notification. This can include the - notification with the initial collection, and changes are only reported for - writes which occur after the initial notification is delivered. - - If no key paths are given, the block will be executed on any insertion, - modification, or deletion for all object properties and the properties of any - nested, linked objects. If a key path or key paths are provided, then the block - will be called for changes which occur only on the provided key paths. For - example, if: - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - // ... - let dogs = myObject.mapOfDogs - let token = dogs.observe(keyPaths: ["name"], on: actor) { actor, changes in - switch changes { - case .initial(let dogs): - // ... - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the collection is modified - // - when an element is inserted or removed from the collection. - // This block is not triggered: - // - when a value other than name is modified on one of the elements. - case .error: - // No longer possible and left for backwards compatibility - } - } - ``` - - If the observed key path were `["toys.brand"]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger - the block. Changes to the `brand` value on any `Toy` that is linked to a `Dog` - in this collection will trigger the block. Changes to a value other than - `brand` on any `Toy` that is linked to a `Dog` in this collection would not - trigger the block. Any insertion or removal to the `Dog` type collection being - observed would also trigger a notification. - - If the above example observed the `["toys"]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. Changes to any value on any `Toy` that is linked to a - `Dog` in this collection would *not* trigger the block. Any insertion or - removal to the `Dog` type collection being observed would still trigger a - notification. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will - trigger the block when they are modified. If `nil`, notifications will be - delivered for any property change on the object. String key paths which do not - correspond to a valid a property will throw an exception. See description above - for more detail on linked properties. - - note: The keyPaths parameter refers to object properties of the collection - type and *does not* refer to particular key/value pairs within the Map. - - parameter actor: The actor which notifications should be delivered on. The - block is passed this actor as an isolated parameter, allowing you to access the - actor synchronously from within the callback. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [String]? = nil, on actor: A, - _ block: @Sendable @escaping (isolated A, RealmMapChange) -> Void - ) async -> NotificationToken { - await with(self, on: actor) { actor, collection in - collection.observe(keyPaths: keyPaths, on: nil) { change in - actor.invokeIsolated(block, change) - } - } - } - - /** - Registers a block to be called each time the map changes. - - The block will be asynchronously called on the actor with the initial map, and - then called again after each write transaction which changes either which keys - are present in the map or the values of any of the objects. - - The `change` parameter that is passed to the block reports, in the form of keys - within the map, which of the key-value pairs were added, removed, or modified - during each write transaction. - - Notifications are delivered to a function isolated to the given actor, on that - actors executor. If the actor is performing blocking work, multiple - notifications may be coalesced into a single notification. This can include the - notification with the initial collection, and changes are only reported for - writes which occur after the initial notification is delivered. - - The block will be called for changes which occur only on the provided key - paths. For example, if: - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - // ... - let dogs = myObject.mapOfDogs - let token = dogs.observe(keyPaths: [\.name], on: actor) { actor, changes in - switch changes { - case .initial(let dogs): - // ... - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the collection is modified - // - when an element is inserted or removed from the collection. - // This block is not triggered: - // - when a value other than name is modified on one of the elements. - case .error: - // No longer possible and left for backwards compatibility - } - } - ``` - - If the observed key path were `[\.toys.brand]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger - the block. Changes to the `brand` value on any `Toy` that is linked to a `Dog` - in this collection will trigger the block. Changes to a value other than - `brand` on any `Toy` that is linked to a `Dog` in this collection would not - trigger the block. Any insertion or removal to the `Dog` type collection being - observed would also trigger a notification. - - If the above example observed the `[\.toys]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. Changes to any value on any `Toy` that is linked to a - `Dog` in this collection would *not* trigger the block. Any insertion or - removal to the `Dog` type collection being observed would still trigger a - notification. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will - trigger the block when they are modified. If `nil`, notifications will be - delivered for any property change on the object. String key paths which do not - correspond to a valid a property will throw an exception. See description above - for more detail on linked properties. - - note: The keyPaths parameter refers to object properties of the collection - type and *does not* refer to particular key/value pairs within the Map. - - parameter actor: The actor which notifications should be delivered on. The - block is passed this actor as an isolated parameter, allowing you to access the - actor synchronously from within the callback. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, RealmMapChange) -> Void - ) async -> NotificationToken where Value: OptionalProtocol, Value.Wrapped: ObjectBase { - await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) - } -#else /** Registers a block to be called each time the map changes. @@ -875,7 +697,6 @@ public final class Map: RLMSwiftColle ) async -> NotificationToken where Value: OptionalProtocol, Value.Wrapped: ObjectBase { await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) } -#endif // MARK: Frozen Objects diff --git a/RealmSwift/MongoClient.swift b/RealmSwift/MongoClient.swift index 24fda36c4f..6f428c1edd 100644 --- a/RealmSwift/MongoClient.swift +++ b/RealmSwift/MongoClient.swift @@ -603,34 +603,6 @@ extension MongoCollection { .compactMap(ObjectiveCSupport.convertBson(object:)) } -#if compiler(<6) - /// Finds the documents in this collection which match the provided filter. - /// - Parameters: - /// - filter: A `Document` as bson that should match the query. - /// - options: `FindOptions` to use when executing the command. - /// - Returns: Array of `Document` filtered. - @_unsafeInheritExecutor - public func find(filter: Document, options: FindOptions? = nil) async throws -> [Document] { - try await __findWhere(ObjectiveCSupport.convert(filter), - options: options ?? .init()) - .map(ObjectiveCSupport.convert) - } - - /// Returns one document from a collection or view which matches the - /// provided filter. If multiple documents satisfy the query, this method - /// returns the first document according to the query's sort order or natural - /// order. - /// - Parameters: - /// - filter: A `Document` as bson that should match the query. - /// - options: `FindOptions` to use when executing the command. - /// - Returns: `Document` filtered. - @_unsafeInheritExecutor - public func findOneDocument(filter: Document, options: FindOptions? = nil) async throws -> Document? { - try await __findOneDocumentWhere(ObjectiveCSupport.convert(filter), - options: options ?? .init()) - .map(ObjectiveCSupport.convert) - } -#else /// Finds the documents in this collection which match the provided filter. /// - Parameters: /// - filter: A `Document` as bson that should match the query. @@ -671,7 +643,6 @@ extension MongoCollection { } } } -#endif /// Runs an aggregation framework pipeline against this collection. /// - Parameters: @@ -736,65 +707,6 @@ extension MongoCollection { upsert: upsert ?? false) } -#if compiler(<6) - /// Updates a single document in a collection based on a query filter and - /// returns the document in either its pre-update or post-update form. Unlike - /// `updateOneDocument`, this action allows you to atomically find, update, and - /// return a document with the same command. This avoids the risk of other - /// update operations changing the document between separate find and update - /// operations. - /// - Parameters: - /// - filter: A bson `Document` representing the match criteria. - /// - update: A bson `Document` representing the update to be applied to a matching document. - /// - options: `RemoteFindOneAndModifyOptions` to use when executing the command. - /// - Returns: `Document` result of the attempt to update a document or `nil` if document wasn't found. - @_unsafeInheritExecutor - public func findOneAndUpdate(filter: Document, update: Document, - options: FindOneAndModifyOptions? = nil) async throws -> Document? { - try await __findOneAndUpdateWhere(ObjectiveCSupport.convert(filter), - updateDocument: ObjectiveCSupport.convert(update), - options: options ?? .init()) - .map(ObjectiveCSupport.convert) - } - - /// Overwrites a single document in a collection based on a query filter and - /// returns the document in either its pre-replacement or post-replacement - /// form. Unlike `updateOneDocument`, this action allows you to atomically find, - /// replace, and return a document with the same command. This avoids the - /// risk of other update operations changing the document between separate - /// find and update operations. - /// - Parameters: - /// - filter: A `Document` that should match the query. - /// - replacement: A `Document` describing the replacement. - /// - options: `FindOneAndModifyOptions` to use when executing the command. - /// - Returns: `Document`result of the attempt to reaplce a document or `nil` if document wasn't found. - @_unsafeInheritExecutor - public func findOneAndReplace(filter: Document, replacement: Document, - options: FindOneAndModifyOptions? = nil) async throws -> Document? { - try await __findOneAndReplaceWhere(ObjectiveCSupport.convert(filter), - replacementDocument: ObjectiveCSupport.convert(replacement), - options: options ?? .init()) - .map(ObjectiveCSupport.convert) - } - - /// Removes a single document from a collection based on a query filter and - /// returns a document with the same form as the document immediately before - /// it was deleted. Unlike `deleteOneDocument`, this action allows you to atomically - /// find and delete a document with the same command. This avoids the risk of - /// other update operations changing the document between separate find and - /// delete operations. - /// - Parameters: - /// - filter: A `Document` that should match the query. - /// - options: `FindOneAndModifyOptions` to use when executing the command. - /// - Returns: `Document` result of the attempt to delete a document or `nil` if document wasn't found. - @_unsafeInheritExecutor - public func findOneAndDelete(filter: Document, - options: FindOneAndModifyOptions? = nil) async throws -> Document? { - try await __findOneAndDeleteWhere(ObjectiveCSupport.convert(filter), - options: options ?? .init()) - .map(ObjectiveCSupport.convert) - } -#else /// Updates a single document in a collection based on a query filter and /// returns the document in either its pre-update or post-update form. Unlike /// `updateOneDocument`, this action allows you to atomically find, update, and @@ -872,7 +784,6 @@ extension MongoCollection { } } } -#endif } private class ChangeEventDelegateProxy: RLMChangeEventDelegate { diff --git a/RealmSwift/Object.swift b/RealmSwift/Object.swift index bffff74af1..b771e80e1a 100644 --- a/RealmSwift/Object.swift +++ b/RealmSwift/Object.swift @@ -406,93 +406,6 @@ extension Object: _RealmCollectionValueInsideOptional { _observe(keyPaths: keyPaths.map(_name(for:)), on: queue, block) } -#if compiler(<6) - /** - Registers a block to be called each time the object changes. - - The block will be asynchronously called on the given actor's executor after - each write transaction which deletes the object or modifies any of the managed - properties of the object, including self-assignments that set a property to its - existing value. The block is passed a copy of the object isolated to the - requested actor which can be safely used on that actor along with information - about what changed. - - For write transactions performed on different threads or in different - processes, the block will be called when the managing Realm is - (auto)refreshed to a version including the changes, while for local write - transactions it will be called at some point in the future after the write - transaction is committed. - - Only objects which are managed by a Realm can be observed in this way. You - must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - By default, only direct changes to the object's properties will produce - notifications, and not changes to linked objects. Note that this is different - from collection change notifications. If a non-nil, non-empty keypath array is - passed in, only changes to the properties identified by those keypaths will - produce change notifications. The keypaths may traverse link properties to - receive information about changes to linked objects. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - parameter actor: The actor to isolate notifications to. - - parameter block: The block to call with information about changes to the object. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [String]? = nil, on actor: A, - _ block: @Sendable @escaping (isolated A, ObjectChange) -> Void - ) async -> NotificationToken { - await with(self, on: actor) { actor, obj in - await obj._observe(keyPaths: keyPaths, on: actor, block) - } - } - - /** - Registers a block to be called each time the object changes. - - The block will be asynchronously called on the given actor's executor after - each write transaction which deletes the object or modifies any of the managed - properties of the object, including self-assignments that set a property to its - existing value. The block is passed a copy of the object isolated to the - requested actor which can be safely used on that actor along with information - about what changed. - - For write transactions performed on different threads or in different - processes, the block will be called when the managing Realm is - (auto)refreshed to a version including the changes, while for local write - transactions it will be called at some point in the future after the write - transaction is committed. - - Only objects which are managed by a Realm can be observed in this way. You - must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - By default, only direct changes to the object's properties will produce - notifications, and not changes to linked objects. Note that this is different - from collection change notifications. If a non-nil, non-empty keypath array is - passed in, only changes to the properties identified by those keypaths will - produce change notifications. The keypaths may traverse link properties to - receive information about changes to linked objects. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - parameter actor: The actor to isolate notifications to. - - parameter block: The block to call with information about changes to the object. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, ObjectChange) -> Void - ) async -> NotificationToken { - await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) - } -#else /** Registers a block to be called each time the object changes. @@ -578,7 +491,6 @@ extension Object: _RealmCollectionValueInsideOptional { ) async -> NotificationToken { await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) } -#endif // MARK: Dynamic list diff --git a/RealmSwift/Projection.swift b/RealmSwift/Projection.swift index f3e514ab78..ff14397e12 100644 --- a/RealmSwift/Projection.swift +++ b/RealmSwift/Projection.swift @@ -458,188 +458,6 @@ extension ProjectionObservable { observe(keyPaths: map(keyPaths: keyPaths), on: queue, block) } -#if compiler(<6) - /** - Registers a block to be called each time the projection's underlying object changes. - - The block will be asynchronously called on the actor after each write transaction which - deletes the underlying object or modifies any of the projected properties of the object, - including self-assignments that set a property to its existing value. - - For write transactions performed on different threads or in different - processes, the block will be called when the managing Realm is - (auto)refreshed to a version including the changes, while for local write - transactions it will be called at some point in the future after the write - transaction is committed. - - If no key paths are given, the block will be executed on any insertion, - modification, or deletion for all projected properties, including projected properties of - any nested, linked objects. If a key path or key paths are provided, - then the block will be called for changes which occur only on the - provided key paths. For example, if: - - ```swift - class Person: Object { - @Persisted var firstName: String - @Persisted var lastName = "" - @Persisted public var friends: List - } - - class PersonProjection: Projection { - @Projected(\Person.firstName) var name - @Projected(\Person.lastName.localizedUppercase) var lastNameCaps - @Projected(\Person.friends.projectTo.firstName) var firstFriendsName: ProjectedCollection - } - - let token = projectedPerson.observe(keyPaths: ["name"], { changes in - // ... - }) - ``` - - The above notification block fires for changes to the - `Person.firstName` property of the the projection's underlying `Person` Object, - but not for any changes made to `Person.lastName` or `Person.friends` list. - - The notification block fires for changes of `PersonProjection.name` property, but not for - another projection's property change. - - If the observed key path were `["firstFriendsName"]`, then any insertion, - deletion, or modification of the `firstName` of the `friends` list will trigger the block. A change to - `someFriend.lastName` would not trigger the block (where `someFriend` - is an element contained in `friends`) - - Notifications are delivered to a function isolated to the given actor, on that - actors executor. If the actor is performing blocking work, multiple - notifications may be coalesced into a single notification. - - Unlike with Collection notifications, there is no "Initial" notification - and there is no gap between when this function returns and when changes - will first be captured. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If `nil`, notifications - will be delivered for any projected property change on the object. - String key paths which do not correspond to a valid projected property - will throw an exception. - - parameter actor: The actor which notifications should be delivered on. The - block is passed this actor as an isolated parameter, - allowing you to access the actor synchronously from within the callback. - - parameter block: The block to call with information about changes to the object. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [String]? = nil, on actor: A, - _ block: @Sendable @escaping (isolated A, ObjectChange) -> Void - ) async -> NotificationToken { - await with(self, on: actor) { actor, obj in - obj.observe(keyPaths: keyPaths, on: nil) { (change: ObjectChange) in - actor.invokeIsolated(block, change) - } - } - } - - /** - Registers a block to be called each time the projection's underlying object changes. - - The block will be asynchronously called on the actor after each write transaction which - deletes the underlying object or modifies any of the projected properties of the object, - including self-assignments that set a property to its existing value. - - For write transactions performed on different threads or in different - processes, the block will be called when the managing Realm is - (auto)refreshed to a version including the changes, while for local write - transactions it will be called at some point in the future after the write - transaction is committed. - - If no key paths are given, the block will be executed on any insertion, - modification, or deletion for all projected properties, including projected properties of - any nested, linked objects. If a key path or key paths are provided, - then the block will be called for changes which occur only on the - provided key paths. For example, if: - - ```swift - class Person: Object { - @Persisted var firstName: String - @Persisted var lastName = "" - @Persisted public var friends: List - } - - class PersonProjection: Projection { - @Projected(\Person.firstName) var name - @Projected(\Person.lastName.localizedUppercase) var lastNameCaps - @Projected(\Person.friends.projectTo.firstName) var firstFriendsName: ProjectedCollection - } - - let token = projectedPerson.observe(keyPaths: [\PersonProjection.name], { changes in - // ... - }) - ``` - - The above notification block fires for changes to the - `Person.firstName` property of the the projection's underlying `Person` Object, - but not for any changes made to `Person.lastName` or `Person.friends` list. - - The notification block fires for changes of `PersonProjection.name` property, but not for - another projection's property change. - - If the observed key path were `[\.firstFriendsName]`, then any insertion, - deletion, or modification of the `firstName` of the `friends` list will trigger the block. A change to - `someFriend.lastName` would not trigger the block (where `someFriend` - is an element contained in `friends`) - - Notifications are delivered to a function isolated to the given actor, on that - actors executor. If the actor is performing blocking work, multiple - notifications may be coalesced into a single notification. - - Unlike with Collection notifications, there is no "Initial" notification - and there is no gap between when this function returns and when changes - will first be captured. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when - the containing Realm is read-only. - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If `nil`, notifications - will be delivered for any projected property change on the object. - String key paths which do not correspond to a valid projected property - will throw an exception. - - parameter actor: The actor which notifications should be delivered on. The - block is passed this actor as an isolated parameter, - allowing you to access the actor synchronously from within the callback. - - parameter block: The block to call with information about changes to the object. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func observe( - keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, ObjectChange) -> Void - ) async -> NotificationToken { - await observe(keyPaths: map(keyPaths: keyPaths), on: actor, block) - } - - fileprivate var schema: [ProjectionProperty] { - projectionSchemaCache.schema(for: self) - } - - private func map(keyPaths: [PartialKeyPath]) -> [String]? { - if keyPaths.isEmpty { - return nil - } - - let names = NSMutableArray() - let root = Root.keyPathRecorder(with: names) - let projection = Self(projecting: root) - return keyPaths.map { - names.removeAllObjects() - _ = projection[keyPath: $0] - return names.componentsJoined(by: ".") - } - } -#else /** Registers a block to be called each time the projection's underlying object changes. @@ -820,7 +638,6 @@ extension ProjectionObservable { return names.componentsJoined(by: ".") } } -#endif } /** Information about a specific property which changed in an `Object` change notification. diff --git a/RealmSwift/Realm.swift b/RealmSwift/Realm.swift index 499fa4695f..24dc76eab6 100644 --- a/RealmSwift/Realm.swift +++ b/RealmSwift/Realm.swift @@ -1270,7 +1270,6 @@ extension Realm { self = Realm(rlmRealm.wrappedValue) } -#if compiler(>=6) /** Asynchronously obtains a `Realm` instance isolated to the current Actor. @@ -1300,124 +1299,6 @@ extension Realm { actor: actor, downloadBeforeOpen: downloadBeforeOpen) return Realm(rlmRealm.wrappedValue) } -#endif - -#if compiler(<6) - /** - Performs actions contained within the given block inside a write transaction. - - This function differs from synchronous ``write`` in that it suspends the - calling task while waiting for its turn to write rather than blocking the - thread. In addition, the actual i/o to write data to disk is done by a - background worker thread. For small writes, using this function on the - main thread may block the main thread for less time than manually - dispatching the write to a background thread. - - If the block throws an error, the transaction will be canceled and any - changes made before the error will be rolled back. - - Only one write transaction can be open at a time for each Realm file. Write - transactions cannot be nested, and trying to begin a write transaction on a - Realm which is already in a write transaction will throw an exception. - Calls to `write` from `Realm` instances for the same Realm file in other - threads or other processes will block until the current write transaction - completes or is cancelled. - - Before beginning the write transaction, `asyncWrite` updates the `Realm` - instance to the latest Realm version, as if `asyncRefresh()` had been called, - and generates notifications if applicable. This has no effect if the Realm - was already up to date. - - You can skip notifying specific notification blocks about the changes made - in this write transaction by passing in their associated notification - tokens. This is primarily useful when the write transaction is saving - changes already made in the UI and you do not want to have the notification - block attempt to re-apply the same changes. - - The tokens passed to this function must be for notifications for this Realm - which were added on the same actor as the write transaction is being - performed on. Notifications for different threads cannot be skipped using - this method. - - - parameter tokens: An array of notification tokens which were returned - from adding callbacks which you do not want to be - notified for the changes made in this write transaction. - - - parameter block: The block containing actions to perform. - - returns: The value returned from the block, if any. - - - throws: An `NSError` if the transaction could not be completed successfully. - `CancellationError` if the task is cancelled. - If `block` throws, the function throws the propagated `ErrorType` instead. - */ - @discardableResult - @_unsafeInheritExecutor - public func asyncWrite(_ block: (() throws -> Result)) async throws -> Result { - guard let actor = rlmRealm.actor as? Actor else { - fatalError("asyncWrite() can only be called on main thread or actor-isolated Realms") - } - return try await withoutActuallyEscaping(block) { block in - try await Self.asyncWrite(actor: actor, realm: Unchecked(rlmRealm), Unchecked(block)).wrappedValue - } - } - - private static func asyncWrite(actor: isolated any Actor, - realm: Unchecked, - _ block: Unchecked<(() throws -> Result)>) async throws - -> Unchecked { - let realm = realm.wrappedValue - let write = realm.beginAsyncWrite() - await withTaskCancellationHandler { - await write.wait() - } onCancel: { - actor.invoke { write.complete(true) } - } - - let ret: Result - do { - try Task.checkCancellation() - ret = try block.wrappedValue() - } catch { - if realm.inWriteTransaction { realm.cancelWriteTransaction() } - throw error - } - - if realm.inWriteTransaction { - try await realm.commitAsyncWrite(withGrouping: false) - } - return Unchecked(ret) - } - - /** - Updates the Realm and outstanding objects managed by the Realm to point to - the most recent data and deliver any applicable notifications. - - This function should be used instead of synchronous ``refresh`` in async - functions, as it suspends the calling task (if required) rather than - blocking. - - - warning: This function is only supported for main thread and - actor-isolated Realms. - - returns: Whether there were any updates for the Realm. Note that `true` - may be returned even if no data actually changed. - */ - @discardableResult - @_unsafeInheritExecutor - public func asyncRefresh() async -> Bool { - guard rlmRealm.actor is Actor else { - fatalError("asyncRefresh() can only be called on main thread or actor-isolated Realms") - } - guard let task = RLMRealmRefreshAsync(rlmRealm) else { - return false - } - return await withTaskCancellationHandler { - await task.wait() - } onCancel: { - task.complete(false) - } - } - -#else // compiler(<6) /** Performs actions contained within the given block inside a write transaction. @@ -1528,7 +1409,6 @@ extension Realm { task.complete(false) } } -#endif // compiler(<6) } @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) @@ -1607,26 +1487,9 @@ extension RLMAsyncDownloadTask: TaskWithCancellation {} @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) internal extension Actor { func verifier() -> (@Sendable () -> Void) { -#if compiler(>=5.10) - // This was made backdeployable in Xcode 15.3 return { self.preconditionIsolated() } -#else - // When possible use the official API for actor checking - if #available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *) { - return { - self.preconditionIsolated() - } - } - - // This exploits a hole in Swift's type system to construct a function - // which is isolated to the current actor, and then casts away that - // information. This results in runtime warnings/aborts if it's called - // from outside the actor when actor data race checking is enabled. - let fn: () -> Void = { _ = self } - return unsafeBitCast(fn, to: (@Sendable () -> Void).self) -#endif } // Asynchronously invoke the given block on the actor. This takes a diff --git a/RealmSwift/RealmCollection.swift b/RealmSwift/RealmCollection.swift index a2b81f0c48..c72da8e1df 100644 --- a/RealmSwift/RealmCollection.swift +++ b/RealmSwift/RealmCollection.swift @@ -524,105 +524,6 @@ public protocol RealmCollection: RealmCollectionBase, Equatable where Iterator = on queue: DispatchQueue?, _ block: @escaping (RealmCollectionChange) -> Void) -> NotificationToken -#if compiler(<6) - /** - Registers a block to be called each time the collection changes. - - The block will be asynchronously called with an initial version of the - collection, and then called again after each write transaction which changes - either any of the objects in the collection, or which objects are in the - collection. - - The `actor` parameter passed to the block is the actor which you pass to this - function. This parameter is required to isolate the callback to the actor. - - The `change` parameter that is passed to the block reports, in the form of - indices within the collection, which of the objects were added, removed, or - modified after the previous notification. The `collection` field in the change - enum will be isolated to the requested actor, and is safe to use within that - actor only. See the ``RealmCollectionChange`` documentation for more - information on the change information supplied and an example of how to use it - to update a `UITableView`. - - Once the initial notification is delivered, the collection will be fully - evaluated and up-to-date, and accessing it will never perform any blocking - work. This guarantee holds only as long as you do not perform a write - transaction on the same actor as notifications are being delivered to. If you - do, accessing the collection before the next notification is delivered may need - to rerun the query. - - Notifications are delivered to the given actor's executor. When notifications - can't be delivered instantly, multiple notifications may be coalesced into a - single notification. This can include the notification with the initial - collection: any writes which occur before the initial notification is delivered - may not produce change notifications. - - Adding, removing or assigning objects in the collection always produces a - notification. By default, modifying the objects which a collection links to - (and the objects which those objects link to, if applicable) will also report - that index in the collection as being modified. If a non-empty array of - keypaths is provided, then only modifications to those keypaths will mark the - object as modified. For example: - - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - - let dogs = realm.objects(Dog.self) - let token = await dogs.observe(keyPaths: ["name"], on: myActor) { actor, changes in - switch changes { - case .initial(let dogs): - // Query has finished running and dogs can not be used without blocking - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the collection is modified - // - when an element is inserted or removed from the collection. - // This block is not triggered: - // - when a value other than name is modified on one of the elements. - case .error: - // Can no longer happen but is left for backwards compatiblity - } - } - ``` - - If the observed key path were `["toys.brand"]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger - the block. Changes to the `brand` value on any `Toy` that is linked to a `Dog` - in this collection will trigger the block. Changes to a value other than - `brand` on any `Toy` that is linked to a `Dog` in this collection would not - trigger the block. Any insertion or removal to the `Dog` type collection being - observed would also trigger a notification. - - If the above example observed the `["toys"]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. Changes to any value on any `Toy` that is linked to a - `Dog` in this collection would *not* trigger the block. Any insertion or - removal to the `Dog` type collection being observed would still trigger a - notification. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If `nil` or empty, notifications - will be delivered for any property change on the object. - String key paths which do not correspond to a valid a property - will throw an exception. See description above for - more detail on linked properties. - - parameter actor: The actor to isolate the notifications to. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - func observe(keyPaths: [String]?, - on actor: A, - _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void) async -> NotificationToken -#else /** Registers a block to be called each time the collection changes. @@ -720,7 +621,6 @@ public protocol RealmCollection: RealmCollectionBase, Equatable where Iterator = on actor: A, _isolation: isolated (any Actor)?, _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void) async -> NotificationToken -#endif // MARK: Frozen Objects @@ -1227,107 +1127,6 @@ public extension RealmCollection { return self.observe(keyPaths: keyPaths, on: queue, block) } -#if compiler(<6) - /** - Registers a block to be called each time the collection changes. - - The block will be asynchronously called with an initial version of the - collection, and then called again after each write transaction which changes - either any of the objects in the collection, or which objects are in the - collection. - - The `actor` parameter passed to the block is the actor which you pass to this - function. This parameter is required to isolate the callback to the actor. - - The `change` parameter that is passed to the block reports, in the form of - indices within the collection, which of the objects were added, removed, or - modified after the previous notification. The `collection` field in the change - enum will be isolated to the requested actor, and is safe to use within that - actor only. See the ``RealmCollectionChange`` documentation for more - information on the change information supplied and an example of how to use it - to update a `UITableView`. - - Once the initial notification is delivered, the collection will be fully - evaluated and up-to-date, and accessing it will never perform any blocking - work. This guarantee holds only as long as you do not perform a write - transaction on the same actor as notifications are being delivered to. If you - do, accessing the collection before the next notification is delivered may need - to rerun the query. - - Notifications are delivered to the given actor's executor. When notifications - can't be delivered instantly, multiple notifications may be coalesced into a - single notification. This can include the notification with the initial - collection: any writes which occur before the initial notification is delivered - may not produce change notifications. - - Adding, removing or assigning objects in the collection always produces a - notification. By default, modifying the objects which a collection links to - (and the objects which those objects link to, if applicable) will also report - that index in the collection as being modified. If a non-empty array of - keypaths is provided, then only modifications to those keypaths will mark the - object as modified. For example: - - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - - let dogs = realm.objects(Dog.self) - let token = await dogs.observe(keyPaths: ["name"], on: myActor) { actor, changes in - switch changes { - case .initial(let dogs): - // Query has finished running and dogs can not be used without blocking - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the collection is modified - // - when an element is inserted or removed from the collection. - // This block is not triggered: - // - when a value other than name is modified on one of the elements. - case .error: - // Can no longer happen but is left for backwards compatiblity - } - } - ``` - - If the observed key path were `["toys.brand"]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger - the block. Changes to the `brand` value on any `Toy` that is linked to a `Dog` - in this collection will trigger the block. Changes to a value other than - `brand` on any `Toy` that is linked to a `Dog` in this collection would not - trigger the block. Any insertion or removal to the `Dog` type collection being - observed would also trigger a notification. - - If the above example observed the `["toys"]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. Changes to any value on any `Toy` that is linked to a - `Dog` in this collection would *not* trigger the block. Any insertion or - removal to the `Dog` type collection being observed would still trigger a - notification. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If `nil` or empty, notifications - will be delivered for any property change on the object. - String key paths which do not correspond to a valid a property - will throw an exception. See description above for - more detail on linked properties. - - parameter actor: The actor to isolate the notifications to. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - func observe(keyPaths: [String]? = nil, - on actor: A, - _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void) async -> NotificationToken { - await self.observe(keyPaths: keyPaths, on: actor, block) - } -#else /** Registers a block to be called each time the collection changes. @@ -1427,7 +1226,6 @@ public extension RealmCollection { _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void) async -> NotificationToken { await self.observe(keyPaths: keyPaths, on: actor, _isolation: _isolation, block) } -#endif } public extension RealmCollection where Element: ObjectBase { @@ -1551,106 +1349,6 @@ public extension RealmCollection where Element: ObjectBase { return self.observe(keyPaths: keyPaths.map(_name(for:)), on: queue, block) } -#if compiler(<6) - /** - Registers a block to be called each time the collection changes. - - The block will be asynchronously called with an initial version of the - collection, and then called again after each write transaction which changes - either any of the objects in the collection, or which objects are in the - collection. - - The `actor` parameter passed to the block is the actor which you pass to this - function. This parameter is required to isolate the callback to the actor. - - The `change` parameter that is passed to the block reports, in the form of - indices within the collection, which of the objects were added, removed, or - modified after the previous notification. The `collection` field in the change - enum will be isolated to the requested actor, and is safe to use within that - actor only. See the ``RealmCollectionChange`` documentation for more - information on the change information supplied and an example of how to use it - to update a `UITableView`. - - Once the initial notification is delivered, the collection will be fully - evaluated and up-to-date, and accessing it will never perform any blocking - work. This guarantee holds only as long as you do not perform a write - transaction on the same actor as notifications are being delivered to. If you - do, accessing the collection before the next notification is delivered may need - to rerun the query. - - Notifications are delivered to the given actor's executor. When notifications - can't be delivered instantly, multiple notifications may be coalesced into a - single notification. This can include the notification with the initial - collection: any writes which occur before the initial notification is delivered - may not produce change notifications. - - Adding, removing or assigning objects in the collection always produces a - notification. By default, modifying the objects which a collection links to - (and the objects which those objects link to, if applicable) will also report - that index in the collection as being modified. If a non-empty array of - keypaths is provided, then only modifications to those keypaths will mark the - object as modified. For example: - - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - - let dogs = realm.objects(Dog.self) - let token = await dogs.observe(keyPaths: [\.name], on: myActor) { actor, changes in - switch changes { - case .initial(let dogs): - // Query has finished running and dogs can not be used without blocking - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the collection is modified - // - when an element is inserted or removed from the collection. - // This block is not triggered: - // - when a value other than name is modified on one of the elements. - case .error: - // Can no longer happen but is left for backwards compatiblity - } - } - ``` - - If the observed key path were `[\.toys.brand]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger - the block. Changes to the `brand` value on any `Toy` that is linked to a `Dog` - in this collection will trigger the block. Changes to a value other than - `brand` on any `Toy` that is linked to a `Dog` in this collection would not - trigger the block. Any insertion or removal to the `Dog` type collection being - observed would also trigger a notification. - - If the above example observed the `[\.toys]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. Changes to any value on any `Toy` that is linked to a - `Dog` in this collection would *not* trigger the block. Any insertion or - removal to the `Dog` type collection being observed would still trigger a - notification. - - You must retain the returned token for as long as you want updates to be sent - to the block. To stop receiving updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If empty, notifications - will be delivered for any property change on the object. - String key paths which do not correspond to a valid a property - will throw an exception. See description above for - more detail on linked properties. - - parameter actor: The actor to isolate the notifications to. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - func observe(keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void) async -> NotificationToken { - await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) - } -#else /** Registers a block to be called each time the collection changes. @@ -1749,7 +1447,6 @@ public extension RealmCollection where Element: ObjectBase { _ block: @Sendable @escaping (isolated A, RealmCollectionChange) -> Void) async -> NotificationToken { await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) } -#endif } extension RealmCollection { diff --git a/RealmSwift/Results.swift b/RealmSwift/Results.swift index 68ad91c000..306535e0fe 100644 --- a/RealmSwift/Results.swift +++ b/RealmSwift/Results.swift @@ -149,53 +149,6 @@ extension Projection: KeypathSortable {} // MARK: Flexible Sync -#if compiler(<6) - /** - Creates a SyncSubscription matching the Results' local query. - After committing the subscription to the realm's local subscription set, the method - will wait for downloads according to `WaitForSyncMode`. - - ### Unnamed subscriptions ### - If `.subscribe()` is called without a name whose query matches an unnamed subscription, another subscription is not created. - - If `.subscribe()` is called without a name whose query matches a named subscription, an additional unnamed subscription is created. - ### Named Subscriptions ### - If `.subscribe()` is called with a name whose query matches an unnamed subscription, an additional named subscription is created. - ### Existing name and query ### - If `.subscribe()` is called with a name whose name is taken on a different query, the old subscription is updated with the new query. - - If `.subscribe()` is called with a name that's in already in use by an identical query, no new subscription is created. - - - - Note: This method will wait for all data to be downloaded before returning when `WaitForSyncMode.always` and `.onCreation` (when the subscription is first created) is used. This requires an internet connection if no timeout is set. - - - Note: This method opens a update transaction that creates or updates a subscription. - It's advised to *not* loop over this method in order to create multiple subscriptions. - This could create a performance bottleneck by opening multiple unnecessary update transactions. - To create multiple subscriptions at once use `SyncSubscription.update`. - - - parameter name: The name applied to the subscription - - parameter waitForSync: ``WaitForSyncMode`` Determines the download behavior for the subscription. Defaults to `.onCreation`. - - parameter timeout: An optional client timeout. The client will cancel waiting for subscription downloads after this time has elapsed. Reaching this timeout doesn't imply a server error. - - returns: Returns `self`. - - - warning: This function is only supported for main thread and - actor-isolated Realms. - - warning: This API is currently in `Preview` and may be subject to changes in the future. - */ - @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - public func subscribe(name: String? = nil, waitForSync: WaitForSyncMode = .onCreation, timeout: TimeInterval? = nil) async throws -> Results { - guard let actor = realm?.rlmRealm.actor as? Actor else { - fatalError("`subscribe` can only be called on main thread or actor-isolated Realms") - } - - var rlmResults = ObjectiveCSupport.convert(object: self) - let scheduler = await RLMScheduler.actor(actor, invoke: actor.invoke, verify: actor.verifier()) - rlmResults = try await rlmResults.subscribe(withName: name, waitForSync: waitForSync, confinedTo: scheduler, timeout: timeout ?? 0) - return self - } -#else /** Creates a SyncSubscription matching the Results' local query. After committing the subscription to the realm's local subscription set, the method @@ -252,7 +205,6 @@ extension Projection: KeypathSortable {} } return self } -#endif /** Removes a SyncSubscription matching the Results' local filter. diff --git a/RealmSwift/SectionedResults.swift b/RealmSwift/SectionedResults.swift index a2fb1a6055..d03c9ed3e4 100644 --- a/RealmSwift/SectionedResults.swift +++ b/RealmSwift/SectionedResults.swift @@ -191,28 +191,6 @@ public protocol RealmSectionedResult: RandomAccessCollection, Equatable, ThreadC _ block: @escaping (SectionedResultsChange) -> Void) -> NotificationToken } -#if compiler(<6) -public extension RealmSectionedResult { - func observe(keyPaths: [String]? = nil, - on queue: DispatchQueue? = nil, - _ block: @escaping (SectionedResultsChange) -> Void) -> NotificationToken { - observe(keyPaths: keyPaths, on: queue, block) - } - - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - func observe( - keyPaths: [String]? = nil, on actor: A, - _ block: @Sendable @escaping (isolated A, SectionedResultsChange) -> Void - ) async -> NotificationToken { - await with(self, on: actor) { actor, collection in - collection.observe(keyPaths: keyPaths, on: nil) { change in - actor.invokeIsolated(block, change) - } - } - } -} -#else public extension RealmSectionedResult { func observe(keyPaths: [String]? = nil, on queue: DispatchQueue? = nil, @@ -234,279 +212,7 @@ public extension RealmSectionedResult { } } } -#endif - -#if compiler(<6) -public extension RealmSectionedResult where Element: RealmSectionedResult, Element.Element: ObjectBase { - /** - Registers a block to be called each time the sectioned results collection changes. - - The block will be asynchronously called with the initial sectioned results collection, and then called again after each write - transaction which changes either any of the objects in the sectioned results collection, or which objects are in the sectioned results collection. - - The `change` parameter that is passed to the block reports, in the form of indices within the collection, which of - the objects were added, removed, or modified during each write transaction. See the `SectionedResultsChange` - documentation for more information on the change information supplied and an example of how to use it to update a - `UITableView`. - - At the time when the block is called, the collection will be fully evaluated and up-to-date, and as long as you do - not perform a write transaction on the same thread or explicitly call `realm.refresh()`, accessing it will never - perform blocking work. - - If no queue is given, notifications are delivered via the standard run loop, and so can't be delivered while the - run loop is blocked by other activity. If a queue is given, notifications are delivered to that queue instead. When - notifications can't be delivered instantly, multiple notifications may be coalesced into a single notification. - This can include the notification with the initial sectioned results collection. - - For example, the following code performs a write transaction immediately after adding the notification block, so - there is no opportunity for the initial notification to be delivered first. As a result, the initial notification - will reflect the state of the Realm after the write transaction. - - ```swift - let dogs = realm.objects(Dog.self) - let sectionedResults = dogs.sectioned(by: \.age, ascending: true) - print("sectionedResults.count: \(sectionedResults?.count)") // => 0 - let token = sectionedResults.observe { changes in - switch changes { - case .initial(let sectionedResults): - // Will print "sectionedResults.count: 1" - print("sectionedResults.count: \(sectionedResults.count)") - break - case .update: - // Will not be hit in this example - break - case .error: - break - } - } - try! realm.write { - let dog = Dog() - dog.name = "Rex" - person.dogs.append(dog) - } - // end of run loop execution context - ``` - - If no key paths are given, the block will be executed on any insertion, - modification, or deletion for all object properties and the properties of - any nested, linked objects. If a key path or key paths are provided, - then the block will be called for changes which occur only on the - provided key paths. For example, if: - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - // ... - let dogs = realm.objects(Dog.self) - let sectionedResults = dogs.sectioned(by: \.age, ascending: true) - let token = sectionedResults.observe(keyPaths: ["name"]) { changes in - switch changes { - case .initial(let sectionedResults): - // ... - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the - // collection is modified - // - when an element is inserted or removed - // from the collection. - // This block is not triggered: - // - when a value other than name is modified on - // one of the elements. - case .error: - // ... - } - } - // end of run loop execution context - ``` - - If the observed key path were `["toys.brand"]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger the block. - Changes to the `brand` value on any `Toy` that is linked to a `Dog` in this - collection will trigger the block. Changes to a value other than `brand` on any `Toy` that - is linked to a `Dog` in this collection would not trigger the block. - Any insertion or removal to the `Dog` type collection being observed - would also trigger a notification. - - If the above example observed the `["toys"]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. - Changes to any value on any `Toy` that is linked to a `Dog` in this collection - would *not* trigger the block. - Any insertion or removal to the `Dog` type collection being observed - would still trigger a notification. - - Any modification to the section key path property which results in the object changing - position in the section, or changing section entirely will trigger a notification. - - - note: Multiple notification tokens on the same object which filter for - separate key paths *do not* filter exclusively. If one key path - change is satisfied for one notification token, then all notification - token blocks for that object will execute. - - You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving - updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If `nil`, notifications - will be delivered for any property change on the object. - - parameter queue: The serial dispatch queue to receive notification on. If - `nil`, notifications are delivered to the current thread. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - func observe(keyPaths: [PartialKeyPath], - on queue: DispatchQueue? = nil, - _ block: @escaping (SectionedResultsChange) -> Void) -> NotificationToken { - observe(keyPaths: keyPaths.map(_name(for:)), on: queue, block) - } - - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - func observe( - keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, SectionedResultsChange) -> Void - ) async -> NotificationToken { - await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) - } -} - -public extension RealmSectionedResult where Element: ObjectBase { - /** - Registers a block to be called each time the sectioned results collection changes. - - The block will be asynchronously called with the initial sectioned results collection, and then called again after each write - transaction which changes either any of the objects in the sectioned results collection, or which objects are in the sectioned results collection. - - The `change` parameter that is passed to the block reports, in the form of indices within the collection, which of - the objects were added, removed, or modified during each write transaction. See the `SectionedResultsChange` - documentation for more information on the change information supplied and an example of how to use it to update a - `UITableView`. - - At the time when the block is called, the collection will be fully evaluated and up-to-date, and as long as you do - not perform a write transaction on the same thread or explicitly call `realm.refresh()`, accessing it will never - perform blocking work. - - If no queue is given, notifications are delivered via the standard run loop, and so can't be delivered while the - run loop is blocked by other activity. If a queue is given, notifications are delivered to that queue instead. When - notifications can't be delivered instantly, multiple notifications may be coalesced into a single notification. - This can include the notification with the initial sectioned results collection. - - For example, the following code performs a write transaction immediately after adding the notification block, so - there is no opportunity for the initial notification to be delivered first. As a result, the initial notification - will reflect the state of the Realm after the write transaction. - - ```swift - let dogs = realm.objects(Dog.self) - let sectionedResults = dogs.sectioned(by: \.age, ascending: true) - print("sectionedResults.count: \(sectionedResults?.count)") // => 0 - let token = sectionedResults.observe { changes in - switch changes { - case .initial(let sectionedResults): - // Will print "sectionedResults.count: 1" - print("sectionedResults.count: \(sectionedResults.count)") - break - case .update: - // Will not be hit in this example - break - case .error: - break - } - } - try! realm.write { - let dog = Dog() - dog.name = "Rex" - person.dogs.append(dog) - } - // end of run loop execution context - ``` - If no key paths are given, the block will be executed on any insertion, - modification, or deletion for all object properties and the properties of - any nested, linked objects. If a key path or key paths are provided, - then the block will be called for changes which occur only on the - provided key paths. For example, if: - ```swift - class Dog: Object { - @Persisted var name: String - @Persisted var age: Int - @Persisted var toys: List - } - // ... - let dogs = realm.objects(Dog.self) - let sectionedResults = dogs.sectioned(by: \.age, ascending: true) - let token = sectionedResults.observe(keyPaths: ["name"]) { changes in - switch changes { - case .initial(let sectionedResults): - // ... - case .update: - // This case is hit: - // - after the token is initialized - // - when the name property of an object in the - // collection is modified - // - when an element is inserted or removed - // from the collection. - // This block is not triggered: - // - when a value other than name is modified on - // one of the elements. - case .error: - // ... - } - } - // end of run loop execution context - ``` - - If the observed key path were `["toys.brand"]`, then any insertion or - deletion to the `toys` list on any of the collection's elements would trigger the block. - Changes to the `brand` value on any `Toy` that is linked to a `Dog` in this - collection will trigger the block. Changes to a value other than `brand` on any `Toy` that - is linked to a `Dog` in this collection would not trigger the block. - Any insertion or removal to the `Dog` type collection being observed - would also trigger a notification. - - If the above example observed the `["toys"]` key path, then any insertion, - deletion, or modification to the `toys` list for any element in the collection - would trigger the block. - Changes to any value on any `Toy` that is linked to a `Dog` in this collection - would *not* trigger the block. - Any insertion or removal to the `Dog` type collection being observed - would still trigger a notification. - - Any modification to the section key path property which results in the object changing - position in the section, or changing section entirely will trigger a notification. - - - note: Multiple notification tokens on the same object which filter for - separate key paths *do not* filter exclusively. If one key path - change is satisfied for one notification token, then all notification - token blocks for that object will execute. - - You must retain the returned token for as long as you want updates to be sent to the block. To stop receiving - updates, call `invalidate()` on the token. - - - warning: This method cannot be called during a write transaction, or when the containing Realm is read-only. - - - parameter keyPaths: Only properties contained in the key paths array will trigger - the block when they are modified. If `nil`, notifications - will be delivered for any property change on the object. - - parameter queue: The serial dispatch queue to receive notification on. If - `nil`, notifications are delivered to the current thread. - - parameter block: The block to be called whenever a change occurs. - - returns: A token which must be held for as long as you want updates to be delivered. - */ - func observe(keyPaths: [PartialKeyPath], - on queue: DispatchQueue? = nil, - _ block: @escaping (SectionedResultsChange) -> Void) -> NotificationToken { - observe(keyPaths: keyPaths.map(_name(for:)), on: queue, block) - } - - @available(macOS 10.15, tvOS 13.0, iOS 13.0, watchOS 6.0, *) - @_unsafeInheritExecutor - func observe( - keyPaths: [PartialKeyPath], on actor: A, - _ block: @Sendable @escaping (isolated A, SectionedResultsChange) -> Void - ) async -> NotificationToken { - await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) - } -} -#else public extension RealmSectionedResult where Element: RealmSectionedResult, Element.Element: ObjectBase { /** Registers a block to be called each time the sectioned results collection changes. @@ -778,7 +484,6 @@ public extension RealmSectionedResult where Element: ObjectBase { await observe(keyPaths: keyPaths.map(_name(for:)), on: actor, block) } } -#endif // Shared implementation of SectionedResults and ResultsSection private protocol SectionedResultImpl: RealmSectionedResult { diff --git a/RealmSwift/Sync.swift b/RealmSwift/Sync.swift index de50726b4d..b8490f6f59 100644 --- a/RealmSwift/Sync.swift +++ b/RealmSwift/Sync.swift @@ -586,11 +586,7 @@ public typealias InitialSubscriptionsConfiguration = RLMInitialSubscriptionsConf // Although RLMSyncConfiguration objects are mutable, we don't expose a way // to mutate the one wrapped by this struct, which makes this safe - #if compiler(<6) - internal let config: RLMSyncConfiguration - #else nonisolated(unsafe) internal let config: RLMSyncConfiguration - #endif internal init(config: RLMSyncConfiguration) { self.config = config } @@ -1128,13 +1124,8 @@ extension User { } } -#if compiler(>=6) @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension User: @retroactive ObservableObject {} -#else -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension User: ObservableObject {} -#endif public extension User { /// Refresh a user's custom data. This will, in effect, refresh the user's auth session. diff --git a/RealmSwift/SyncSubscription.swift b/RealmSwift/SyncSubscription.swift index d42ec94ad6..88edaee3ef 100644 --- a/RealmSwift/SyncSubscription.swift +++ b/RealmSwift/SyncSubscription.swift @@ -170,19 +170,6 @@ import Realm.Private /// :nodoc: public typealias QueryFunction = (Query) -> Query -#if compiler(<6) - /** - Creates a `QuerySubscription` for the given type. - - - parameter name: Name of the subscription. - - parameter query: The query for the subscription, if nil it will set the query to all documents for the collection. - */ - public init(name: String? = nil, query: QueryFunction? = nil) { - self.name = name - self.className = "\(T.self)" - self.predicate = query?(Query()).predicate ?? NSPredicate(format: "TRUEPREDICATE") - } -#else /** Creates a `QuerySubscription` which subscribes to all documents of the given type. @@ -193,7 +180,6 @@ import Realm.Private self.className = "\(T.self)" self.predicate = NSPredicate(format: "TRUEPREDICATE") } -#endif /** Creates a `QuerySubscription` for the given type. diff --git a/RealmSwift/Tests/AnyRealmValueTests.swift b/RealmSwift/Tests/AnyRealmValueTests.swift index cd7ee506f7..e66d91507f 100644 --- a/RealmSwift/Tests/AnyRealmValueTests.swift +++ b/RealmSwift/Tests/AnyRealmValueTests.swift @@ -91,7 +91,7 @@ func doubleValue(_ value: AnyRealmValue) -> Double { } } -class AnyRealmValueTests: TestCase, @unchecked Sendable { +class AnyRealmValueTests: TestCase { func testAnyRealmValue() { let values = T.anyValues() let o = AnyRealmTypeObject() @@ -110,7 +110,7 @@ class AnyRealmValueTests: TestCase, @unchecked Sendable { XCTAssertEqual(o.anyValue.value, values[2]) } } -class AnyRealmValuePrimitiveTests: TestCase, @unchecked Sendable { +class AnyRealmValuePrimitiveTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Any Realm Value Tests") AnyRealmValueTests.defaultTestSuite.tests.forEach(suite.addTest) @@ -126,7 +126,7 @@ class AnyRealmValuePrimitiveTests: TestCase, @unchecked Sendable { } } -class AnyRealmValueObjectTests: TestCase, @unchecked Sendable { +class AnyRealmValueObjectTests: TestCase { func testObject() { let o = AnyRealmTypeObject() let so = SwiftStringObject() @@ -237,7 +237,7 @@ class AnyRealmValueObjectTests: TestCase, @unchecked Sendable { // MARK: - List tests -class AnyRealmValueListTestsBase: TestCase, @unchecked Sendable { +class AnyRealmValueListTestsBase: TestCase { var realm: Realm? var obj: ModernAllTypesObject! var array: List! @@ -257,7 +257,7 @@ class AnyRealmValueListTestsBase: TestCase } } -class AnyRealmValueListTests: AnyRealmValueListTestsBase, @unchecked Sendable { +class AnyRealmValueListTests: AnyRealmValueListTestsBase { private func assertEqual(_ obj: AnyRealmValue, _ anotherObj: AnyRealmValue) { if case let .object(a) = obj, case let .object(b) = anotherObj { XCTAssertEqual((a as! SwiftStringObject).stringCol, (b as! SwiftStringObject).stringCol) @@ -487,7 +487,7 @@ class AnyRealmValueListTests: AnyRealmValu } } -class MinMaxAnyRealmValueListTests: AnyRealmValueListTestsBase, @unchecked Sendable { +class MinMaxAnyRealmValueListTests: AnyRealmValueListTestsBase { func testMin() { XCTAssertNil(array.min()) array.append(objectsIn: values.reversed()) @@ -501,7 +501,7 @@ class MinMaxAnyRealmValueListTests: AnyRea } } -class AddableAnyRealmValueListTests: AnyRealmValueListTestsBase, @unchecked Sendable where V: NumericValueFactory { +class AddableAnyRealmValueListTests: AnyRealmValueListTestsBase where V: NumericValueFactory { func testSum() { if array.realm != nil { XCTAssertEqual(array.sum().intValue, nil) @@ -547,7 +547,7 @@ func addAnyRealmValueTests(_ suite: XCTestSuite, _ type: OF.T AddableAnyRealmValueListTests.defaultTestSuite.tests.forEach(suite.addTest) } -class UnmanagedAnyRealmValueListTests: TestCase, @unchecked Sendable { +class UnmanagedAnyRealmValueListTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Unmanaged AnyRealmValue Lists") addAnyRealmValueTests(suite, UnmanagedObjectFactory.self) @@ -555,7 +555,7 @@ class UnmanagedAnyRealmValueListTests: TestCase, @unchecked Sendable { } } -class ManagedAnyRealmValueListTests: TestCase, @unchecked Sendable { +class ManagedAnyRealmValueListTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Managed AnyRealmValue Lists") addAnyRealmValueTests(suite, ManagedObjectFactory.self) @@ -565,7 +565,7 @@ class ManagedAnyRealmValueListTests: TestCase, @unchecked Sendable { // MARK: - Set tests -class AnyRealmValueSetTestsBase: TestCase, @unchecked Sendable { +class AnyRealmValueSetTestsBase: TestCase { var realm: Realm? var obj: ModernAllTypesObject! var obj2: ModernAllTypesObject! @@ -590,7 +590,7 @@ class AnyRealmValueSetTestsBase: TestCase, } } -class AnyRealmValueMutableSetTests: AnyRealmValueSetTestsBase, @unchecked Sendable { +class AnyRealmValueMutableSetTests: AnyRealmValueSetTestsBase { private func assertEqual(_ obj: AnyRealmValue, _ anotherObj: AnyRealmValue) { if case let .object(a) = obj, case let .object(b) = anotherObj { @@ -799,7 +799,7 @@ class AnyRealmValueMutableSetTests: AnyRea } } -class MinMaxAnyRealmValueMutableSetTests: AnyRealmValueSetTestsBase, @unchecked Sendable { +class MinMaxAnyRealmValueMutableSetTests: AnyRealmValueSetTestsBase { func testMin() { XCTAssertNil(mutableSet.min()) mutableSet.insert(objectsIn: values) @@ -813,7 +813,7 @@ class MinMaxAnyRealmValueMutableSetTests: } } -class AddableAnyRealmValueMutableSetTests: AnyRealmValueSetTestsBase, @unchecked Sendable where V: NumericValueFactory { +class AddableAnyRealmValueMutableSetTests: AnyRealmValueSetTestsBase where V: NumericValueFactory { func testSum() { if mutableSet.realm != nil { XCTAssertEqual(mutableSet.sum().intValue, nil) @@ -858,7 +858,7 @@ func addAnyRealmValueMutableSetTests(_ suite: XCTestSuite, _ AddableAnyRealmValueMutableSetTests.defaultTestSuite.tests.forEach(suite.addTest) } -class UnmanagedAnyRealmValueMutableSetTests: TestCase, @unchecked Sendable { +class UnmanagedAnyRealmValueMutableSetTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Unmanaged Primitive Sets") addAnyRealmValueMutableSetTests(suite, UnmanagedObjectFactory.self) @@ -866,7 +866,7 @@ class UnmanagedAnyRealmValueMutableSetTests: TestCase, @unchecked Sendable { } } -class ManagedAnyRealmValueMutableSetTests: TestCase, @unchecked Sendable { +class ManagedAnyRealmValueMutableSetTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Managed Primitive Sets") addAnyRealmValueMutableSetTests(suite, ManagedObjectFactory.self) @@ -876,7 +876,7 @@ class ManagedAnyRealmValueMutableSetTests: TestCase, @unchecked Sendable { // MARK: - Map tests -class AnyRealmValueMapTestsBase: TestCase, @unchecked Sendable { +class AnyRealmValueMapTestsBase: TestCase { var realm: Realm? var obj: ModernAllTypesObject! var map: Map! @@ -897,7 +897,7 @@ class AnyRealmValueMapTestsBase: TestCase, } } -class AnyRealmValueMapTests: AnyRealmValueMapTestsBase, @unchecked Sendable { +class AnyRealmValueMapTests: AnyRealmValueMapTestsBase { func testInvalidated() { XCTAssertFalse(map.isInvalidated) if let realm = obj.realm { @@ -1042,7 +1042,7 @@ class AnyRealmValueMapTests: AnyRealmValue } } -class MinMaxAnyRealmValueMapTests: AnyRealmValueMapTestsBase, @unchecked Sendable { +class MinMaxAnyRealmValueMapTests: AnyRealmValueMapTestsBase { func testMin() { XCTAssertNil(map.min()) map.merge(values) { $1 } @@ -1056,7 +1056,7 @@ class MinMaxAnyRealmValueMapTests: AnyReal } } -class AddableAnyRealmValueMapTests: AnyRealmValueMapTestsBase, @unchecked Sendable where V: NumericValueFactory { +class AddableAnyRealmValueMapTests: AnyRealmValueMapTestsBase where V: NumericValueFactory { func testSum() { XCTAssertEqual(map.sum().intValue, 0) map.merge(values) { $1 } @@ -1096,7 +1096,7 @@ func addAnyRealmValueMapTests(_ suite: XCTestSuite, _ type: O AddableAnyRealmValueMapTests.defaultTestSuite.tests.forEach(suite.addTest) } -class UnmanagedAnyRealmValueMapTests: TestCase, @unchecked Sendable { +class UnmanagedAnyRealmValueMapTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Unmanaged AnyRealmValue Maps") addAnyRealmValueMapTests(suite, UnmanagedObjectFactory.self) @@ -1104,7 +1104,7 @@ class UnmanagedAnyRealmValueMapTests: TestCase, @unchecked Sendable { } } -class ManagedAnyRealmValueMapTests: TestCase, @unchecked Sendable { +class ManagedAnyRealmValueMapTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Managed AnyRealmValue Maps") addAnyRealmValueMapTests(suite, ManagedObjectFactory.self) diff --git a/RealmSwift/Tests/CodableTests.swift b/RealmSwift/Tests/CodableTests.swift index 567113ca3d..981f4c2e23 100644 --- a/RealmSwift/Tests/CodableTests.swift +++ b/RealmSwift/Tests/CodableTests.swift @@ -291,7 +291,7 @@ final class CodableEmbeddedObject: EmbeddedObject, Codable { } @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class CodableTests: TestCase, @unchecked Sendable { +class CodableTests: TestCase { let decoder = JSONDecoder() let encoder = JSONEncoder() diff --git a/RealmSwift/Tests/CombineTests.swift b/RealmSwift/Tests/CombineTests.swift index 54eaacbd10..9c622f8ff5 100644 --- a/RealmSwift/Tests/CombineTests.swift +++ b/RealmSwift/Tests/CombineTests.swift @@ -48,7 +48,7 @@ func hasCombine() -> Bool { } @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) -class ObjectIdentifiableTests: TestCase, @unchecked Sendable { +class ObjectIdentifiableTests: TestCase { override class var defaultTestSuite: XCTestSuite { if hasCombine() { return super.defaultTestSuite @@ -94,7 +94,7 @@ class ObjectIdentifiableTests: TestCase, @unchecked Sendable { } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class CombinePublisherTestCase: TestCase, @unchecked Sendable { +class CombinePublisherTestCase: TestCase { var realm: Realm! var cancellable: AnyCancellable? var notificationToken: NotificationToken? @@ -144,7 +144,7 @@ class CombinePublisherTestCase: TestCase, @unchecked Sendable { } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class CombineRealmTests: CombinePublisherTestCase, @unchecked Sendable { +class CombineRealmTests: CombinePublisherTestCase { func testWillChangeLocalWrite() { var called = false cancellable = realm @@ -199,8 +199,9 @@ class CombineRealmTests: CombinePublisherTestCase, @unchecked Sendable { cancellable = realm.objectWillChange.sink { exp.fulfill() } + let configuration = self.realm.configuration subscribeOnQueue.async { - let backgroundRealm = try! Realm(configuration: self.realm.configuration) + let backgroundRealm = try! Realm(configuration: configuration) try! backgroundRealm.write { backgroundRealm.create(SwiftIntObject.self) } @@ -212,7 +213,7 @@ class CombineRealmTests: CombinePublisherTestCase, @unchecked Sendable { // MARK: - Object @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class CombineObjectPublisherTests: CombinePublisherTestCase, @unchecked Sendable { +class CombineObjectPublisherTests: CombinePublisherTestCase { var obj: SwiftIntObject! override func setUp() { @@ -785,8 +786,26 @@ private protocol CombineTestCollection { // MARK: - List, MutableSet +private func checkChangeset(_ change: RealmCollectionChange, calls: Int, frozen: Bool = false) { + switch change { + case .initial(let collection): + XCTAssertEqual(collection.isFrozen, frozen) + XCTAssertEqual(calls, 0) + XCTAssertEqual(collection.count, 0) + case .update(let collection, deletions: let deletions, insertions: let insertions, + modifications: let modifications): + XCTAssertEqual(collection.isFrozen, frozen) + XCTAssertEqual(collection.count, calls) + XCTAssertEqual(insertions, [calls - 1]) + XCTAssertEqual(deletions, []) + XCTAssertEqual(modifications, []) + case .error(let error): + XCTFail("Unexpected error \(error)") + } +} + @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -private class CombineCollectionPublisherTests: CombinePublisherTestCase, @unchecked Sendable +private class CombineCollectionPublisherTests: CombinePublisherTestCase where Collection: CombineTestCollection, Collection: RealmSubscribable { var collection: Collection! @@ -870,30 +889,12 @@ private class CombineCollectionPublisherTests: Comb } } - func checkChangeset(_ change: RealmCollectionChange, calls: Int, frozen: Bool = false) { - switch change { - case .initial(let collection): - XCTAssertEqual(collection.isFrozen, frozen) - XCTAssertEqual(calls, 0) - XCTAssertEqual(collection.count, 0) - case .update(let collection, deletions: let deletions, insertions: let insertions, - modifications: let modifications): - XCTAssertEqual(collection.isFrozen, frozen) - XCTAssertEqual(collection.count, calls) - XCTAssertEqual(insertions, [calls - 1]) - XCTAssertEqual(deletions, []) - XCTAssertEqual(modifications, []) - case .error(let error): - XCTFail("Unexpected error \(error)") - } - } - func testChangeSet() { var exp = XCTestExpectation(description: "initial") var calls = 0 cancellable = collection.changesetPublisher .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -913,7 +914,7 @@ private class CombineCollectionPublisherTests: Comb .changesetPublisher .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1073,7 +1074,7 @@ private class CombineCollectionPublisherTests: Comb cancellable = collection.changesetPublisher .subscribe(on: subscribeOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 sema.signal() } @@ -1128,7 +1129,7 @@ private class CombineCollectionPublisherTests: Comb .subscribe(on: subscribeOnQueue) .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 sema.signal() } @@ -1147,7 +1148,7 @@ private class CombineCollectionPublisherTests: Comb cancellable = collection.changesetPublisher .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1168,7 +1169,7 @@ private class CombineCollectionPublisherTests: Comb .saveToken(on: self, at: \.notificationToken) .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1212,7 +1213,7 @@ private class CombineCollectionPublisherTests: Comb .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1235,7 +1236,7 @@ private class CombineCollectionPublisherTests: Comb .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1283,7 +1284,7 @@ private class CombineCollectionPublisherTests: Comb .sink { @Sendable arr in XCTAssertEqual(arr.count, 10) for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } sema.signal() } @@ -1306,7 +1307,7 @@ private class CombineCollectionPublisherTests: Comb .sink { @Sendable arr in XCTAssertEqual(arr.count, 10) for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } exp.fulfill() } @@ -1329,7 +1330,7 @@ private class CombineCollectionPublisherTests: Comb .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } sema.signal() } @@ -1375,7 +1376,7 @@ private class CombineCollectionPublisherTests: Comb .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } exp.fulfill() } @@ -1410,7 +1411,7 @@ extension Results: CombineTestCollection where Element == ModernAllTypesObject { } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class ResultsPublisherTests: TestCase, @unchecked Sendable { +class ResultsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineCollectionPublisherTests>.testSuite("Results") } @@ -1440,7 +1441,7 @@ extension List: CombineTestCollection where Element == ModernAllTypesObject { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class ManagedListPublisherTests: TestCase, @unchecked Sendable { +class ManagedListPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineCollectionPublisherTests>.testSuite("List") } @@ -1469,7 +1470,7 @@ extension MutableSet: CombineTestCollection where Element == ModernAllTypesObjec } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class ManagedMutableSetPublisherTests: TestCase, @unchecked Sendable { +class ManagedMutableSetPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineCollectionPublisherTests>.testSuite("MutableSet") } @@ -1501,7 +1502,7 @@ extension LinkingObjects: CombineTestCollection where Element == ModernAllTypesO } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class LinkingObjectsPublisherTests: TestCase, @unchecked Sendable { +class LinkingObjectsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineCollectionPublisherTests>.testSuite("LinkingObjects") } @@ -1530,7 +1531,7 @@ extension AnyRealmCollection: CombineTestCollection where Element == ModernAllTy } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class AnyRealmCollectionPublisherTests: TestCase, @unchecked Sendable { +class AnyRealmCollectionPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineCollectionPublisherTests>.testSuite("AnyRealmCollection") } @@ -1538,8 +1539,26 @@ class AnyRealmCollectionPublisherTests: TestCase, @unchecked Sendable { // MARK: - Map +private func checkChangeset(_ change: RealmMapChange, calls: Int, frozen: Bool = false) { + switch change { + case .initial(let collection): + XCTAssertEqual(collection.isFrozen, frozen) + XCTAssertEqual(calls, 0) + XCTAssertEqual(collection.count, 0) + case .update(let collection, deletions: let deletions, insertions: let insertions, modifications: let modifications): + XCTAssertEqual(collection.isFrozen, frozen) + XCTAssertEqual(collection.count, calls) + // one insertion at a time + XCTAssertEqual(insertions.count, 1) + XCTAssertEqual(modifications, []) + XCTAssertEqual(deletions, []) + case .error(let error): + XCTFail("Unexpected error \(error)") + } +} + @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -private class CombineMapPublisherTests: CombinePublisherTestCase, @unchecked Sendable +private class CombineMapPublisherTests: CombinePublisherTestCase where Collection: CombineTestCollection, Collection: RealmSubscribable { var collection: Collection! @@ -1623,30 +1642,12 @@ private class CombineMapPublisherTests: Combin } } - func checkChangeset(_ change: RealmMapChange, calls: Int, frozen: Bool = false) { - switch change { - case .initial(let collection): - XCTAssertEqual(collection.isFrozen, frozen) - XCTAssertEqual(calls, 0) - XCTAssertEqual(collection.count, 0) - case .update(let collection, deletions: let deletions, insertions: let insertions, modifications: let modifications): - XCTAssertEqual(collection.isFrozen, frozen) - XCTAssertEqual(collection.count, calls) - // one insertion at a time - XCTAssertEqual(insertions.count, 1) - XCTAssertEqual(modifications, []) - XCTAssertEqual(deletions, []) - case .error(let error): - XCTFail("Unexpected error \(error)") - } - } - func testChangeSet() { var exp = XCTestExpectation(description: "initial") var calls = 0 cancellable = collection.changesetPublisher .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1666,7 +1667,7 @@ private class CombineMapPublisherTests: Combin .changesetPublisher .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1826,7 +1827,7 @@ private class CombineMapPublisherTests: Combin cancellable = collection.changesetPublisher .subscribe(on: subscribeOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 sema.signal() } @@ -1883,7 +1884,7 @@ private class CombineMapPublisherTests: Combin .subscribe(on: subscribeOnQueue) .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 sema.signal() } @@ -1902,7 +1903,7 @@ private class CombineMapPublisherTests: Combin cancellable = collection.changesetPublisher .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1923,7 +1924,7 @@ private class CombineMapPublisherTests: Combin .saveToken(on: self, at: \.notificationToken) .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1967,7 +1968,7 @@ private class CombineMapPublisherTests: Combin .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -1990,7 +1991,7 @@ private class CombineMapPublisherTests: Combin .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, calls: calls) + checkChangeset(change, calls: calls) calls += 1 exp.fulfill() } @@ -2036,7 +2037,7 @@ private class CombineMapPublisherTests: Combin .collect() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } sema.signal() } @@ -2058,7 +2059,7 @@ private class CombineMapPublisherTests: Combin .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } exp.fulfill() } @@ -2081,7 +2082,7 @@ private class CombineMapPublisherTests: Combin .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } sema.signal() } @@ -2127,7 +2128,7 @@ private class CombineMapPublisherTests: Combin .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, calls: i, frozen: true) + checkChangeset(change, calls: i, frozen: true) } exp.fulfill() } @@ -2163,7 +2164,7 @@ extension Map: CombineTestCollection where Key == String, Value == SwiftObject? } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class ManagedMapPublisherTests: TestCase, @unchecked Sendable { +class ManagedMapPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineMapPublisherTests>.testSuite("Map") } @@ -2180,8 +2181,23 @@ extension ModernAllTypesObject: RealmSectionedObject { var key: Int8 { int8Col } // This property will never change. } +private func checkChangeset( + _ change: SectionedResultsChange, + insertions: [IndexPath] = [], deletions: [IndexPath] = [], frozen: Bool = false) { + switch change { + case .initial(let collection): + XCTAssertEqual(collection.isFrozen, frozen) + case .update(let collection, deletions: let del, insertions: let ins, + modifications: let modifications, _, _): + XCTAssertEqual(collection.isFrozen, frozen) + XCTAssertEqual(ins, insertions) + XCTAssertEqual(del, deletions) + XCTAssertEqual(modifications, []) + } +} + @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -private class CombineSectionedResultsPublisherTests: CombinePublisherTestCase, @unchecked Sendable +private class CombineSectionedResultsPublisherTests: CombinePublisherTestCase where Collection: CombineTestCollection, Collection: RealmSubscribable, Collection.Element: RealmSectionedObject { var collection: Collection! @@ -2330,28 +2346,13 @@ private class CombineSectionedResultsPublisherTests } } - func checkChangeset( - _ change: SectionedResultsChange, - insertions: [IndexPath] = [], deletions: [IndexPath] = [], frozen: Bool = false) { - switch change { - case .initial(let collection): - XCTAssertEqual(collection.isFrozen, frozen) - case .update(let collection, deletions: let del, insertions: let ins, - modifications: let modifications, _, _): - XCTAssertEqual(collection.isFrozen, frozen) - XCTAssertEqual(ins, insertions) - XCTAssertEqual(del, deletions) - XCTAssertEqual(modifications, []) - } - } - func testChangeSet() { var exp = XCTestExpectation(description: "initial") var calls = 0 let sectionedResults = collection.sectioned(by: \.key) cancellable = sectionedResults.changesetPublisher .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 exp.fulfill() } @@ -2367,7 +2368,7 @@ private class CombineSectionedResultsPublisherTests var sectionCalls = 10 cancellable = sectionedResults[0].changesetPublisher .sink { change in - self.checkChangeset(change, deletions: [IndexPath(item: sectionCalls, section: 0)]) + checkChangeset(change, deletions: [IndexPath(item: sectionCalls, section: 0)]) sectionCalls -= 1 sectionExp.fulfill() } @@ -2386,7 +2387,7 @@ private class CombineSectionedResultsPublisherTests .changesetPublisher .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 exp.fulfill() } @@ -2406,7 +2407,7 @@ private class CombineSectionedResultsPublisherTests .changesetPublisher .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, deletions: [IndexPath(item: sectionCalls, section: 0)]) + checkChangeset(change, deletions: [IndexPath(item: sectionCalls, section: 0)]) sectionCalls -= 1 sectionExp.fulfill() } @@ -2711,7 +2712,7 @@ private class CombineSectionedResultsPublisherTests .changesetPublisher .subscribe(on: subscribeOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 sema.signal() } @@ -2727,7 +2728,7 @@ private class CombineSectionedResultsPublisherTests .changesetPublisher .subscribe(on: subscribeOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) sectionCalls += 1 sema.signal() } @@ -2833,7 +2834,7 @@ private class CombineSectionedResultsPublisherTests .subscribe(on: subscribeOnQueue) .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 sema.signal() } @@ -2852,7 +2853,7 @@ private class CombineSectionedResultsPublisherTests .subscribe(on: subscribeOnQueue) .saveToken(on: self, at: \.notificationToken) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) sectionCalls += 1 sema.signal() } @@ -2872,7 +2873,7 @@ private class CombineSectionedResultsPublisherTests .changesetPublisher .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 exp.fulfill() } @@ -2890,7 +2891,7 @@ private class CombineSectionedResultsPublisherTests .changesetPublisher .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) sectionCalls += 1 sectionExp.fulfill() } @@ -2912,7 +2913,7 @@ private class CombineSectionedResultsPublisherTests .saveToken(on: self, at: \.notificationToken) .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 exp.fulfill() } @@ -2933,7 +2934,7 @@ private class CombineSectionedResultsPublisherTests .saveToken(on: self, at: \.notificationToken) .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) sectionCalls += 1 sectionExp.fulfill() } @@ -3004,7 +3005,7 @@ private class CombineSectionedResultsPublisherTests .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 exp.fulfill() } @@ -3024,7 +3025,7 @@ private class CombineSectionedResultsPublisherTests .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) sectionCalls += 1 sectionExp.fulfill() } @@ -3048,7 +3049,7 @@ private class CombineSectionedResultsPublisherTests .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: calls - 1, section: 0)]) calls += 1 exp.fulfill() } @@ -3071,7 +3072,7 @@ private class CombineSectionedResultsPublisherTests .threadSafeReference() .receive(on: receiveOnQueue) .sink { change in - self.checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) + checkChangeset(change, insertions: [IndexPath(item: sectionCalls - 1, section: 0)]) sectionCalls += 1 sectionExp.fulfill() } @@ -3148,7 +3149,7 @@ private class CombineSectionedResultsPublisherTests .sink { @Sendable arr in XCTAssertEqual(arr.count, 10) for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) } sema.signal() } @@ -3170,7 +3171,7 @@ private class CombineSectionedResultsPublisherTests .sink { @Sendable arr in XCTAssertEqual(arr.count, objectsCount) for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) } sema.signal() } @@ -3196,7 +3197,7 @@ private class CombineSectionedResultsPublisherTests .sink { @Sendable arr in XCTAssertEqual(arr.count, 10) for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) } exp.fulfill() } @@ -3217,7 +3218,7 @@ private class CombineSectionedResultsPublisherTests .sink { @Sendable arr in XCTAssertEqual(arr.count, objectsCount) for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) } sectionExp.fulfill() } @@ -3243,7 +3244,7 @@ private class CombineSectionedResultsPublisherTests .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) } sema.signal() } @@ -3265,7 +3266,7 @@ private class CombineSectionedResultsPublisherTests .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) } sema.signal() } @@ -3343,7 +3344,7 @@ private class CombineSectionedResultsPublisherTests .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: i - 1, section: 0)], frozen: true) } exp.fulfill() } @@ -3364,7 +3365,7 @@ private class CombineSectionedResultsPublisherTests .assertNoFailure() .sink { @Sendable arr in for (i, change) in arr.enumerated() { - self.checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) + checkChangeset(change, insertions: [IndexPath(item: (i + objectsCount) - 1, section: 0)], frozen: true) } sectionExp.fulfill() } @@ -3378,7 +3379,7 @@ private class CombineSectionedResultsPublisherTests @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) // swiftlint:disable:next type_name -class ResultsWithSectionedResultsPublisherTests: TestCase, @unchecked Sendable { +class ResultsWithSectionedResultsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineSectionedResultsPublisherTests>.testSuite("Results") } @@ -3386,7 +3387,7 @@ class ResultsWithSectionedResultsPublisherTests: TestCase, @unchecked Sendable { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) // swiftlint:disable:next type_name -class ManagedListSectionedResultsPublisherTests: TestCase, @unchecked Sendable { +class ManagedListSectionedResultsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineSectionedResultsPublisherTests>.testSuite("List") } @@ -3394,7 +3395,7 @@ class ManagedListSectionedResultsPublisherTests: TestCase, @unchecked Sendable { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) // swiftlint:disable:next type_name -class ManagedMutableSetSectionedResultsPublisherTests: TestCase, @unchecked Sendable { +class ManagedMutableSetSectionedResultsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineSectionedResultsPublisherTests>.testSuite("MutableSet") } @@ -3402,7 +3403,7 @@ class ManagedMutableSetSectionedResultsPublisherTests: TestCase, @unchecked Send @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) // swiftlint:disable:next type_name -class LinkingObjectsSectionedResultsPublisherTests: TestCase, @unchecked Sendable { +class LinkingObjectsSectionedResultsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineSectionedResultsPublisherTests>.testSuite("LinkingObjects") } @@ -3410,7 +3411,7 @@ class LinkingObjectsSectionedResultsPublisherTests: TestCase, @unchecked Sendabl @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) // swiftlint:disable:next type_name -class AnyRealmCollectionSectionedResultsPublisherTests: TestCase, @unchecked Sendable { +class AnyRealmCollectionSectionedResultsPublisherTests: TestCase { override class var defaultTestSuite: XCTestSuite { return CombineSectionedResultsPublisherTests>.testSuite("AnyRealmCollection") } @@ -3432,7 +3433,7 @@ public final class AltSimpleProjection: Projection, ObjectKeyIdent } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class CombineProjectionPublisherTests: CombinePublisherTestCase, @unchecked Sendable { +class CombineProjectionPublisherTests: CombinePublisherTestCase { var object: SimpleObject! var projection: SimpleProjection! @@ -4066,7 +4067,7 @@ class CombineProjectionPublisherTests: CombinePublisherTestCase, @unchecked Send } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -class CombineAsyncRealmTests: CombinePublisherTestCase, @unchecked Sendable { +class CombineAsyncRealmTests: CombinePublisherTestCase { @MainActor func testWillChangeLocalWrite() { let asyncWriteExpectation = expectation(description: "Should complete async write") diff --git a/RealmSwift/Tests/CompactionTests.swift b/RealmSwift/Tests/CompactionTests.swift index a01036809d..3a55a7b631 100644 --- a/RealmSwift/Tests/CompactionTests.swift +++ b/RealmSwift/Tests/CompactionTests.swift @@ -24,7 +24,7 @@ private func fileSize(path: String) -> Int { return attributes[.size] as! Int } -class CompactionTests: TestCase, @unchecked Sendable { +class CompactionTests: TestCase { func testSuccessfulCompactOnLaunch() { let expectedUsedBytesBeforeMin = 50000 let count = 1000 diff --git a/RealmSwift/Tests/CustomColumnNameTests.swift b/RealmSwift/Tests/CustomColumnNameTests.swift index feb231c9f0..f3c9f6898c 100644 --- a/RealmSwift/Tests/CustomColumnNameTests.swift +++ b/RealmSwift/Tests/CustomColumnNameTests.swift @@ -212,7 +212,7 @@ class EmbeddedCustomObject: EmbeddedObject { // MARK: - Schema Discovery -class CustomColumnNamesSchemaTest: TestCase, @unchecked Sendable { +class CustomColumnNamesSchemaTest: TestCase { func testCustomColumnNameSchema() { let modernCustomObjectSchema = ModernCustomObject().objectSchema for property in modernCustomObjectSchema.properties { @@ -375,7 +375,7 @@ class CustomColumnNamesSchemaTest: TestCase, @unchecked Sendable { } } -class CustomColumnModernDynamicObjectTest: TestCase, @unchecked Sendable { +class CustomColumnModernDynamicObjectTest: TestCase { var realm: Realm! override func setUp() { @@ -443,7 +443,7 @@ class CustomColumnModernDynamicObjectTest: TestCase, @unchecked Sendable { } } -class CustomColumnTestsBase: TestCase, @unchecked Sendable where O.Root == F.Root { +class CustomColumnTestsBase: TestCase where O.Root == F.Root { var realm: Realm! public var notificationTokens: [NotificationToken] = [] @@ -489,7 +489,7 @@ class CustomColumnTestsBase: CustomColumnTestsBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: Equatable { +class CustomColumnResultsTestBase: CustomColumnTestsBase where O.Root == F.Root, F.ValueType: Equatable { var results: Results! override func setUp() { @@ -503,7 +503,7 @@ class CustomColumnResultsTestBase: CustomColumnResultsTestBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: Equatable { +class CustomColumnResultsTest: CustomColumnResultsTestBase where O.Root == F.Root, F.ValueType: Equatable { // MARK: - Create Object func testCustomColumnResultsCreate() throws { @@ -640,7 +640,7 @@ class CustomColumnResultsTest: CustomColumnResultsTestBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: Equatable { +class CustomColumnResultsAggregatesTest: CustomColumnResultsTestBase where O.Root == F.Root, F.ValueType: Equatable { // MARK: - Aggregates func testCustomColumnResultsAggregateAvg() throws { @@ -698,7 +698,7 @@ class CustomColumnResultsAggregatesTest: CustomColumnResultsTestBase, @unchecked Sendable where O.Root == F.Root { +class CustomColumnResultsSectionedTest: CustomColumnResultsTestBase where O.Root == F.Root { // MARK: - Sectioned func testCustomColumnSectionedResults() throws { @@ -712,7 +712,7 @@ class CustomColumnResultsSectionedTest: CustomColumnTestsBase, @unchecked Sendable where O: ObjectBase, O.Root == F.Root, F.ValueType: Equatable { +class CustomColumnObjectTest: CustomColumnTestsBase where O: ObjectBase, O.Root == F.Root, F.ValueType: Equatable { // MARK: - Subscript func testCustomColumnObjectKVC() throws { @@ -764,7 +764,7 @@ class CustomColumnObjectTest: CustomColumnTestsBase, @unchecked Sendable where O: ObjectBase, O.Root == F.Root, F.ValueType: RealmKeyedCollection { +class CustomColumnKeyedObjectTest: CustomColumnTestsBase where O: ObjectBase, O.Root == F.Root, F.ValueType: RealmKeyedCollection { func testCustomColumnObjectDynamicMap() throws { func testCustomColumnObjectDynamicMap() throws { for (keyPath, count) in F.dynamicMutableSetProperty { @@ -775,7 +775,7 @@ class CustomColumnKeyedObjectTest: CustomColumnTestsBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: RealmCollectionValue { +class CustomColumnListTest: CustomColumnTestsBase where O.Root == F.Root, F.ValueType: RealmCollectionValue { var list: List! override func setUp() { @@ -818,7 +818,7 @@ class CustomColumnListTest: CustomColumnTestsBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: RealmCollectionValue { +class CustomColumnSetTest: CustomColumnTestsBase where O.Root == F.Root, F.ValueType: RealmCollectionValue { var set: MutableSet! override func setUp() { @@ -856,7 +856,7 @@ class CustomColumnSetTest: CustomColumnTestsBase, @unchecked Sendable where O.Root == F.Root { +class CustomColumnMapTestBase: CustomColumnTestsBase where O.Root == F.Root { var map: Map! override func setUp() { @@ -881,7 +881,7 @@ class CustomColumnMapTestBase: CustomColumnMapTestBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: Equatable { +class CustomColumnMapTest: CustomColumnMapTestBase where O.Root == F.Root, F.ValueType: Equatable { // MARK: - ValueForKey func testCustomColumnMapGetValueForKey() throws { @@ -940,7 +940,7 @@ class CustomColumnMapTest: CustomColumnMapTestBase, @unchecked Sendable where O.Root == F.Root, F.ValueType: RealmCollectionValue { +class CustomColumnAggregatesMapTest: CustomColumnMapTestBase where O.Root == F.Root, F.ValueType: RealmCollectionValue { // MARK: - Aggregates func testCustomColumnResultsAggregateAvg() throws { @@ -998,7 +998,7 @@ class CustomColumnAggregatesMapTest [EmbeddedObjectWrapper] { return objectValues().map(EmbeddedObjectWrapper.init) } -class CustomObjectCreationTests: TestCase, @unchecked Sendable { +class CustomObjectCreationTests: TestCase { var rawValues: [String: Any]! var wrappedValues: [String: Any]! var nilOptionalValues: [String: Any]! diff --git a/RealmSwift/Tests/Decimal128Tests.swift b/RealmSwift/Tests/Decimal128Tests.swift index 1cf41ceeb4..e63ce4982d 100644 --- a/RealmSwift/Tests/Decimal128Tests.swift +++ b/RealmSwift/Tests/Decimal128Tests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class Decimal128Tests: TestCase, @unchecked Sendable { +class Decimal128Tests: TestCase { // MARK: - Initialization func testDecimal128Initialization() { diff --git a/RealmSwift/Tests/GeospatialTests.swift b/RealmSwift/Tests/GeospatialTests.swift index 11c3fa0638..f3be559059 100644 --- a/RealmSwift/Tests/GeospatialTests.swift +++ b/RealmSwift/Tests/GeospatialTests.swift @@ -64,7 +64,7 @@ class PersonLocation: Object { } } -class GeospatialTests: TestCase, @unchecked Sendable { +class GeospatialTests: TestCase { func populatePersonLocationTable() throws { let realm = realmWithTestPath() try realm.write { diff --git a/RealmSwift/Tests/KVOTests.swift b/RealmSwift/Tests/KVOTests.swift index 7900a1a43c..7b4c1eb5de 100644 --- a/RealmSwift/Tests/KVOTests.swift +++ b/RealmSwift/Tests/KVOTests.swift @@ -142,7 +142,7 @@ class SwiftKVOObject: Object { // Most of the testing of KVO functionality is done in the obj-c tests // These tests just verify that it also works on Swift types @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class KVOTests: TestCase, @unchecked Sendable { +class KVOTests: TestCase { var realm: Realm! = nil override func setUp() { @@ -559,7 +559,7 @@ class KVOTests: TestCase, @unchecked Sendable { } @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class KVOPersistedTests: KVOTests, @unchecked Sendable { +class KVOPersistedTests: KVOTests { override func getObject(_ obj: SwiftKVOObject) -> (SwiftKVOObject, SwiftKVOObject) { realm.add(obj) return (obj, obj) @@ -567,7 +567,7 @@ class KVOPersistedTests: KVOTests, @unchecked Sendable { } @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class KVOMultipleAccessorsTests: KVOTests, @unchecked Sendable { +class KVOMultipleAccessorsTests: KVOTests { override func getObject(_ obj: SwiftKVOObject) -> (SwiftKVOObject, SwiftKVOObject) { realm.add(obj) return (obj, realm.object(ofType: SwiftKVOObject.self, forPrimaryKey: obj.pk)!) diff --git a/RealmSwift/Tests/KeyPathTests.swift b/RealmSwift/Tests/KeyPathTests.swift index c6cf112c80..57c8a0f08e 100644 --- a/RealmSwift/Tests/KeyPathTests.swift +++ b/RealmSwift/Tests/KeyPathTests.swift @@ -20,7 +20,7 @@ import XCTest import RealmSwift import Foundation -class KeyPathTests: TestCase, @unchecked Sendable { +class KeyPathTests: TestCase { func testModernObjectTopLevel() { XCTAssertEqual(_name(for: \ModernAllTypesObject.pk), "pk") diff --git a/RealmSwift/Tests/ListTests.swift b/RealmSwift/Tests/ListTests.swift index 617e4e2a38..e15bf7eb03 100644 --- a/RealmSwift/Tests/ListTests.swift +++ b/RealmSwift/Tests/ListTests.swift @@ -23,7 +23,7 @@ import RealmSwift import RealmSwiftTestSupport #endif -class ListTests: TestCase, @unchecked Sendable { +class ListTests: TestCase { var str1: SwiftStringObject? var str2: SwiftStringObject? var arrayObject: SwiftArrayPropertyObject! @@ -661,7 +661,7 @@ class ListTests: TestCase, @unchecked Sendable { } } -class ListStandaloneTests: ListTests, @unchecked Sendable { +class ListStandaloneTests: ListTests { override func createArray() -> SwiftArrayPropertyObject { let array = SwiftArrayPropertyObject() XCTAssertNil(array.realm) @@ -679,7 +679,7 @@ class ListStandaloneTests: ListTests, @unchecked Sendable { } } -class ListNewlyAddedTests: ListTests, @unchecked Sendable { +class ListNewlyAddedTests: ListTests { override func createArray() -> SwiftArrayPropertyObject { let array = SwiftArrayPropertyObject() array.name = "name" @@ -708,7 +708,7 @@ class ListNewlyAddedTests: ListTests, @unchecked Sendable { } } -class ListNewlyCreatedTests: ListTests, @unchecked Sendable { +class ListNewlyCreatedTests: ListTests { override func createArray() -> SwiftArrayPropertyObject { let realm = realmWithTestPath() realm.beginWrite() @@ -737,7 +737,7 @@ class ListNewlyCreatedTests: ListTests, @unchecked Sendable { } } -class ListRetrievedTests: ListTests, @unchecked Sendable { +class ListRetrievedTests: ListTests { override func createArray() -> SwiftArrayPropertyObject { let realm = realmWithTestPath() realm.beginWrite() @@ -770,7 +770,7 @@ class ListRetrievedTests: ListTests, @unchecked Sendable { } /// Ensure the range replaceable collection methods behave correctly when emulated for Swift 4 and later. -class ListRRCMethodsTests: TestCase, @unchecked Sendable { +class ListRRCMethodsTests: TestCase { private func compare(array: [Int], with list: List) { guard array.count == list.count else { XCTFail("Array and list have different sizes (\(array.count) and \(list.count), respectively).") diff --git a/RealmSwift/Tests/MapTests.swift b/RealmSwift/Tests/MapTests.swift index 60d2325220..b808a58ba4 100644 --- a/RealmSwift/Tests/MapTests.swift +++ b/RealmSwift/Tests/MapTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class MapTests: TestCase, @unchecked Sendable { +class MapTests: TestCase { var str1: SwiftStringObject! var str2: SwiftStringObject! var realm: Realm! @@ -806,8 +806,8 @@ class MapTests: TestCase, @unchecked Sendable { /* Expect notification on "intCol" key path when intCol is changed */ ex = expectation(description: "change notification") - dispatchSyncNewThread { - let realm = self.realmWithTestPath() + dispatchSyncBackground { unsafeSelf in + let realm = unsafeSelf.realmWithTestPath() realm.beginWrite() let obj = realm.objects(SwiftMapPropertyObject.self).first! let value = obj.swiftObjectMap["first"]!! @@ -842,8 +842,8 @@ class MapTests: TestCase, @unchecked Sendable { /* Expect no notification on "intCol" key path when stringCol is changed */ ex = expectation(description: "NO change notification") ex.isInverted = true - dispatchSyncNewThread { - let realm = self.realmWithTestPath() + dispatchSyncBackground { unsafeSelf in + let realm = unsafeSelf.realmWithTestPath() realm.beginWrite() let obj = realm.objects(SwiftMapPropertyObject.self).first! let value = obj.swiftObjectMap["first"]!! @@ -880,8 +880,8 @@ class MapTests: TestCase, @unchecked Sendable { /* Expect notification on "intCol" key path when intCol is changed */ ex = expectation(description: "change notification") - dispatchSyncNewThread { - let realm = self.realmWithTestPath() + dispatchSyncBackground { unsafeSelf in + let realm = unsafeSelf.realmWithTestPath() realm.beginWrite() let obj = realm.objects(SwiftMapPropertyObject.self).first! obj.swiftObjectMap.removeObject(for: "first") @@ -916,8 +916,8 @@ class MapTests: TestCase, @unchecked Sendable { wait(for: [ex], timeout: 0.1) ex = expectation(description: "change notification") - dispatchSyncNewThread { - let realm = self.realmWithTestPath() + dispatchSyncBackground { unsafeSelf in + let realm = unsafeSelf.realmWithTestPath() realm.beginWrite() let obj = realm.objects(SwiftMapPropertyObject.self).first! let value = obj.swiftObjectMap["first"]!! @@ -955,8 +955,8 @@ class MapTests: TestCase, @unchecked Sendable { wait(for: [ex], timeout: 0.1) ex = expectation(description: "change notification") - dispatchSyncNewThread { - let realm = self.realmWithTestPath() + dispatchSyncBackground { unsafeSelf in + let realm = unsafeSelf.realmWithTestPath() realm.beginWrite() let obj = realm.objects(SwiftOwnerObject.self).first! obj.name = "Curley" @@ -1029,7 +1029,7 @@ class MapTests: TestCase, @unchecked Sendable { } } -class MapStandaloneTests: MapTests, @unchecked Sendable { +class MapStandaloneTests: MapTests { override func createMap() -> Map { return createMapObject().map } @@ -1086,7 +1086,7 @@ class MapStandaloneTests: MapTests, @unchecked Sendable { } } -class MapNewlyAddedTests: MapTests, @unchecked Sendable { +class MapNewlyAddedTests: MapTests { override func createMap() -> Map { let mapObj = SwiftMapPropertyObject() realm.add(mapObj) @@ -1109,7 +1109,7 @@ class MapNewlyAddedTests: MapTests, @unchecked Sendable { } } -class MapNewlyCreatedTests: MapTests, @unchecked Sendable { +class MapNewlyCreatedTests: MapTests { override func createMap() -> Map { let mapObj = realm.create(SwiftMapPropertyObject.self, value: ["name"]) try! realm.commitWrite() @@ -1131,7 +1131,7 @@ class MapNewlyCreatedTests: MapTests, @unchecked Sendable { } } -class MapRetrievedTests: MapTests, @unchecked Sendable { +class MapRetrievedTests: MapTests { override func createMap() -> Map { realm.create(SwiftMapPropertyObject.self, value: ["name"]) try! realm.commitWrite() diff --git a/RealmSwift/Tests/MigrationTests.swift b/RealmSwift/Tests/MigrationTests.swift index 1f0a02fb8e..71a9d6df2c 100644 --- a/RealmSwift/Tests/MigrationTests.swift +++ b/RealmSwift/Tests/MigrationTests.swift @@ -44,7 +44,7 @@ private func dynamicRealm(_ fileURL: URL) -> RLMRealm { return try! RLMRealm(configuration: config) } -class MigrationTests: TestCase, @unchecked Sendable { +class MigrationTests: TestCase { private func createDefaultRealm() throws { let config = Realm.Configuration(fileURL: defaultRealmURL()) try autoreleasepool { @@ -173,6 +173,7 @@ class MigrationTests: TestCase, @unchecked Sendable { } } + nonisolated(unsafe) let unsafeSelf = self try testMigration(schemaVersion: 2) { migration, _ in var count = 0 migration.enumerateObjects(ofType: "SwiftStringObject", { oldObj, newObj in @@ -184,8 +185,8 @@ class MigrationTests: TestCase, @unchecked Sendable { XCTAssertEqual(oldObj.objectSchema.className, "SwiftStringObject") XCTAssertEqual((newObj["stringCol"] as! String), "string") XCTAssertEqual((oldObj["stringCol"] as! String), "string") - self.assertThrows(oldObj["noSuchCol"] as! String) - self.assertThrows(newObj["noSuchCol"] as! String) + unsafeSelf.assertThrows(oldObj["noSuchCol"] as! String) + unsafeSelf.assertThrows(newObj["noSuchCol"] as! String) count += 1 }) XCTAssertEqual(count, 1) @@ -712,6 +713,7 @@ class MigrationTests: TestCase, @unchecked Sendable { func testCreate() throws { try createDefaultRealm() + nonisolated(unsafe) let unsafeSelf = self try testMigration { migration, _ in migration.create("SwiftStringObject", value: ["string1"]) migration.create("SwiftStringObject", value: ["stringCol": "string2"]) @@ -719,7 +721,7 @@ class MigrationTests: TestCase, @unchecked Sendable { migration.create("SwiftStringObject", value: ["stringCol": StringWrapper(persistedValue: "string3")]) migration.create("SwiftStringObject") - self.assertThrows(migration.create("NoSuchObject")) + unsafeSelf.assertThrows(migration.create("NoSuchObject")) } validation: { realm, _ in let objects = realm.objects(SwiftStringObject.self) XCTAssertEqual(objects.count, 5) @@ -823,6 +825,7 @@ class MigrationTests: TestCase, @unchecked Sendable { } } + nonisolated(unsafe) let unsafeSelf = self try testMigration { migration, _ in var enumerated = false migration.enumerateObjects(ofType: "SwiftObject", { oldObj, newObj in @@ -958,8 +961,8 @@ class MigrationTests: TestCase, @unchecked Sendable { XCTAssertEqual(map.count, 2) XCTAssertEqual((map["key"]?!["boolCol"] as! Bool), true) - self.assertThrows(newObj.value(forKey: "noSuchKey")) - self.assertThrows(newObj.setValue(1, forKey: "noSuchKey")) + unsafeSelf.assertThrows(newObj.value(forKey: "noSuchKey")) + unsafeSelf.assertThrows(newObj.setValue(1, forKey: "noSuchKey")) // set it again newObj["arrayCol"] = [falseObj, trueObj] @@ -1022,7 +1025,7 @@ class MigrationTests: TestCase, @unchecked Sendable { \\); \\} """ - self.assertMatches(newObj.description, expected.replacingOccurrences(of: " ", with: "\t")) + unsafeSelf.assertMatches(newObj.description, expected.replacingOccurrences(of: " ", with: "\t")) enumerated = true }) @@ -1062,7 +1065,7 @@ class MigrationTests: TestCase, @unchecked Sendable { \\); \\} """ - self.assertMatches(newObj.description, expected.replacingOccurrences(of: " ", with: "\t")) + unsafeSelf.assertMatches(newObj.description, expected.replacingOccurrences(of: " ", with: "\t")) } validation: { realm, _ in let object = realm.objects(SwiftObject.self).first! XCTAssertEqual(object.boolCol, false) diff --git a/RealmSwift/Tests/MixedCollectionTest.swift b/RealmSwift/Tests/MixedCollectionTest.swift index 6c4367c022..a3199cc1d0 100644 --- a/RealmSwift/Tests/MixedCollectionTest.swift +++ b/RealmSwift/Tests/MixedCollectionTest.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class MixedCollectionTest: TestCase, @unchecked Sendable { +class MixedCollectionTest: TestCase { func testAnyMixedDictionary() throws { let so = SwiftStringObject() so.stringCol = "hello" diff --git a/RealmSwift/Tests/ModernKVOTests.swift b/RealmSwift/Tests/ModernKVOTests.swift index 06b152f873..0d8ca9afc6 100644 --- a/RealmSwift/Tests/ModernKVOTests.swift +++ b/RealmSwift/Tests/ModernKVOTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class ModernKVOTests: TestCase, @unchecked Sendable { +class ModernKVOTests: TestCase { var realm: Realm! = nil override func setUp() { @@ -390,14 +390,14 @@ class ModernKVOTests: TestCase, @unchecked Sendable { } } -class ModernKVOPersistedTests: ModernKVOTests, @unchecked Sendable { +class ModernKVOPersistedTests: ModernKVOTests { override func getObject(_ obj: ModernAllTypesObject) -> (ModernAllTypesObject, ModernAllTypesObject) { realm.add(obj) return (obj, obj) } } -class ModernKVOMultipleAccessorsTests: ModernKVOTests, @unchecked Sendable { +class ModernKVOMultipleAccessorsTests: ModernKVOTests { override func getObject(_ obj: ModernAllTypesObject) -> (ModernAllTypesObject, ModernAllTypesObject) { realm.add(obj) return (obj, realm.object(ofType: ModernAllTypesObject.self, forPrimaryKey: obj.pk)!) diff --git a/RealmSwift/Tests/ModernObjectAccessorTests.swift b/RealmSwift/Tests/ModernObjectAccessorTests.swift index cbc3257958..162b2268b1 100644 --- a/RealmSwift/Tests/ModernObjectAccessorTests.swift +++ b/RealmSwift/Tests/ModernObjectAccessorTests.swift @@ -21,7 +21,7 @@ import Realm.Private import RealmSwift import Foundation -class ModernObjectAccessorTests: TestCase, @unchecked Sendable { +class ModernObjectAccessorTests: TestCase { let data = "b".data(using: .utf8, allowLossyConversion: false)! let date = Date(timeIntervalSinceReferenceDate: 2) let oid1 = ObjectId("1234567890ab1234567890ab") @@ -680,11 +680,11 @@ class ModernObjectAccessorTests: TestCase, @unchecked Sendable { obj.arrayInt.removeAll() obj.arrayInt8.removeAll() } - dispatchSyncNewThread { - self.assertThrows(_ = obj.intCol, reason: "incorrect thread") - self.assertThrows(obj.arrayInt.removeAll(), reason: "incorrect thread") - self.assertThrows(obj.int8Col = 5, reason: "incorrect thread") - self.assertThrows(obj.arrayInt8 = List(), reason: "incorrect thread") + dispatchSyncBackground { unsafeSelf in + unsafeSelf.assertThrows(_ = obj.intCol, reason: "incorrect thread") + unsafeSelf.assertThrows(obj.arrayInt.removeAll(), reason: "incorrect thread") + unsafeSelf.assertThrows(obj.int8Col = 5, reason: "incorrect thread") + unsafeSelf.assertThrows(obj.arrayInt8 = List(), reason: "incorrect thread") } } diff --git a/RealmSwift/Tests/ModernObjectCreationTests.swift b/RealmSwift/Tests/ModernObjectCreationTests.swift index 01ff9d00f3..a4884c1131 100644 --- a/RealmSwift/Tests/ModernObjectCreationTests.swift +++ b/RealmSwift/Tests/ModernObjectCreationTests.swift @@ -20,7 +20,7 @@ import XCTest import RealmSwift import Realm.Private -class ModernObjectCreationTests: TestCase, @unchecked Sendable { +class ModernObjectCreationTests: TestCase { var values: [String: Any]! override func setUp() { values = [ @@ -937,7 +937,7 @@ private func mapValues(_ values: [T]) -> [String: T] { return map } -class ModernEnumObjectCreationTests: TestCase, @unchecked Sendable { +class ModernEnumObjectCreationTests: TestCase { let values: [String: Any] = [ "listInt": EnumInt.values(), "listInt8": EnumInt8.values(), diff --git a/RealmSwift/Tests/ModernObjectTests.swift b/RealmSwift/Tests/ModernObjectTests.swift index f4d60767c2..d1da788b16 100644 --- a/RealmSwift/Tests/ModernObjectTests.swift +++ b/RealmSwift/Tests/ModernObjectTests.swift @@ -34,7 +34,7 @@ class ModernDynamicDefaultObject: Object { @Persisted var binaryCol = UUID().uuidString.data(using: .utf8) } -class ModernObjectTests: TestCase, @unchecked Sendable { +class ModernObjectTests: TestCase { // init() Tests are in ObjectCreationTests.swift // init(value:) tests are in ObjectCreationTests.swift diff --git a/RealmSwift/Tests/MutableSetTests.swift b/RealmSwift/Tests/MutableSetTests.swift index e2f582deaa..03845ce3ee 100644 --- a/RealmSwift/Tests/MutableSetTests.swift +++ b/RealmSwift/Tests/MutableSetTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class MutableSetTests: TestCase, @unchecked Sendable { +class MutableSetTests: TestCase { var str1: SwiftStringObject? var str2: SwiftStringObject? var str3: SwiftStringObject? @@ -485,7 +485,7 @@ class MutableSetTests: TestCase, @unchecked Sendable { } } -class MutableSetStandaloneTests: MutableSetTests, @unchecked Sendable { +class MutableSetStandaloneTests: MutableSetTests { override func createSet() -> SwiftMutableSetPropertyObject { let set = SwiftMutableSetPropertyObject() XCTAssertNil(set.realm) @@ -499,7 +499,7 @@ class MutableSetStandaloneTests: MutableSetTests, @unchecked Sendable { } } -class MutableSetNewlyAddedTests: MutableSetTests, @unchecked Sendable { +class MutableSetNewlyAddedTests: MutableSetTests { override func createSet() -> SwiftMutableSetPropertyObject { let set = SwiftMutableSetPropertyObject() set.name = "name" @@ -520,7 +520,7 @@ class MutableSetNewlyAddedTests: MutableSetTests, @unchecked Sendable { } } -class MutableSetNewlyCreatedTests: MutableSetTests, @unchecked Sendable { +class MutableSetNewlyCreatedTests: MutableSetTests { override func createSet() -> SwiftMutableSetPropertyObject { let realm = realmWithTestPath() realm.beginWrite() @@ -542,7 +542,7 @@ class MutableSetNewlyCreatedTests: MutableSetTests, @unchecked Sendable { } } -class MutableSetRetrievedTests: MutableSetTests, @unchecked Sendable { +class MutableSetRetrievedTests: MutableSetTests { override func createSet() -> SwiftMutableSetPropertyObject { let realm = realmWithTestPath() realm.beginWrite() diff --git a/RealmSwift/Tests/ObjectAccessorTests.swift b/RealmSwift/Tests/ObjectAccessorTests.swift index a2235579f1..bd9e0b3de0 100644 --- a/RealmSwift/Tests/ObjectAccessorTests.swift +++ b/RealmSwift/Tests/ObjectAccessorTests.swift @@ -22,7 +22,7 @@ import RealmSwift import Foundation @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class ObjectAccessorTests: TestCase, @unchecked Sendable { +class ObjectAccessorTests: TestCase { func setAndTestAllPropertiesViaNormalAccess(_ object: SwiftObject, _ optObject: SwiftOptionalObject) { object.boolCol = true XCTAssertEqual(object.boolCol, true) diff --git a/RealmSwift/Tests/ObjectCreationTests.swift b/RealmSwift/Tests/ObjectCreationTests.swift index d29770de04..c2759399f6 100644 --- a/RealmSwift/Tests/ObjectCreationTests.swift +++ b/RealmSwift/Tests/ObjectCreationTests.swift @@ -20,6 +20,10 @@ import XCTest import RealmSwift import Realm.Private +#if canImport(RealmTestSupport) +import RealmSwiftTestSupport +#endif + class ObjectWithPrivateOptionals: Object { private var nilInt: Int? private var nilFloat: Float? @@ -32,7 +36,7 @@ class ObjectWithPrivateOptionals: Object { } @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class ObjectCreationTests: TestCase, @unchecked Sendable { +class ObjectCreationTests: TestCase { // MARK: - Init tests func testInitWithDefaults() { diff --git a/RealmSwift/Tests/ObjectCustomPropertiesTests.swift b/RealmSwift/Tests/ObjectCustomPropertiesTests.swift index bf1e7c8581..ff0d5d5a99 100644 --- a/RealmSwift/Tests/ObjectCustomPropertiesTests.swift +++ b/RealmSwift/Tests/ObjectCustomPropertiesTests.swift @@ -20,7 +20,7 @@ import XCTest import Realm @_spi(RealmSwiftPrivate) import RealmSwift -final class ObjectCustomPropertiesTests: TestCase, @unchecked Sendable { +final class ObjectCustomPropertiesTests: TestCase { override func tearDown() { super.tearDown() CustomPropertiesObject.injected_customRealmProperties = nil diff --git a/RealmSwift/Tests/ObjectIdTests.swift b/RealmSwift/Tests/ObjectIdTests.swift index cb9932b568..68b63e8435 100644 --- a/RealmSwift/Tests/ObjectIdTests.swift +++ b/RealmSwift/Tests/ObjectIdTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class ObjectIdTests: TestCase, @unchecked Sendable { +class ObjectIdTests: TestCase { func testObjectIdInitialization() { let strValue = "000123450000ffbeef91906c" diff --git a/RealmSwift/Tests/ObjectSchemaInitializationTests.swift b/RealmSwift/Tests/ObjectSchemaInitializationTests.swift index 745f49ce45..b57da6f1b9 100644 --- a/RealmSwift/Tests/ObjectSchemaInitializationTests.swift +++ b/RealmSwift/Tests/ObjectSchemaInitializationTests.swift @@ -28,7 +28,7 @@ import Foundation #endif @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class ObjectSchemaInitializationTests: TestCase, @unchecked Sendable { +class ObjectSchemaInitializationTests: TestCase { func testAllValidTypes() { let object = SwiftObject() let objectSchema = object.objectSchema @@ -806,7 +806,7 @@ class SwiftObjectWithNonOptionalLinkProperty: SwiftFakeObject { @objc dynamic var objectCol = SwiftBoolObject() } -#if compiler(<6) || SWIFT_PACKAGE +#if SWIFT_PACKAGE extension Set: RealmOptionalType { public static func _rlmFromObjc(_ value: Any, insideOptional: Bool) -> Set? { fatalError() diff --git a/RealmSwift/Tests/ObjectSchemaTests.swift b/RealmSwift/Tests/ObjectSchemaTests.swift index 64d4ae0024..6611e7ae19 100644 --- a/RealmSwift/Tests/ObjectSchemaTests.swift +++ b/RealmSwift/Tests/ObjectSchemaTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class ObjectSchemaTests: TestCase, @unchecked Sendable { +class ObjectSchemaTests: TestCase { var objectSchema: ObjectSchema! var swiftObjectSchema: ObjectSchema { diff --git a/RealmSwift/Tests/ObjectTests.swift b/RealmSwift/Tests/ObjectTests.swift index 6646a0047d..cd25510935 100644 --- a/RealmSwift/Tests/ObjectTests.swift +++ b/RealmSwift/Tests/ObjectTests.swift @@ -45,7 +45,7 @@ class SwiftDynamicDefaultObject: Object { } @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class ObjectTests: TestCase, @unchecked Sendable { +class ObjectTests: TestCase { // init() Tests are in ObjectCreationTests.swift // init(value:) tests are in ObjectCreationTests.swift @@ -599,9 +599,9 @@ class ObjectTests: TestCase, @unchecked Sendable { XCTAssertEqual((getter(object, "setCol") as! MutableSet).count, 0) } - func dynamicSetAndTestAllTypes(_ setter: (DynamicObject, Any?, String) -> Void, - getter: (DynamicObject, String) -> (Any?), object: DynamicObject, - boolObject: DynamicObject) { + static func dynamicSetAndTestAllTypes(_ setter: (DynamicObject, Any?, String) -> Void, + getter: (DynamicObject, String) -> (Any?), object: DynamicObject, + boolObject: DynamicObject) { setter(object, true, "boolCol") XCTAssertEqual((getter(object, "boolCol") as! Bool), true) @@ -718,7 +718,7 @@ class ObjectTests: TestCase, @unchecked Sendable { withMigrationObject { migrationObject, migration in let boolObject = migration.create("SwiftBoolObject", value: [true]) - self.dynamicSetAndTestAllTypes(setter, getter: getter, object: migrationObject, boolObject: boolObject) + Self.dynamicSetAndTestAllTypes(setter, getter: getter, object: migrationObject, boolObject: boolObject) } setAndTestAllTypes(setter, getter: getter, object: SwiftObject()) @@ -738,7 +738,7 @@ class ObjectTests: TestCase, @unchecked Sendable { withMigrationObject { migrationObject, migration in let boolObject = migration.create("SwiftBoolObject", value: [true]) - self.dynamicSetAndTestAllTypes(setter, getter: getter, object: migrationObject, boolObject: boolObject) + Self.dynamicSetAndTestAllTypes(setter, getter: getter, object: migrationObject, boolObject: boolObject) } setAndTestAllTypes(setter, getter: getter, object: SwiftObject()) @@ -752,9 +752,9 @@ class ObjectTests: TestCase, @unchecked Sendable { withMigrationObject { migrationObject, migration in let boolObject = migration.create("SwiftBoolObject", value: [true]) migrationObject.anyCol = boolObject - self.assertEqual(migrationObject.anyCol as? DynamicObject, boolObject) + assertEqual(migrationObject.anyCol as? DynamicObject, boolObject) migrationObject.objectCol = boolObject - self.assertEqual(migrationObject.objectCol as? DynamicObject, boolObject) + assertEqual(migrationObject.objectCol as? DynamicObject, boolObject) migrationObject.anyCol = 12345 XCTAssertEqual(migrationObject.anyCol as! Int, 12345) } diff --git a/RealmSwift/Tests/ObjectiveCSupportTests.swift b/RealmSwift/Tests/ObjectiveCSupportTests.swift index 523a8ff630..f54ff057d2 100644 --- a/RealmSwift/Tests/ObjectiveCSupportTests.swift +++ b/RealmSwift/Tests/ObjectiveCSupportTests.swift @@ -21,7 +21,7 @@ import Realm import RealmSwift import XCTest -class ObjectiveCSupportTests: TestCase, @unchecked Sendable { +class ObjectiveCSupportTests: TestCase { func testSupport() { diff --git a/RealmSwift/Tests/PerformanceTests.swift b/RealmSwift/Tests/PerformanceTests.swift index 66e5887853..201ac5b885 100644 --- a/RealmSwift/Tests/PerformanceTests.swift +++ b/RealmSwift/Tests/PerformanceTests.swift @@ -45,7 +45,7 @@ private nonisolated(unsafe) var mediumRealm: Realm! private nonisolated(unsafe) var largeRealm: Realm! @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class SwiftPerformanceTests: TestCase, @unchecked Sendable { +class SwiftPerformanceTests: TestCase { override class var defaultTestSuite: XCTestSuite { #if !DEBUG && os(iOS) && !targetEnvironment(macCatalyst) && !targetEnvironment(simulator) return super.defaultTestSuite @@ -938,7 +938,7 @@ class SwiftPerformanceTests: TestCase, @unchecked Sendable { } } -class SwiftSyncRealmPerformanceTests: TestCase, @unchecked Sendable { +class SwiftSyncRealmPerformanceTests: TestCase { override class var defaultTestSuite: XCTestSuite { #if !DEBUG && os(iOS) && !targetEnvironment(macCatalyst) && !targetEnvironment(simulator) return super.defaultTestSuite @@ -1043,7 +1043,7 @@ class SwiftSyncRealmPerformanceTests: TestCase, @unchecked Sendable { } } -class SwiftFlexibleSyncRealmPerformanceTests: SwiftSyncRealmPerformanceTests, @unchecked Sendable { +class SwiftFlexibleSyncRealmPerformanceTests: SwiftSyncRealmPerformanceTests { override var config: Realm.Configuration { var config = ObjectiveCSupport.convert(object: RLMRealmConfiguration.fakeFlexibleSync()) config.objectTypes = [] diff --git a/RealmSwift/Tests/PrimitiveListTests.swift b/RealmSwift/Tests/PrimitiveListTests.swift index 43a8b4e2ca..1ba6ea96b8 100644 --- a/RealmSwift/Tests/PrimitiveListTests.swift +++ b/RealmSwift/Tests/PrimitiveListTests.swift @@ -21,6 +21,7 @@ import XCTest #if canImport(RealmTestSupport) import RealmTestSupport +import RealmSwiftTestSupport #endif class PrimitiveListTestsBase: RLMTestCaseBase { @@ -499,7 +500,7 @@ func addTests(_ suite: XCTestSuite, _ type: OF.Type) { AddablePrimitiveListTests.defaultTestSuite.tests.forEach(suite.addTest) } -class UnmanagedPrimitiveListTests: TestCase, @unchecked Sendable { +class UnmanagedPrimitiveListTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Unmanaged Primitive Lists") addTests(suite, UnmanagedObjectFactory.self) @@ -507,7 +508,7 @@ class UnmanagedPrimitiveListTests: TestCase, @unchecked Sendable { } } -class ManagedPrimitiveListTests: TestCase, @unchecked Sendable { +class ManagedPrimitiveListTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Managed Primitive Lists") addTests(suite, ManagedObjectFactory.self) diff --git a/RealmSwift/Tests/PrimitiveMapTests.swift b/RealmSwift/Tests/PrimitiveMapTests.swift index e6f6b77144..7dfa5c877c 100644 --- a/RealmSwift/Tests/PrimitiveMapTests.swift +++ b/RealmSwift/Tests/PrimitiveMapTests.swift @@ -20,9 +20,13 @@ import XCTest import Realm import RealmSwift +#if canImport(RealmSwiftTestSupport) +import RealmSwiftTestSupport +#endif + // swiftlint:disable cyclomatic_complexity -class PrimitiveMapTestsBase: TestCase, @unchecked Sendable { +class PrimitiveMapTestsBase: TestCase { var realm: Realm? var obj: V.MapRoot! var obj2: V.MapRoot! @@ -49,7 +53,7 @@ class PrimitiveMapTestsBase: TestCase, @un } } -class PrimitiveMapTests: PrimitiveMapTestsBase, @unchecked Sendable { +class PrimitiveMapTests: PrimitiveMapTestsBase { func testInvalidated() { XCTAssertFalse(map.isInvalidated) if let realm = obj.realm { @@ -180,7 +184,7 @@ class PrimitiveMapTests: PrimitiveMapTests } } -class MinMaxPrimitiveMapTests: PrimitiveMapTestsBase, @unchecked Sendable where V.PersistedType: MinMaxType { +class MinMaxPrimitiveMapTests: PrimitiveMapTestsBase where V.PersistedType: MinMaxType { func testMin() { XCTAssertNil(map.min()) map.merge(values) { $1 } @@ -195,7 +199,7 @@ class MinMaxPrimitiveMapTests: PrimitiveMa } } -class AddablePrimitiveMapTests: PrimitiveMapTestsBase, @unchecked Sendable where V: NumericValueFactory, V.PersistedType: AddableType { +class AddablePrimitiveMapTests: PrimitiveMapTestsBase where V: NumericValueFactory, V.PersistedType: AddableType { func testSum() { XCTAssertEqual(map.sum(), .zero) map.merge(values) { $1 } @@ -209,7 +213,7 @@ class AddablePrimitiveMapTests: PrimitiveM } } -class SortablePrimitiveMapTests: PrimitiveMapTestsBase, @unchecked Sendable where V.PersistedType: SortableType { +class SortablePrimitiveMapTests: PrimitiveMapTestsBase where V.PersistedType: SortableType { func testSorted() { map.merge(values) { $1 } XCTAssertEqual(map.count, 3) @@ -325,7 +329,7 @@ func addPrimitiveMapTests(_ suite: XCTestSuite, _ type: OF.Ty MinMaxPrimitiveMapTests.defaultTestSuite.tests.forEach(suite.addTest) } -class UnmanagedPrimitiveMapTests: TestCase, @unchecked Sendable { +class UnmanagedPrimitiveMapTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Unmanaged Primitive Maps") addPrimitiveMapTests(suite, UnmanagedObjectFactory.self) @@ -333,7 +337,7 @@ class UnmanagedPrimitiveMapTests: TestCase, @unchecked Sendable { } } -class ManagedPrimitiveMapTests: TestCase, @unchecked Sendable { +class ManagedPrimitiveMapTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Managed Primitive Maps") addPrimitiveMapTests(suite, ManagedObjectFactory.self) diff --git a/RealmSwift/Tests/PrimitiveMutableSetTests.swift b/RealmSwift/Tests/PrimitiveMutableSetTests.swift index 24bd1a0a73..9167426e7c 100644 --- a/RealmSwift/Tests/PrimitiveMutableSetTests.swift +++ b/RealmSwift/Tests/PrimitiveMutableSetTests.swift @@ -19,7 +19,11 @@ import XCTest import RealmSwift -class PrimitiveMutableSetTestsBase: TestCase, @unchecked Sendable { +#if canImport(RealmSwiftTestSupport) +import RealmSwiftTestSupport +#endif + +class PrimitiveMutableSetTestsBase: TestCase { var realm: Realm? var obj: V.SetRoot! var obj2: V.SetRoot! @@ -46,7 +50,7 @@ class PrimitiveMutableSetTestsBase: TestCa } } -class PrimitiveMutableSetTests: PrimitiveMutableSetTestsBase, @unchecked Sendable { +class PrimitiveMutableSetTests: PrimitiveMutableSetTestsBase { func testInvalidated() { XCTAssertFalse(mutableSet.isInvalidated) if let realm = obj.realm { @@ -187,7 +191,7 @@ class PrimitiveMutableSetTests: PrimitiveM } } -class MinMaxPrimitiveMutableSetTests: PrimitiveMutableSetTestsBase, @unchecked Sendable where V.PersistedType: MinMaxType { +class MinMaxPrimitiveMutableSetTests: PrimitiveMutableSetTestsBase where V.PersistedType: MinMaxType { func testMin() { XCTAssertNil(mutableSet.min()) mutableSet.insert(objectsIn: values) @@ -201,7 +205,7 @@ class MinMaxPrimitiveMutableSetTests: Prim } } -class AddablePrimitiveMutableSetTests: PrimitiveMutableSetTestsBase, @unchecked Sendable where V: NumericValueFactory, V.PersistedType: AddableType { +class AddablePrimitiveMutableSetTests: PrimitiveMutableSetTestsBase where V: NumericValueFactory, V.PersistedType: AddableType { func testSum() { XCTAssertEqual(mutableSet.sum(), .zero) mutableSet.insert(objectsIn: values) @@ -215,7 +219,7 @@ class AddablePrimitiveMutableSetTests: Pri } } -class SortablePrimitiveMutableSetTests: PrimitiveMutableSetTestsBase, @unchecked Sendable where V.PersistedType: SortableType { +class SortablePrimitiveMutableSetTests: PrimitiveMutableSetTestsBase where V.PersistedType: SortableType { func testSorted() { var shuffled = values! shuffled.removeFirst() @@ -329,7 +333,7 @@ func addMutableSetTests(_ suite: XCTestSuite, _ type: OF.Type MinMaxPrimitiveMutableSetTests.defaultTestSuite.tests.forEach(suite.addTest) } -class UnmanagedPrimitiveMutableSetTests: TestCase, @unchecked Sendable { +class UnmanagedPrimitiveMutableSetTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Unmanaged Primitive Sets") addMutableSetTests(suite, UnmanagedObjectFactory.self) @@ -337,7 +341,7 @@ class UnmanagedPrimitiveMutableSetTests: TestCase, @unchecked Sendable { } } -class ManagedPrimitiveMutableSetTests: TestCase, @unchecked Sendable { +class ManagedPrimitiveMutableSetTests: TestCase { override class var defaultTestSuite: XCTestSuite { let suite = XCTestSuite(name: "Managed Primitive Sets") addMutableSetTests(suite, ManagedObjectFactory.self) diff --git a/RealmSwift/Tests/ProjectedCollectTests.swift b/RealmSwift/Tests/ProjectedCollectTests.swift index 37d166f9d1..054f2762e9 100644 --- a/RealmSwift/Tests/ProjectedCollectTests.swift +++ b/RealmSwift/Tests/ProjectedCollectTests.swift @@ -30,7 +30,7 @@ class ProjectedCollections: Projection { @Projected(\PersistedCollections.set.projectTo.firstName) var set: ProjectedCollection } -class ProjectedCollectionsTestsTemplate: TestCase, @unchecked Sendable { +class ProjectedCollectionsTestsTemplate: TestCase { // To test some of methods there should be a collection of projections instead of collection of strings // set value in subclass var collection: ProjectedCollection! @@ -193,8 +193,9 @@ class ProjectedCollectionsTestsTemplate: TestCase, @unchecked Sendable { ex.fulfill() } + let config = configurationWithTestPath() dispatchSyncNewThread { @Sendable in - let realm = self.realmWithTestPath() + let realm = try! Realm(configuration: config) realm.beginWrite() let obj = realm.create(CommonPerson.self) obj.firstName = "Name" @@ -276,8 +277,9 @@ class ProjectedCollectionsTestsTemplate: TestCase, @unchecked Sendable { func testFreezeFromWrongThread() { nonisolated(unsafe) let collection = realmWithTestPath().objects(PersonProjection.self).first!.firstFriendsName + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - self.assertThrows(collection.freeze(), reason: "Realm accessed from incorrect thread") + unsafeSelf.assertThrows(collection.freeze(), reason: "Realm accessed from incorrect thread") } } @@ -302,7 +304,7 @@ class ProjectedCollectionsTestsTemplate: TestCase, @unchecked Sendable { } } -class ProjectedListTests: ProjectedCollectionsTestsTemplate, @unchecked Sendable { +class ProjectedListTests: ProjectedCollectionsTestsTemplate { override func setUp() { super.setUp() let realm = realmWithTestPath() @@ -314,7 +316,7 @@ class ProjectedListTests: ProjectedCollectionsTestsTemplate, @unchecked Sendable } } -class ProjectedSetTests: ProjectedCollectionsTestsTemplate, @unchecked Sendable { +class ProjectedSetTests: ProjectedCollectionsTestsTemplate { override func setUp() { super.setUp() let realm = realmWithTestPath() diff --git a/RealmSwift/Tests/ProjectionTests.swift b/RealmSwift/Tests/ProjectionTests.swift index 68cc7e2881..54a8e2b30a 100644 --- a/RealmSwift/Tests/ProjectionTests.swift +++ b/RealmSwift/Tests/ProjectionTests.swift @@ -26,11 +26,7 @@ import RealmTestSupport #endif // Keypaths are supposed to be Sendable but that never got implemented -#if compiler(<6) -extension KeyPath: @unchecked Sendable {} -#else extension KeyPath: @retroactive @unchecked Sendable {} -#endif // MARK: Test objects definitions @@ -258,7 +254,7 @@ public final class MultipleProjectionsFromOneProperty: Projection // MARK: Tests @available(iOS 13.0, *) -class ProjectionTests: TestCase, @unchecked Sendable { +class ProjectionTests: TestCase { func assertSetEquals(_ set: MutableSet, _ expected: Array) { XCTAssertEqual(set.count, Set(expected).count) XCTAssertEqual(Set(set), Set(expected)) @@ -715,8 +711,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { // Write on a background thread so that oldValue is present let tsr = ThreadSafeReference(to: obj) nonisolated(unsafe) let newValue = new + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() let obj = realm.resolve(tsr)! try! realm.write { obj.int8Col = 5 // Write to another property to verify keypath filtering works @@ -962,8 +959,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { let token = johnProjection.observe(keyPaths: ["lastName"], on: nil) { _ in ex.fulfill() } + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { @Sendable in - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() try! realm.write { let johnObject = realm.objects(CommonPerson.self).filter("lastName == 'Snow'").first! johnObject.lastName = "Targaryen" @@ -988,8 +986,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { XCTFail("expected .change, got \(changes)") } } + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { @Sendable in - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() try! realm.write { let johnObject = realm.objects(CommonPerson.self).filter("lastName == 'Snow'").first! johnObject.extras?.phone?.mobile?.number = "529-345-678" @@ -1009,7 +1008,7 @@ class ProjectionTests: TestCase, @unchecked Sendable { } } dispatchSyncNewThread { @Sendable in - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() try! realm.write { let johnObject = realm.objects(CommonPerson.self).filter("lastName == 'Snow'").first! johnObject.extras?.email = "joe@realm.com" @@ -1031,7 +1030,7 @@ class ProjectionTests: TestCase, @unchecked Sendable { } } dispatchSyncNewThread { @Sendable in - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() try! realm.write { let johnObject = realm.objects(CommonPerson.self).filter("lastName == 'Snow'").first! johnObject.address?.city = "Barranquilla" @@ -1673,8 +1672,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { func testFreezeFromWrongThread() { nonisolated(unsafe) let projection = simpleProjection() + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - self.assertThrows(projection.freeze(), "Realm accessed from incorrect thread") + unsafeSelf.assertThrows(projection.freeze(), "Realm accessed from incorrect thread") } } @@ -1770,8 +1770,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { let tsr = ThreadSafeReference(to: projection) nonisolated(unsafe) var frozen: SimpleProjection! + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() let resolvedProjection: SimpleProjection = realm.resolve(tsr)! try! realm.write { resolvedProjection.int = 1 @@ -1789,8 +1790,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { let realm = realmWithTestPath() XCTAssertEqual(realm.objects(SimpleProjection.self).count, 0) nonisolated(unsafe) var frozen: SimpleProjection! + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - let projection = self.simpleProjection() + let projection = unsafeSelf.simpleProjection() frozen = projection.freeze() } XCTAssertNil(frozen.thaw()) @@ -1823,8 +1825,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { // Wait for the notifier to be registered before we do the write realm.refresh() + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { @Sendable in - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() let johnObject = realm.objects(CommonPerson.self).filter("lastName == 'Snow'").first! try! realm.write { johnObject.lastName = "Ali" @@ -1857,8 +1860,9 @@ class ProjectionTests: TestCase, @unchecked Sendable { try! realm.write {} + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - let realm = self.realmWithTestPath() + let realm = unsafeSelf.realmWithTestPath() try! realm.write { realm.objects(SimpleObject.self).first!.int = 2 } diff --git a/RealmSwift/Tests/PropertyTests.swift b/RealmSwift/Tests/PropertyTests.swift index 40bf491880..deee163d85 100644 --- a/RealmSwift/Tests/PropertyTests.swift +++ b/RealmSwift/Tests/PropertyTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class PropertyTests: TestCase, @unchecked Sendable { +class PropertyTests: TestCase { var primitiveProperty: Property! var linkProperty: Property! var primaryProperty: Property! diff --git a/RealmSwift/Tests/QueryTests.swift b/RealmSwift/Tests/QueryTests.swift index 108b3f6d55..2fe0eff3b5 100644 --- a/RealmSwift/Tests/QueryTests.swift +++ b/RealmSwift/Tests/QueryTests.swift @@ -22,7 +22,7 @@ import RealmSwift // This file is generated from a template. Do not edit directly. // swiftlint:disable large_tuple vertical_parameter_alignment -class QueryTests: TestCase, @unchecked Sendable { +class QueryTests: TestCase { private var realm: Realm! // MARK: Test data population diff --git a/RealmSwift/Tests/QueryTests.swift.gyb b/RealmSwift/Tests/QueryTests.swift.gyb index 993942ab21..81781bfc81 100644 --- a/RealmSwift/Tests/QueryTests.swift.gyb +++ b/RealmSwift/Tests/QueryTests.swift.gyb @@ -234,7 +234,7 @@ import RealmSwift return [list(p) for _, p in itertools.groupby(properties, lambda x: x.className)] }% -class QueryTests: TestCase, @unchecked Sendable { +class QueryTests: TestCase { private var realm: Realm! // MARK: Test data population diff --git a/RealmSwift/Tests/RealmCollectionTypeTests.swift b/RealmSwift/Tests/RealmCollectionTypeTests.swift index 91ea3cde97..eab97ff6e8 100644 --- a/RealmSwift/Tests/RealmCollectionTypeTests.swift +++ b/RealmSwift/Tests/RealmCollectionTypeTests.swift @@ -90,7 +90,7 @@ struct Config { SwiftObject.self, SwiftBoolObject.self]) } -class RealmCollectionTests: TestCase, @unchecked Sendable where +class RealmCollectionTests: TestCase where Collection.Element == CTTNullableStringObjectWithLink, Collection.Index == Int, AggregateCollection.Element == CTTAggregateObject, AggregateCollection.Index == Int { var str1: CTTNullableStringObjectWithLink! @@ -666,7 +666,7 @@ class RealmCollectionTests) { + func check(_ changes: RealmCollectionChange) { switch changes { case .initial(let collection): XCTAssertEqual(collection.count, 2) @@ -685,7 +685,7 @@ class RealmCollectionTests) { + func checkFiltered(_ changes: RealmCollectionChange) { switch changes { case .initial(let collection): XCTAssertEqual(collection.count, 2) @@ -819,8 +819,9 @@ class RealmCollectionTests, Results>, @unchecked Sendable { +class ResultsTests: RealmCollectionTests, Results> { override class var defaultTestSuite: XCTestSuite { // Don't run tests for the base class if isEqual(ResultsTests.self) { @@ -1464,7 +1472,7 @@ class ResultsTests: RealmCollectionTests Results { return realm.objects(CTTNullableStringObjectWithLink.self) } @@ -1621,7 +1629,7 @@ class ResultsFromTableTests: ResultsTests, @unchecked Sendable { } } -class ResultsFromTableViewTests: ResultsTests, @unchecked Sendable { +class ResultsFromTableViewTests: ResultsTests { override func getCollection(_ realm: Realm) -> Results { return realm.objects(CTTNullableStringObjectWithLink.self).filter("stringCol != ''") } @@ -1632,7 +1640,7 @@ class ResultsFromTableViewTests: ResultsTests, @unchecked Sendable { } } -class ResultsFromLinkViewTests: ResultsTests, @unchecked Sendable { +class ResultsFromLinkViewTests: ResultsTests { override func getCollection(_ realm: Realm) -> Results { let array = realm.create(CTTStringList.self, value: [[str1, str2]]) return array.array.filter(NSPredicate(value: true)) @@ -1656,7 +1664,7 @@ class ResultsFromLinkViewTests: ResultsTests, @unchecked Sendable { // MARK: List -class ListRealmCollectionTests: RealmCollectionTests, List>, @unchecked Sendable { +class ListRealmCollectionTests: RealmCollectionTests, List> { override class var defaultTestSuite: XCTestSuite { // Don't run tests for the base class if isEqual(ListRealmCollectionTests.self) { @@ -1671,7 +1679,7 @@ class ListRealmCollectionTests: RealmCollectionTests List { return CTTStringList(value: [[str1, str2]]).array } @@ -1827,7 +1835,7 @@ class ListUnmanagedRealmCollectionTests: ListRealmCollectionTests, @unchecked Se override func testQueryFrozenCollection() {} } -class ListNewlyAddedRealmCollectionTests: ListRealmCollectionTests, @unchecked Sendable { +class ListNewlyAddedRealmCollectionTests: ListRealmCollectionTests { override func getCollection(_ realm: Realm) -> List { let array = CTTStringList(value: [[str1, str2]]) realm.add(array) @@ -1841,7 +1849,7 @@ class ListNewlyAddedRealmCollectionTests: ListRealmCollectionTests, @unchecked S } } -class ListNewlyCreatedRealmCollectionTests: ListRealmCollectionTests, @unchecked Sendable { +class ListNewlyCreatedRealmCollectionTests: ListRealmCollectionTests { override func getCollection(_ realm: Realm) -> List { realm.create(CTTStringList.self, value: [[str1, str2]]).array } @@ -1852,7 +1860,7 @@ class ListNewlyCreatedRealmCollectionTests: ListRealmCollectionTests, @unchecked } } -class ListRetrievedRealmCollectionTests: ListRealmCollectionTests, @unchecked Sendable { +class ListRetrievedRealmCollectionTests: ListRealmCollectionTests { override func getCollection(_ realm: Realm) -> List { _ = realm.create(CTTStringList.self, value: [[str1, str2]]) return realm.objects(CTTStringList.self).first!.array @@ -1867,7 +1875,7 @@ class ListRetrievedRealmCollectionTests: ListRealmCollectionTests, @unchecked Se // MARK: MutableSet -class MutableSetRealmCollectionTests: RealmCollectionTests, MutableSet>, @unchecked Sendable { +class MutableSetRealmCollectionTests: RealmCollectionTests, MutableSet> { override class var defaultTestSuite: XCTestSuite { // Don't run tests for the base class if isEqual(MutableSetRealmCollectionTests.self) { @@ -1937,7 +1945,7 @@ class MutableSetRealmCollectionTests: RealmCollectionTests MutableSet { return CTTStringSet(value: [[str1, str2]]).set } @@ -2079,7 +2087,7 @@ class MutableSetUnmanagedRealmCollectionTests: MutableSetRealmCollectionTests, @ override func testQueryFrozenCollection() {} } -class MutableSetNewlyAddedRealmCollectionTests: MutableSetRealmCollectionTests, @unchecked Sendable { +class MutableSetNewlyAddedRealmCollectionTests: MutableSetRealmCollectionTests { override func getCollection(_ realm: Realm) -> MutableSet { let set = CTTStringSet(value: [[str1, str2]]) realm.add(set) @@ -2093,7 +2101,7 @@ class MutableSetNewlyAddedRealmCollectionTests: MutableSetRealmCollectionTests, } } -class MutableSetNewlyCreatedRealmCollectionTests: MutableSetRealmCollectionTests, @unchecked Sendable { +class MutableSetNewlyCreatedRealmCollectionTests: MutableSetRealmCollectionTests { override func getCollection(_ realm: Realm) -> MutableSet { realm.create(CTTStringSet.self, value: [[str1, str2]]).set } @@ -2104,7 +2112,7 @@ class MutableSetNewlyCreatedRealmCollectionTests: MutableSetRealmCollectionTests } } -class MutableSetRetrievedRealmCollectionTests: MutableSetRealmCollectionTests, @unchecked Sendable { +class MutableSetRetrievedRealmCollectionTests: MutableSetRealmCollectionTests { override func getCollection(_ realm: Realm) -> MutableSet { _ = realm.create(CTTStringSet.self, value: [[str1, str2]]) return realm.objects(CTTStringSet.self).first!.set @@ -2116,7 +2124,7 @@ class MutableSetRetrievedRealmCollectionTests: MutableSetRealmCollectionTests, @ return realm.objects(CTTAggregateObjectSet.self).first!.set } } -class LinkingObjectsCollectionTypeTests: RealmCollectionTests, LinkingObjects>, @unchecked Sendable { +class LinkingObjectsCollectionTypeTests: RealmCollectionTests, LinkingObjects> { override func getCollection(_ realm: Realm) -> LinkingObjects { let target = realm.create(CTTLinkTarget.self, value: [0]) for object in realm.objects(CTTNullableStringObjectWithLink.self) { @@ -2140,7 +2148,7 @@ class LinkingObjectsCollectionTypeTests: RealmCollectionTests, AnyRealmCollection>, @unchecked Sendable { +class AnyRealmCollectionTests: RealmCollectionTests, AnyRealmCollection> { override func getCollection(_ realm: Realm) -> AnyRealmCollection { AnyRealmCollection(realm.create(CTTStringList.self, value: [[str1, str2]]).array) } @@ -2183,7 +2191,7 @@ class AnyRealmCollectionTests: RealmCollectionTests(keyPath: KeyPath>, value: T?) { let o = RealmPropertyObject() diff --git a/RealmSwift/Tests/RealmTests.swift b/RealmSwift/Tests/RealmTests.swift index ecd0ff6c63..cafe6a23f7 100644 --- a/RealmSwift/Tests/RealmTests.swift +++ b/RealmSwift/Tests/RealmTests.swift @@ -30,7 +30,7 @@ import RealmSwiftTestSupport #endif @available(*, deprecated) // Silence deprecation warnings for RealmOptional -class RealmTests: TestCase, @unchecked Sendable { +class RealmTests: TestCase { enum TestError: Error { case intentional } @@ -1405,6 +1405,7 @@ class RealmTests: TestCase, @unchecked Sendable { XCTAssertEqual(2, realm.objects(SwiftStringObject.self).count) } + @MainActor func testAsyncTransactionCancel() { let waitComplete = expectation(description: "async wait complete") let expectation = XCTestExpectation(description: "testAsyncTransactionCancel expectation") @@ -1412,7 +1413,7 @@ class RealmTests: TestCase, @unchecked Sendable { let unexpectation = XCTestExpectation(description: "should not fulfill") unexpectation.isInverted = true - DispatchQueue.main.async { + DispatchQueue.main.async { @MainActor in let realm = try! Realm() realm.beginAsyncWrite { realm.create(SwiftStringObject.self, value: ["string"]) @@ -1662,8 +1663,9 @@ extension RealmTests { @available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.4, *) func testAsyncRefreshOnQueueConfinedRealm() async throws { let realm = Locked(wrappedValue: nil) + let queue = self.queue dispatchSyncNewThread { - realm.wrappedValue = try! Realm(queue: self.queue) + realm.wrappedValue = try! Realm(queue: queue) } // asyncRefresh() has to be called from a statically isolated context, // but the test as whole can't be isolated (or the dispatch async breaks), @@ -1916,24 +1918,19 @@ extension RealmTests { @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) @globalActor actor CustomGlobalActor: GlobalActor { +#if compiler(<6.2) static var shared = CustomGlobalActor() +#else + static let shared = CustomGlobalActor() +#endif } -#if compiler(<6) -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -extension CancellationError: Equatable { - public static func == (lhs: CancellationError, rhs: CancellationError) -> Bool { - true - } -} -#else @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) extension CancellationError: @retroactive Equatable { public static func == (lhs: CancellationError, rhs: CancellationError) -> Bool { true } } -#endif // Helper extension LogLevel { @@ -1964,7 +1961,7 @@ extension LogLevel { } @available(macOS 12.0, watchOS 8.0, iOS 15.0, tvOS 15.0, macCatalyst 15.0, *) -class LoggerTests: TestCase, @unchecked Sendable { +class LoggerTests: TestCase { var logger: Logger! override func setUp() { logger = Logger.shared diff --git a/RealmSwift/Tests/SchemaTests.swift b/RealmSwift/Tests/SchemaTests.swift index 93c4b9cabb..77353533df 100644 --- a/RealmSwift/Tests/SchemaTests.swift +++ b/RealmSwift/Tests/SchemaTests.swift @@ -19,7 +19,7 @@ import XCTest import RealmSwift -class SchemaTests: TestCase, @unchecked Sendable { +class SchemaTests: TestCase { var schema: Schema! override func setUp() { diff --git a/RealmSwift/Tests/SectionedResultsTests.swift b/RealmSwift/Tests/SectionedResultsTests.swift index 24a7568754..87745d5df3 100644 --- a/RealmSwift/Tests/SectionedResultsTests.swift +++ b/RealmSwift/Tests/SectionedResultsTests.swift @@ -236,7 +236,7 @@ class BaseOptionalPrimitiveSectionedResultsTests Bool { } @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) -class SwiftUITests: TestCase, @unchecked Sendable { +class SwiftUITests: TestCase { override class var defaultTestSuite: XCTestSuite { if hasSwiftUI() { return super.defaultTestSuite diff --git a/RealmSwift/Tests/SwiftUnicodeTests.swift b/RealmSwift/Tests/SwiftUnicodeTests.swift index 25e0aa7e96..1bf2df51b1 100644 --- a/RealmSwift/Tests/SwiftUnicodeTests.swift +++ b/RealmSwift/Tests/SwiftUnicodeTests.swift @@ -19,9 +19,13 @@ import XCTest import RealmSwift +#if canImport(RealmSwiftTestSupport) +import RealmSwiftTestSupport +#endif + let utf8TestString = "值значен™👍☞⎠‱௹♣︎☐▼❒∑⨌⧭иеمرحبا" -class SwiftUnicodeTests: TestCase, @unchecked Sendable { +class SwiftUnicodeTests: TestCase { func testUTF8StringContents() { let realm = realmWithTestPath() diff --git a/RealmSwift/Tests/TestCase.swift b/RealmSwift/Tests/TestCase.swift index 3fee880840..e7187be2dd 100644 --- a/RealmSwift/Tests/TestCase.swift +++ b/RealmSwift/Tests/TestCase.swift @@ -35,19 +35,21 @@ func inMemoryRealm(_ inMememoryIdentifier: String) -> Realm { func openRealm(configuration: Realm.Configuration = .defaultConfiguration, actor: isolated any Actor, downloadBeforeOpen: Realm.OpenBehavior = .never) async throws -> Realm { -#if compiler(<6) - try await Realm(configuration: configuration, actor: actor, downloadBeforeOpen: downloadBeforeOpen) -#else try await Realm.open(configuration: configuration, downloadBeforeOpen: downloadBeforeOpen) -#endif } -class TestCase: RLMTestCaseBase, @unchecked Sendable { +class TestCase: RLMTestCaseBase { @Locked var exceptionThrown = false var testDir: String! = nil let queue = DispatchQueue(label: "background") + func configurationWithTestPath(configuration: Realm.Configuration = Realm.Configuration()) -> Realm.Configuration { + var configuration = configuration + configuration.fileURL = testRealmURL() + return configuration + } + @discardableResult func realmWithTestPath(configuration: Realm.Configuration = Realm.Configuration()) -> Realm { var configuration = configuration @@ -100,10 +102,7 @@ class TestCase: RLMTestCaseBase, @unchecked Sendable { } } - #if compiler(<6) - // This actually should be @Sendable even in Swift 5 mode, but updating the - // relevant tests are difficult in that mode - func dispatchSyncNewThread(block: @escaping () -> Void) { + func dispatchSyncNewThread(block: @Sendable @escaping () -> Void) { queue.async { autoreleasepool { block() @@ -111,16 +110,16 @@ class TestCase: RLMTestCaseBase, @unchecked Sendable { } queue.sync { } } - #else - func dispatchSyncNewThread(block: @Sendable @escaping () -> Void) { + + func dispatchSyncBackground(block: @Sendable @escaping (TestCase) -> Void) { + nonisolated(unsafe) let unsafeSelf = self queue.async { autoreleasepool { - block() + block(unsafeSelf) } } queue.sync { } } - #endif func assertThrows(_ block: @autoclosure () -> T, named: String? = RLMExceptionName, _ message: String? = nil, fileName: String = #file, lineNumber: UInt = #line) { diff --git a/RealmSwift/Tests/TestUtils.swift b/RealmSwift/Tests/TestUtils.swift index fc00b44eac..b2fca978ab 100644 --- a/RealmSwift/Tests/TestUtils.swift +++ b/RealmSwift/Tests/TestUtils.swift @@ -80,207 +80,134 @@ public class Locked: @unchecked Sendable { } } -public extension XCTestCase { - /// Check whether two test objects are equal (refer to the same row in the same Realm), even if their models - /// don't define a primary key. - func assertEqual(_ o1: O?, _ o2: O?, fileName: StaticString = #filePath, lineNumber: UInt = #line) { - if o1 == nil && o2 == nil { - return - } - if let o1 = o1, let o2 = o2, o1.isSameObject(as: o2) { - return - } - XCTFail("Objects expected to be equal, but weren't. First: \(String(describing: o1)), " - + "second: \(String(describing: o2))", file: (fileName), line: lineNumber) +/// Check whether two test objects are equal (refer to the same row in the same Realm), even if their models +/// don't define a primary key. +public func assertEqual(_ o1: O?, _ o2: O?, fileName: StaticString = #filePath, lineNumber: UInt = #line) { + if o1 == nil && o2 == nil { + return } - - /// Check whether two collections containing Realm objects are equal. - func assertEqual(_ c1: C, _ c2: C, fileName: StaticString = #filePath, lineNumber: UInt = #line) - where C.Iterator.Element: Object { - XCTAssertEqual(c1.count, c2.count, "Collection counts were incorrect", file: (fileName), line: lineNumber) - for (o1, o2) in zip(c1, c2) { - assertEqual(o1, o2, fileName: fileName, lineNumber: lineNumber) - } - } - - func assertEqual(_ expected: [T?], _ actual: [T?], file: StaticString = #file, line: UInt = #line) { - if expected.count != actual.count { - XCTFail("assertEqual failed: (\"\(expected)\") is not equal to (\"\(actual)\")", - file: (file), line: line) - return - } - - XCTAssertEqual(expected.count, actual.count, "Collection counts were incorrect", file: (file), line: line) - for (e, a) in zip(expected, actual) where e != a { - XCTFail("assertEqual failed: (\"\(expected)\") is not equal to (\"\(actual)\")", - file: (file), line: line) - return - } - } - - func assertSucceeds(message: String? = nil, fileName: StaticString = #filePath, - lineNumber: UInt = #line, block: () throws -> Void) { - do { - try block() - } catch { - XCTFail("Expected no error, but instead caught <\(error)>.", - file: (fileName), line: lineNumber) - } - } - - func assertFails(_ expectedError: Realm.Error.Code, _ message: String? = nil, - fileName: StaticString = #filePath, lineNumber: UInt = #line, - block: () throws -> T) { - do { - _ = try autoreleasepool(invoking: block) - XCTFail("Expected to catch <\(expectedError)>, but no error was thrown.", - file: fileName, line: lineNumber) - } catch let e as Realm.Error where e.code == expectedError { - if message != nil { - XCTAssertEqual(e.localizedDescription, message, file: fileName, line: lineNumber) - } - } catch { - XCTFail("Expected to catch <\(expectedError)>, but instead caught <\(error)>.", - file: fileName, line: lineNumber) - } + if let o1 = o1, let o2 = o2, o1.isSameObject(as: o2) { + return } + XCTFail("Objects expected to be equal, but weren't. First: \(String(describing: o1)), " + + "second: \(String(describing: o2))", file: (fileName), line: lineNumber) +} - func assertFails(_ expectedError: Realm.Error.Code, _ file: URL, _ message: String, - fileName: StaticString = #filePath, lineNumber: UInt = #line, - block: () throws -> T) { - do { - _ = try autoreleasepool(invoking: block) - XCTFail("Expected to catch <\(expectedError)>, but no error was thrown.", - file: fileName, line: lineNumber) - } catch let e as Realm.Error where e.code == expectedError { - XCTAssertEqual(e.localizedDescription, message, file: fileName, line: lineNumber) - XCTAssertEqual(e.fileURL, file, file: fileName, line: lineNumber) - } catch { - XCTFail("Expected to catch <\(expectedError)>, but instead caught <\(error)>.", - file: fileName, line: lineNumber) - } +/// Check whether two collections containing Realm objects are equal. +public func assertEqual(_ c1: C, _ c2: C, fileName: StaticString = #filePath, lineNumber: UInt = #line) +where C.Iterator.Element: Object { + XCTAssertEqual(c1.count, c2.count, "Collection counts were incorrect", file: (fileName), line: lineNumber) + for (o1, o2) in zip(c1, c2) { + assertEqual(o1, o2, fileName: fileName, lineNumber: lineNumber) } +} - func assertFails(_ expectedError: Error, _ message: String? = nil, - fileName: StaticString = #filePath, lineNumber: UInt = #line, - block: () throws -> T) { - do { - _ = try autoreleasepool(invoking: block) - XCTFail("Expected to catch <\(expectedError)>, but no error was thrown.", - file: fileName, line: lineNumber) - } catch let e where e._code == expectedError._code { - // Success! - } catch { - XCTFail("Expected to catch <\(expectedError)>, but instead caught <\(error)>.", - file: fileName, line: lineNumber) - } +public func assertEqual(_ expected: [T?], _ actual: [T?], file: StaticString = #file, line: UInt = #line) { + if expected.count != actual.count { + XCTFail("assertEqual failed: (\"\(expected)\") is not equal to (\"\(actual)\")", + file: (file), line: line) + return } - func assertNil(block: @autoclosure() -> T?, _ message: String? = nil, - fileName: StaticString = #filePath, lineNumber: UInt = #line) { - XCTAssert(block() == nil, message ?? "", file: (fileName), line: lineNumber) + XCTAssertEqual(expected.count, actual.count, "Collection counts were incorrect", file: (file), line: line) + for (e, a) in zip(expected, actual) where e != a { + XCTFail("assertEqual failed: (\"\(expected)\") is not equal to (\"\(actual)\")", + file: (file), line: line) + return } +} - func assertMatches(_ block: @autoclosure () -> String, _ regexString: String, _ message: String? = nil, - fileName: String = #file, lineNumber: UInt = #line) { - RLMAssertMatches(self, block, regexString, message, fileName, lineNumber) +public func assertSucceeds(message: String? = nil, fileName: StaticString = #filePath, + lineNumber: UInt = #line, block: () throws -> Void) { + do { + try block() + } catch { + XCTFail("Expected no error, but instead caught <\(error)>.", + file: (fileName), line: lineNumber) } +} - /// Check that a `MutableSet` contains all expected elements. - func assertSetContains(_ set: MutableSet, keyPath: KeyPath, items: [U]) where U: Hashable { - var itemMap = Dictionary(uniqueKeysWithValues: items.map { ($0, false)}) - set.map { $0[keyPath: keyPath]}.forEach { - itemMap[$0] = items.contains($0) +public func assertFails(_ expectedError: Realm.Error.Code, _ message: String? = nil, + fileName: StaticString = #filePath, lineNumber: UInt = #line, + block: () throws -> T) { + do { + _ = try autoreleasepool(invoking: block) + XCTFail("Expected to catch <\(expectedError)>, but no error was thrown.", + file: fileName, line: lineNumber) + } catch let e as Realm.Error where e.code == expectedError { + if message != nil { + XCTAssertEqual(e.localizedDescription, message, file: fileName, line: lineNumber) } - // ensure all items are present in the set. - XCTAssertFalse(itemMap.values.contains(false)) + } catch { + XCTFail("Expected to catch <\(expectedError)>, but instead caught <\(error)>.", + file: fileName, line: lineNumber) } +} - /// Check that an `AnyRealmCollection` contains all expected elements. - func assertAnyRealmCollectionContains(_ set: AnyRealmCollection, keyPath: KeyPath, items: [U]) where U: Hashable { - var itemMap = Dictionary(uniqueKeysWithValues: items.map { ($0, false)}) - set.map { $0[keyPath: keyPath]}.forEach { - itemMap[$0] = items.contains($0) - } - // ensure all items are present in the set. - XCTAssertFalse(itemMap.values.contains(false)) +public func assertFails(_ expectedError: Realm.Error.Code, _ file: URL, _ message: String, + fileName: StaticString = #filePath, lineNumber: UInt = #line, + block: () throws -> T) { + do { + _ = try autoreleasepool(invoking: block) + XCTFail("Expected to catch <\(expectedError)>, but no error was thrown.", + file: fileName, line: lineNumber) + } catch let e as Realm.Error where e.code == expectedError { + XCTAssertEqual(e.localizedDescription, message, file: fileName, line: lineNumber) + XCTAssertEqual(e.fileURL, file, file: fileName, line: lineNumber) + } catch { + XCTFail("Expected to catch <\(expectedError)>, but instead caught <\(error)>.", + file: fileName, line: lineNumber) } } -#if compiler(<6) -@_unsafeInheritExecutor -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -public func assertThrowsErrorAsync( - _ expression: @autoclosure () async throws -> T, - _ expectedError: E, - file: StaticString = #filePath, line: UInt = #line) async { +public func assertFails(_ expectedError: Error, _ message: String? = nil, + fileName: StaticString = #filePath, lineNumber: UInt = #line, + block: () throws -> T) { do { - _ = try await expression() - XCTFail("Expected expression to throw error \(expectedError)", file: file, line: line) - } catch let error as E { - XCTAssertEqual(error, expectedError, file: file, line: line) + _ = try autoreleasepool(invoking: block) + XCTFail("Expected to catch <\(expectedError)>, but no error was thrown.", + file: fileName, line: lineNumber) + } catch let e where e._code == expectedError._code { + // Success! } catch { - XCTFail("Expected expression to throw error \(expectedError) but got \(error)", file: file, line: line) + XCTFail("Expected to catch <\(expectedError)>, but instead caught <\(error)>.", + file: fileName, line: lineNumber) } } -// Fork, call an expression which should hit a precondition failure in the child -// process, and then verify that the expected failure message was printed. Note -// that Swift and Foundation do not support fork(), so anything which does more -// than a very limited amount of work before the precondition failure is very -// likely to break. -@_unsafeInheritExecutor -@available(macOS 10.15.4, iOS 13.4, tvOS 13.4, watchOS 6.4, *) -public func assertPreconditionFailure(_ message: String, _ expression: () async throws -> T, - file: StaticString = #filePath, line: UInt = #line) async throws { - // We can't perform these tests on tvOS, watchOS, or on devices - guard RLMCanFork() else { return } - - let pipe = Pipe() +public func assertNil(block: @autoclosure() -> T?, _ message: String? = nil, + fileName: StaticString = #filePath, lineNumber: UInt = #line) { + XCTAssert(block() == nil, message ?? "", file: (fileName), line: lineNumber) +} - let pid = RLMFork() - if pid == -1 { - return XCTFail("Failed to fork for test", file: file, line: line) - } - if pid == 0 { - // In child process - // Point stdout and stderr at our pipe - let fd = pipe.fileHandleForWriting.fileDescriptor - while dup2(fd, STDOUT_FILENO) == -1 && errno == EINTR {} - while dup2(fd, STDERR_FILENO) == -1 && errno == EINTR {} - _ = try await expression() - exit(0) - } - - try pipe.fileHandleForWriting.close() - while true { - var status: Int32 = 0 - let ret = waitpid(pid, &status, 0) - if ret == -1 && errno == EINTR { - continue - } - guard ret > 0 else { - return XCTFail("Failed to wait for child process to exit? errno: \(errno)", file: file, line: line) - } - guard status != 0 else { - return XCTFail("Expected child process to crash with message \"\(message)\", but it exited cleanly", file: file, line: line) - } - break +public extension XCTestCase { + func assertMatches(_ block: @autoclosure () -> String, _ regexString: String, _ message: String? = nil, + fileName: String = #file, lineNumber: UInt = #line) { + RLMAssertMatches(self, block, regexString, message, fileName, lineNumber) } +} - guard let data = try pipe.fileHandleForReading.readToEnd() else { - return XCTFail("Expected child process to crash with message \"\(message)\", but it exited without printing anything", file: file, line: line) - } - guard let str = String(data: data, encoding: .utf8) else { - return XCTFail("Expected child process to crash with message \"\(message)\", but it did not print valid utf-8", file: file, line: line) +/// Check that a `MutableSet` contains all expected elements. +public func assertSetContains(_ set: MutableSet, keyPath: KeyPath, items: [U]) where U: Hashable { + var itemMap = Dictionary(uniqueKeysWithValues: items.map { ($0, false)}) + set.map { $0[keyPath: keyPath]}.forEach { + itemMap[$0] = items.contains($0) } + // ensure all items are present in the set. + XCTAssertFalse(itemMap.values.contains(false)) +} - if !str.contains("Precondition failed: \(message)") && !str.contains("Fatal error: \(message)") { - XCTFail("Expected \"\(str)\" to contain \"\(message)\")", file: file, line: line) +/// Check that an `AnyRealmCollection` contains all expected elements. +public func assertAnyRealmCollectionContains(_ set: AnyRealmCollection, keyPath: KeyPath, items: [U]) where U: Hashable { + var itemMap = Dictionary(uniqueKeysWithValues: items.map { ($0, false) }) + set.map { $0[keyPath: keyPath]}.forEach { + itemMap[$0] = items.contains($0) } + // ensure all items are present in the set. + XCTAssertFalse(itemMap.values.contains(false)) } -#else + @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) public func assertThrowsErrorAsync( _ expression: @autoclosure () async throws -> T, @@ -356,4 +283,3 @@ public func assertPreconditionFailure( XCTFail("Expected \"\(str)\" to contain \"\(message)\")", file: file, line: line) } } -#endif diff --git a/RealmSwift/Tests/ThreadSafeReferenceTests.swift b/RealmSwift/Tests/ThreadSafeReferenceTests.swift index 7dcc907ce3..d54ac1452e 100644 --- a/RealmSwift/Tests/ThreadSafeReferenceTests.swift +++ b/RealmSwift/Tests/ThreadSafeReferenceTests.swift @@ -19,7 +19,11 @@ import XCTest import RealmSwift -class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { +#if canImport(RealmSwiftTestSupport) +import RealmSwiftTestSupport +#endif + +class ThreadSafeReferenceTests: TestCase { /// Resolve a thread-safe reference confirming that you can't resolve it a second time. func assertResolve(_ realm: Realm, _ reference: ThreadSafeReference) -> T? { XCTAssertFalse(reference.isInvalidated) @@ -61,14 +65,16 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { try! realm.commitWrite() let ref2 = ThreadSafeReference(to: stringObject) let ref3 = ThreadSafeReference(to: stringObject) + + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { - XCTAssertNil(self.realmWithTestPath().resolve(ref1)) + XCTAssertNil(unsafeSelf.realmWithTestPath().resolve(ref1)) let realm = try! Realm() _ = realm.resolve(ref2) - self.assertThrows(realm.resolve(ref2), - reason: "Can only resolve a thread safe reference once") + unsafeSelf.assertThrows(realm.resolve(ref2), + reason: "Can only resolve a thread safe reference once") // Assert that we can resolve a different reference to the same object. - XCTAssertEqual(self.assertResolve(realm, ref3)!.stringCol, "hello") + XCTAssertEqual(unsafeSelf.assertResolve(realm, ref3)!.stringCol, "hello") } } @@ -87,11 +93,12 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { realm.deleteAll() } } + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - XCTAssertEqual(self.assertResolve(realm, ref1)!.intCol, 0) + XCTAssertEqual(unsafeSelf.assertResolve(realm, ref1)!.intCol, 0) realm.refresh() - XCTAssertNil(self.assertResolve(realm, ref2)) + XCTAssertNil(unsafeSelf.assertResolve(realm, ref2)) } } @@ -106,10 +113,11 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { let intObjectRef = ThreadSafeReference(to: intObject) XCTAssertEqual("", stringObject.stringCol) XCTAssertEqual(0, intObject.intCol) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - let stringObject = self.assertResolve(realm, stringObjectRef)! - let intObject = self.assertResolve(realm, intObjectRef)! + let stringObject = unsafeSelf.assertResolve(realm, stringObjectRef)! + let intObject = unsafeSelf.assertResolve(realm, intObjectRef)! try! realm.write { stringObject.stringCol = "the meaning of life" intObject.intCol = 42 @@ -132,9 +140,10 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { XCTAssertEqual(1, company.employees.count) XCTAssertEqual("jg", company.employees[0].name) let listRef = ThreadSafeReference(to: company.employees) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - let employees = self.assertResolve(realm, listRef)! + let employees = unsafeSelf.assertResolve(realm, listRef)! XCTAssertEqual(1, employees.count) XCTAssertEqual("jg", employees[0].name) @@ -165,9 +174,10 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { XCTAssertEqual(1, company.employeeSet.count) XCTAssertEqual("jg", company.employeeSet[0].name) let setRef = ThreadSafeReference(to: company.employeeSet) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - let employeeSet = self.assertResolve(realm, setRef)! + let employeeSet = unsafeSelf.assertResolve(realm, setRef)! XCTAssertEqual(1, employeeSet.count) XCTAssertEqual("jg", employeeSet[0].name) @@ -177,7 +187,7 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { employeeSet.insert(SwiftEmployeeObject(value: ["name": "az"])) } XCTAssertEqual(2, employeeSet.count) - self.assertSetContains(employeeSet, keyPath: \.name, items: ["jp", "az"]) + assertSetContains(employeeSet, keyPath: \.name, items: ["jp", "az"]) } XCTAssertEqual(1, company.employeeSet.count) XCTAssertEqual("jg", company.employeeSet[0].name) @@ -204,9 +214,10 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { XCTAssertEqual("D", results[0].stringCol) XCTAssertEqual("B", results[1].stringCol) XCTAssertEqual("A", results[2].stringCol) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - let results = self.assertResolve(realm, resultsRef)! + let results = unsafeSelf.assertResolve(realm, resultsRef)! let allObjects = realm.objects(SwiftStringObject.self) XCTAssertEqual(0, allObjects.count) XCTAssertEqual(0, results.count) @@ -252,10 +263,11 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { XCTAssertEqual("Andrea", dogA.owners[0].name) let ownersARef = ThreadSafeReference(to: dogA.owners) let ownersBRef = ThreadSafeReference(to: unaccessedDogB.owners) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - let ownersA = self.assertResolve(realm, ownersARef)! - let ownersB = self.assertResolve(realm, ownersBRef)! + let ownersA = unsafeSelf.assertResolve(realm, ownersARef)! + let ownersB = unsafeSelf.assertResolve(realm, ownersBRef)! XCTAssertEqual(1, ownersA.count) XCTAssertEqual("Andrea", ownersA[0].name) @@ -314,11 +326,12 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { let resultsRef = ThreadSafeReference(to: results) let listRef = ThreadSafeReference(to: list) let setRef = ThreadSafeReference(to: set) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() - let results = self.assertResolve(realm, resultsRef)! - let list = self.assertResolve(realm, listRef)! - let set = self.assertResolve(realm, setRef)! + let results = unsafeSelf.assertResolve(realm, resultsRef)! + let list = unsafeSelf.assertResolve(realm, listRef)! + let set = unsafeSelf.assertResolve(realm, setRef)! XCTAssertEqual(6, results.count) XCTAssertEqual("D", results[0].name) XCTAssertEqual("D", results[1].name) @@ -329,7 +342,7 @@ class ThreadSafeReferenceTests: TestCase, @unchecked Sendable { XCTAssertEqual("C", list[2].name) XCTAssertEqual("D", list[3].name) XCTAssertEqual(4, set.count) - self.assertAnyRealmCollectionContains(set, keyPath: \.name, items: ["A", "B", "C", "D"]) + assertAnyRealmCollectionContains(set, keyPath: \.name, items: ["A", "B", "C", "D"]) } } } @@ -350,7 +363,7 @@ struct TestThreadSafeWrapperStruct { } // MARK: ThreadSafeWrapperTests -class ThreadSafeWrapperTests: ThreadSafeReferenceTests, @unchecked Sendable { +class ThreadSafeWrapperTests: ThreadSafeReferenceTests { func wrapperStruct() -> TestThreadSafeWrapperStruct { let realm = try! Realm() var stringObj: SwiftStringObject?, intObj: SwiftIntObject? @@ -514,11 +527,12 @@ class ThreadSafeWrapperTests: ThreadSafeReferenceTests, @unchecked Sendable { XCTAssertEqual(testStruct.stringObject!.stringCol, "before") XCTAssertEqual(testStruct.intObject!.intCol, 1) + nonisolated(unsafe) let unsafeSelf = self dispatchSyncNewThread { let realm = try! Realm() try! realm.write { - self.assertThrows(testStruct.stringObject = SwiftStringObject(), - reason: "Only managed objects may be wrapped as thread safe.") + unsafeSelf.assertThrows(testStruct.stringObject = SwiftStringObject(), + reason: "Only managed objects may be wrapped as thread safe.") } } } @@ -597,7 +611,7 @@ class ThreadSafeWrapperTests: ThreadSafeReferenceTests, @unchecked Sendable { testStruct.employeeSet!.insert(SwiftEmployeeObject(value: ["name": "az"])) } XCTAssertEqual(2, testStruct.employeeSet!.count) - self.assertSetContains(testStruct.employeeSet!, keyPath: \.name, items: ["jp", "az"]) + assertSetContains(testStruct.employeeSet!, keyPath: \.name, items: ["jp", "az"]) } XCTAssertEqual(2, testStruct.employeeSet!.count) assertSetContains(testStruct.employeeSet!, keyPath: \.name, items: ["jp", "az"]) @@ -716,7 +730,7 @@ class ThreadSafeWrapperTests: ThreadSafeReferenceTests, @unchecked Sendable { XCTAssertEqual("C", testStruct.arcList![2].name) XCTAssertEqual("D", testStruct.arcList![3].name) XCTAssertEqual(4, testStruct.arcSet!.count) - self.assertAnyRealmCollectionContains(testStruct.arcSet!, keyPath: \.name, items: ["A", "B", "C", "D"]) + assertAnyRealmCollectionContains(testStruct.arcSet!, keyPath: \.name, items: ["A", "B", "C", "D"]) } } diff --git a/build.sh b/build.sh index 18382d0d59..34f6e19916 100755 --- a/build.sh +++ b/build.sh @@ -1011,7 +1011,7 @@ case "$COMMAND" in "release-package") version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" - find . -name 'build-*-1*' -maxdepth 1 \ + find . -regex './build-[a-z]*-[12].*' -maxdepth 1 \ | sed 's@./build-[a-z]*-\(.*\)-.*@\1@' \ | sort -u --version-sort \ | xargs ./scripts/create-release-package.rb "${ROOT_WORKSPACE}/pkg" "${version}" @@ -1167,9 +1167,9 @@ x.y.z Release notes (yyyy-MM-dd) ### Compatibility * Realm Studio: 15.0.0 or later. * APIs are backwards compatible with all previous releases in the 10.x.y series. -* Carthage release for Swift is built with Xcode 16.4.0. +* Carthage release for Swift is built with Xcode 26.0.1. * CocoaPods: 1.10 or later. -* Xcode: 15.3.0-26 beta 1. +* Xcode: 16.3.0-26.0.1. ### Internal * Upgraded realm-core from ? to ? diff --git a/scripts/create-release-package.rb b/scripts/create-release-package.rb index 9be3dd86ee..2686f702ca 100755 --- a/scripts/create-release-package.rb +++ b/scripts/create-release-package.rb @@ -28,14 +28,12 @@ def platforms(xcode_version) end def create_xcframework(root, xcode_version, configuration, name) - signing_identity = ENV['SIGNING_IDENTITY'] prefix = "#{root}/#{xcode_version}" output = "#{prefix}/#{configuration}/#{name}.xcframework" files = Dir.glob "#{prefix}/build/#{configuration}/*/#{name}.xcframework/*/#{name}.framework" sh 'xcodebuild', '-create-xcframework', '-allow-internal-distribution', '-output', output, *files.flat_map {|f| ['-framework', f]} - sh 'codesign', '--timestamp', '-s', signing_identity, output end def zip(name, *files) diff --git a/scripts/package_examples.rb b/scripts/package_examples.rb index 550254e25a..ac808c7788 100755 --- a/scripts/package_examples.rb +++ b/scripts/package_examples.rb @@ -44,7 +44,7 @@ def replace_framework(example, framework, path) "examples/tvos/swift", ] -xcode_versions = %w(15.3 15.4 16.2 16.3 16.4) +xcode_versions = %w(16.3 16.4 26.0.1) # Remove reference to Realm.xcodeproj from all example workspaces. base_examples.each do |example| diff --git a/scripts/pr-ci-matrix.rb b/scripts/pr-ci-matrix.rb index 4d17383766..efe9f0a507 100755 --- a/scripts/pr-ci-matrix.rb +++ b/scripts/pr-ci-matrix.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -XCODE_VERSIONS = %w(15.3 15.4 16.2 16.3 16.4) -DOC_VERSION = '16.4' +XCODE_VERSIONS = %w(16.3 16.4 26.0) +DOC_VERSION = '26.0' all = ->(v) { true } latest_only = ->(v) { v == XCODE_VERSIONS.last } @@ -82,10 +82,9 @@ def minimum_version(major) if not filter.call(version) next end - image = version.start_with?('16') ? 'macos-15' : 'macos-14' output_file << """ #{name}-#{version.gsub(' ', '_').gsub('.', '_')}: - runs-on: #{image} + runs-on: macos-15 name: Test #{name} on Xcode #{version} env: DEVELOPER_DIR: '/Applications/Xcode_#{version}.app/Contents/Developer'