├── .github └── workflows │ ├── create-release.yml │ └── pull_request.yml ├── .gitignore ├── .jazzy.yaml ├── .swiftformat ├── .swiftlint.yml ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .versionrc ├── Brewfile ├── CHANGELOG.md ├── Cartfile ├── Cartfile.resolved ├── Dangerfile ├── Examples ├── Podfile ├── Podfile.lock ├── RxRealmDemo-iOS │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── RxRealmDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── RxRealmDemo-iOS.xcscheme ├── RxRealmDemo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── pod_install.sh └── project.yml ├── File ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── LICENSE.md ├── Package.resolved ├── Package.swift ├── README.md ├── RxRealm.podspec ├── RxRealm.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── RxRealm iOS.xcscheme │ ├── RxRealm macOS.xcscheme │ ├── RxRealm tvOS.xcscheme │ └── RxRealm watchOS.xcscheme ├── Sources └── RxRealm │ ├── Info.plist │ ├── RealmObserver.swift │ └── RxRealm.swift ├── Tests ├── LinuxMain.swift └── RxRealmTests │ ├── RxRealmLinkingObjectsTests.swift │ ├── RxRealmListTests.swift │ ├── RxRealmObjectTests.swift │ ├── RxRealmOnQueueTests.swift │ ├── RxRealmRealmTests.swift │ ├── RxRealmResultsTests.swift │ ├── RxRealmTests.swift │ ├── RxRealmWriteSinks.swift │ ├── TestModels.swift │ └── XCTestManifests.swift ├── assets └── animatedChanges.gif ├── cleanup.sh ├── pods.sh ├── project-carthage.yml ├── project-spm.yml └── scripts ├── bump-version.sh ├── carthage.sh └── xcframeworks.sh /.github/workflows/create-release.yml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | build: 9 | if: "!contains(github.event.head_commit.message, 'skip ci')" 10 | runs-on: macos-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Fetch all tags 18 | run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 19 | 20 | - name: Bump version 21 | run: | 22 | chmod +x ./scripts/bump-version.sh 23 | ./scripts/bump-version.sh 24 | 25 | # - name: Changelog 26 | # uses: scottbrenner/generate-changelog-action@master 27 | # id: Changelog 28 | # env: 29 | # REPO: ${{ github.repository }} 30 | 31 | - name: Push changes 32 | uses: ad-m/github-push-action@master 33 | with: 34 | github_token: ${{ secrets.GITHUB_TOKEN }} 35 | branch: ${{ github.ref }} 36 | 37 | - name: Restore SPM Cache 38 | uses: actions/cache@v1 39 | with: 40 | path: .build 41 | key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} 42 | restore-keys: | 43 | ${{ runner.os }}-spm- 44 | 45 | - name: Release XCFrameworks 46 | run: | 47 | chmod +x ./scripts/xcframeworks.sh 48 | ./scripts/xcframeworks.sh 49 | 50 | - name: Create Release 51 | id: create_release 52 | uses: actions/create-release@v1 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | with: 56 | tag_name: ${{ env.RELEASE_VERSION }} 57 | release_name: RxRealm ${{ env.RELEASE_VERSION }} 58 | # body: | 59 | # ${{ steps.Changelog.outputs.changelog }} 60 | draft: false 61 | prerelease: false 62 | 63 | - name: Upload XCFramework 64 | uses: actions/upload-release-asset@v1 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | with: 68 | upload_url: ${{ steps.create_release.outputs.upload_url }} 69 | asset_path: ./RxRealm.xcframework.zip 70 | asset_name: RxRealm.xcframework.zip 71 | asset_content_type: application/zip 72 | 73 | - name: Deploy to Cocoapods 74 | run: | 75 | set -eo pipefail 76 | export RELEASE_VERSION="$(git describe --abbrev=0 | tr -d '\n')" 77 | RELEASE_VERSION=${RELEASE_VERSION:1} 78 | pod lib lint --allow-warnings 79 | pod trunk push --allow-warnings 80 | env: 81 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} 82 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Lint, build and test 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | jobs: 8 | build: 9 | runs-on: macos-latest 10 | 11 | steps: 12 | - uses: maxim-lobanov/setup-xcode@v1 13 | with: 14 | xcode-version: latest-stable 15 | 16 | - uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Fetch all tags 21 | run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 22 | 23 | - name: Resolve dependencies 24 | run: | 25 | brew bundle 26 | bundle 27 | 28 | # - name: Danger 29 | # run: | 30 | # bundle exec danger 31 | # env: 32 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | - name: Restore SPM Cache 35 | uses: actions/cache@v1 36 | with: 37 | path: .build 38 | key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} 39 | restore-keys: | 40 | ${{ runner.os }}-spm- 41 | 42 | - name: Build and test (SPM) 43 | run: | 44 | swift build 45 | swift test 46 | 47 | - name: Build Cocoapods iOS Demo 48 | run: | 49 | set -eo pipefail 50 | cd Examples 51 | xcodegen 52 | pod install --repo-update 53 | xcodebuild build -scheme 'RxRealmDemo-iOS' -workspace 'RxRealmDemo.xcworkspace' -sdk iphonesimulator -destination "platform=iOS simulator,name=iPhone 11" 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.swp 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | *.xcscmblueprint 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | Carthage/Checkouts 43 | Carthage/Build 44 | 45 | # Swift Package Manager 46 | .build 47 | Package.resolved 48 | 49 | .DS_Store 50 | **/.idea/** 51 | 52 | RxRealm-SPM.xcodeproj -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | output: ../docs/RxRealm 2 | module: RxRealm 3 | xcodebuild_arguments: 4 | - -scheme 5 | - RxRealm iOS -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --allman false 2 | --binarygrouping none 3 | --commas inline 4 | --comments ignore 5 | --decimalgrouping 3,6 6 | --elseposition same-line 7 | --empty void 8 | --exponentcase lowercase 9 | --exponentgrouping disabled 10 | --fractiongrouping disabled 11 | --disable blankLinesAroundMark 12 | --header ignore 13 | --hexgrouping 4,8 14 | --hexliteralcase uppercase 15 | --patternlet hoist 16 | --ifdef indent 17 | --indent 2 18 | --indentcase false 19 | --linebreaks lf 20 | --octalgrouping none 21 | --operatorfunc nospace 22 | --patternlet hoist 23 | --ranges nospace 24 | --self remove 25 | --semicolons inline 26 | --stripunusedargs closure-only 27 | --trimwhitespace always 28 | --wraparguments after-first 29 | --wrapcollections before-first 30 | --enable blankLinesBetweenScopes 31 | --enable redundantVoidReturnType 32 | --enable strongifiedSelf 33 | --enable trailingClosures 34 | --enable trailingSpace 35 | --enable typeSugar 36 | --enable spaceInsideParens 37 | --enable spaceInsideBrackets 38 | --enable spaceInsideBraces 39 | --enable spaceAroundOperators 40 | --enable spaceAroundBraces 41 | --enable redundantParens 42 | --enable redundantNilInit 43 | --enable consecutiveSpaces 44 | --enable anyObjectProtocol 45 | --disable isEmpty -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | - trailing_comma 4 | - large_tuple 5 | - nesting 6 | - todo 7 | - operator_whitespace 8 | - colon 9 | - identifier_name 10 | 11 | opt_in_rules: # some rules are only opt-in 12 | - empty_count 13 | - yoda_condition 14 | # Find all the available rules by running: 15 | # swiftlint rules 16 | 17 | included: 18 | - Sources 19 | - Tests 20 | - Examples/RxRealmDemo-iOS 21 | 22 | # excluded: # paths to ignore during linting. Takes precedence over `included`. 23 | 24 | # configurable rules can be customized from this configuration file 25 | # binary rules can set their severity level 26 | force_cast: warning # implicitly 27 | force_try: 28 | severity: warning # explicitly 29 | # rules that have both warning and error levels, can set just the warning level 30 | # implicitly 31 | line_length: 180 32 | # they can set both implicitly with an array 33 | function_body_length: 120 34 | type_body_length: 35 | - 400 # warning 36 | - 500 # error 37 | # or they can set both explicitly 38 | file_length: 39 | warning: 500 40 | error: 1200 41 | # naming rules can set warnings/errors for min_length and max_length 42 | # additionally they can set excluded names 43 | generic_type_name: 44 | min_length: 1 # only warning 45 | max_length: 30 46 | 47 | type_name: 48 | min_length: 2 # only warning 49 | max_length: # warning and error 50 | warning: 100 51 | error: 120 52 | excluded: iPhone # excluded via string 53 | identifier_name: 54 | min_length: 55 | warning: 1 56 | reporter: "xcode" 57 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.versionrc: -------------------------------------------------------------------------------- 1 | { 2 | "releaseCommitMessageFormat": "chore(release): {{currentTag}} [skip ci]", 3 | "compareUrlFormat": "{{host}}/{{owner}}/{{repository}}/branches/compare/{{currentTag}}%0D{{previousTag}}", 4 | "commitUrlFormat": "{{host}}/{{owner}}/{{repository}}/commits/{{hash}}" 5 | } 6 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | brew "xcodegen" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [5.0.8](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.8%0Dv5.0.7) (2024-05-07) 6 | 7 | ### [5.0.6](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.6%0Dv5.0.5) (2023-10-29) 8 | 9 | ### 5.0.5 (2022-03-09) 10 | 11 | ## [5.0.8](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.8%0Dv5.0.7) (2024-05-07) 12 | 13 | - Merge pull request #210 from trmquang93/main; override namespace Observable 14 | 15 | ## [5.0.7](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.7%0Dv5.0.6) (2024-03-08) 16 | 17 | ### Updates 18 | - Merge pull request #209 from markusfi/main; update RxRealm.podspec: Realm dependency updated to 10.44 19 | 20 | ## [5.0.6](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.6%0Dv5.0.5) (2023-10-29) 21 | 22 | ### Bug Fixes 23 | 24 | * ci ([66e43ab](https://github.com/RxSwiftCommunity/RxRealm/commits/66e43ab63448892cbef60d11468614795582a3e2)) 25 | 26 | ## [5.0.5](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.5%0Dv5.0.4) (2022-03-09) 27 | 28 | ### Bug Fixes 29 | 30 | * replacing rx attribute of Realm type by standard Reactive extension. ([dabb105](https://github.com/RxSwiftCommunity/RxRealm/commits/dabb105c94a6c0fdccbf99bb618b618be3f0d5c0)) 31 | * update Realm and RxSwift to latest version ([#178](https://github.com/RxSwiftCommunity/RxRealm/issues/178)) ([63d00c6](https://github.com/RxSwiftCommunity/RxRealm/commits/63d00c6349b31d4e26fd5b5a5e428400b6b91356)) 32 | 33 | ## [5.0.4](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.4%0Dv5.0.3) (2022-01-16) 34 | 35 | ### Bug Fixes 36 | 37 | * replacing rx attribute of Realm type by standard Reactive extension. ([dabb105](https://github.com/RxSwiftCommunity/RxRealm/commits/dabb105c94a6c0fdccbf99bb618b618be3f0d5c0)) 38 | 39 | ## [5.0.3](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.3%0Dv5.0.2) (2021-09-05) 40 | 41 | ## [5.0.2](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.2%0Dv5.0.1) (2021-05-10) 42 | 43 | ### Bug Fixes 44 | 45 | * Brew before generate project ([7eef1d1](https://github.com/RxSwiftCommunity/RxRealm/commits/7eef1d1ba130627af7d140682f912c89830cf1eb)) 46 | * Bump version script fix ([8f9f99f](https://github.com/RxSwiftCommunity/RxRealm/commits/8f9f99f4680a07cbeedd63cfb7a26e269b907bf9)) 47 | 48 | ## [5.0.1](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.1%0Dv5.0.0) (2021-01-04) 49 | 50 | ### Bug Fixes 51 | 52 | * Bump version script fix ([8f9f99f](https://github.com/RxSwiftCommunity/RxRealm/commits/8f9f99f4680a07cbeedd63cfb7a26e269b907bf9)) 53 | 54 | ### Bug Fixes 55 | 56 | * Bump version script fix ([8f9f99f](https://github.com/RxSwiftCommunity/RxRealm/commits/8f9f99f4680a07cbeedd63cfb7a26e269b907bf9)) 57 | 58 | ## [5.0.0](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v5.0.0%0Dv4.0.3) (2021-01-03) 59 | 60 | ### ⚠ BREAKING CHANGES 61 | 62 | * RxSwift 6.0.0 support 63 | 64 | ### Features 65 | 66 | * Supports xcframework as per release ([09c2a93](https://github.com/RxSwiftCommunity/RxRealm/commits/09c2a93da5e0ca43fa323e2c76f356d07320069d)) 67 | 68 | ### Bug Fixes 69 | 70 | * Podspec version ([5c750e7](https://github.com/RxSwiftCommunity/RxRealm/commits/5c750e788284f54b981eaf2d7af5c3f1d61600a8)) 71 | 72 | ## [4.0.3](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v4.0.3%0Dv4.0.2) (2020-12-20) 73 | 74 | ## [4.0.2](https://github.com/RxSwiftCommunity/RxRealm/branches/compare/v4.0.2%0Dv4.0.1) (2020-12-05) 75 | 76 | ## 4.0.1 (2020-12-05) 77 | 78 | Changelog 79 | ========= 80 | 81 | 3.1.0 82 | _____ 83 | 84 | - Bump minimum iOS version to 9.0 to sync with realm-cocoa 85 | 86 | 3.0.0 87 | _____ 88 | 89 | - added support for Realm 5 90 | - added `queue` optional parameter to `changeset`, `array` and `collection` to specify realm notifications queue 91 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "realm/realm-cocoa" ~> 10.21 2 | github "ReactiveX/RxSwift" ~> 6.1 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "ReactiveX/RxSwift" "6.5.0" 2 | github "realm/realm-cocoa" "v10.21.1" 3 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Sometimes it's a README fix, or something like that - which isn't relevant for 2 | # including in a project's CHANGELOG for example 3 | declared_trivial = (github.pr_title + github.pr_body).include?("#trivial") 4 | 5 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet 6 | warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]" 7 | 8 | # Warn summary on pull request 9 | if github.pr_body.length < 5 10 | warn "Please provide a summary in the Pull Request description" 11 | end 12 | 13 | # If these are all empty something has gone wrong, better to raise it in a comment 14 | if git.modified_files.empty? && git.added_files.empty? && git.deleted_files.empty? 15 | fail "This PR has no changes at all, this is likely a developer issue." 16 | end 17 | 18 | # Warn when there is a big PR 19 | message("Big PR") if git.lines_of_code > 500 20 | warn("Huge PR") if git.lines_of_code > 1000 21 | fail("Enormous PR. Please split this pull request.") if git.lines_of_code > 3000 22 | 23 | # Warn Cartfile changes 24 | message("Cartfile changed") if git.modified_files.include?("Cartfile") 25 | message("Cartfile.resolved changed") if git.modified_files.include?("Cartfile.resolved") 26 | 27 | # Lint 28 | swiftlint.lint_all_files = true 29 | swiftlint.max_num_violations = 20 30 | swiftlint.config_file = ".swiftlint.yml" 31 | swiftlint.lint_files fail_on_error: true 32 | 33 | # xcov.report( 34 | # scheme: "RxRealm iOS", 35 | # project: "RxRealm.xcodeproj", 36 | # include_targets: "RxRealm.framework", 37 | # minimum_coverage_percentage: 20.0, 38 | # derived_data_path: "Build/", 39 | # ) 40 | -------------------------------------------------------------------------------- /Examples/Podfile: -------------------------------------------------------------------------------- 1 | source "https://cdn.cocoapods.org/" 2 | project "RxRealmDemo.xcodeproj" 3 | use_frameworks! 4 | 5 | def common 6 | pod "RxRealm", :path => "../" 7 | end 8 | 9 | def common_tests 10 | pod "RxBlocking", "~> 6.0" 11 | end 12 | 13 | target "RxRealmDemo-iOS" do 14 | platform :ios, "12.0" 15 | common 16 | end 17 | 18 | # target "RxRealmDemo-macOS" do 19 | # platform :osx, "10.12" 20 | # common 21 | # end 22 | 23 | # target "RxRealmDemo-tvOS" do 24 | # platform :tvos, "9.0" 25 | # common 26 | # end 27 | 28 | post_install do |installer| 29 | installer.pods_project.targets.each do |target| 30 | target.build_configurations.each do |config| 31 | if Gem::Version.new('12.0') > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET']) 32 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /Examples/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Realm (10.50.0): 3 | - Realm/Headers (= 10.50.0) 4 | - Realm/Headers (10.50.0) 5 | - RealmSwift (10.50.0): 6 | - Realm (= 10.50.0) 7 | - RxCocoa (6.7.1): 8 | - RxRelay (= 6.7.1) 9 | - RxSwift (= 6.7.1) 10 | - RxRealm (5.0.8): 11 | - Realm (~> 10.50) 12 | - RealmSwift (~> 10.50) 13 | - RxCocoa (~> 6.1) 14 | - RxSwift (~> 6.1) 15 | - RxRelay (6.7.1): 16 | - RxSwift (= 6.7.1) 17 | - RxSwift (6.7.1) 18 | 19 | DEPENDENCIES: 20 | - RxRealm (from `../`) 21 | 22 | SPEC REPOS: 23 | trunk: 24 | - Realm 25 | - RealmSwift 26 | - RxCocoa 27 | - RxRelay 28 | - RxSwift 29 | 30 | EXTERNAL SOURCES: 31 | RxRealm: 32 | :path: "../" 33 | 34 | SPEC CHECKSUMS: 35 | Realm: a0a6a99e3dfa8959358a95c3c05845b5e5d6af30 36 | RealmSwift: fa111fb98e092a4dff1d0785ad0c3483feda607a 37 | RxCocoa: f5609cb4637587a7faa99c5d5787e3ad582b75a4 38 | RxRealm: 4858ba414f07514e38657d85807479bae6d0b9a9 39 | RxRelay: 4151ba01152436b08271e08410135e099880eae5 40 | RxSwift: b9a93a26031785159e11abd40d1a55bcb8057e52 41 | 42 | PODFILE CHECKSUM: df29b255dac1d305deddb3b085a7407b288544fc 43 | 44 | COCOAPODS: 1.14.0 45 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo-iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import UIKit 3 | 4 | let formatter: DateFormatter = { 5 | let f = DateFormatter() 6 | f.timeStyle = .long 7 | return f 8 | }() 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | var window: UIWindow? 13 | 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 15 | // reset the realm on each app launch 16 | let realm = try! Realm() 17 | try! realm.write { 18 | realm.deleteAll() 19 | } 20 | 21 | return true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo-iOS/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo-iOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo-iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /Examples/RxRealmDemo-iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo-iOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import RxCocoa 3 | import RxRealm 4 | import RxSwift 5 | import UIKit 6 | 7 | // realm model 8 | class Lap: Object { 9 | @objc dynamic var time: TimeInterval = Date().timeIntervalSinceReferenceDate 10 | } 11 | 12 | class TickCounter: Object { 13 | @objc dynamic var id = UUID().uuidString 14 | @objc dynamic var ticks: Int = 0 15 | override static func primaryKey() -> String? { return "id" } 16 | } 17 | 18 | // view controller 19 | class ViewController: UIViewController { 20 | let bag = DisposeBag() 21 | 22 | @IBOutlet var tableView: UITableView! 23 | @IBOutlet var tickItemButton: UIBarButtonItem! 24 | @IBOutlet var addTwoItemsButton: UIBarButtonItem! 25 | 26 | var laps: Results! 27 | 28 | let footer: UILabel = { 29 | let l = UILabel() 30 | l.textAlignment = .center 31 | return l 32 | }() 33 | 34 | lazy var ticker: TickCounter = { 35 | let realm = try! Realm() 36 | let ticker = TickCounter() 37 | try! realm.write { 38 | realm.add(ticker) 39 | } 40 | return ticker 41 | }() 42 | 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | let realm = try! Realm() 47 | laps = realm.objects(Lap.self).sorted(byKeyPath: "time", ascending: false) 48 | 49 | /* 50 | Observable> - wrap Results as observable 51 | */ 52 | Observable.collection(from: laps) 53 | .map { results in "laps: \(results.count)" } 54 | .subscribe { event in 55 | self.title = event.element 56 | } 57 | .disposed(by: bag) 58 | 59 | /* 60 | Observable> - reacting to change sets 61 | */ 62 | Observable.changeset(from: laps) 63 | .subscribe(onNext: { [unowned self] _, changes in 64 | if let changes = changes { 65 | self.tableView.applyChangeset(changes) 66 | } else { 67 | self.tableView.reloadData() 68 | } 69 | }) 70 | .disposed(by: bag) 71 | 72 | /* 73 | Use bindable sink to add objects 74 | */ 75 | addTwoItemsButton.rx.tap 76 | .map { [Lap(), Lap()] } 77 | .bind(to: Realm.rx.add(onError: { elements, error in 78 | if let elements = elements { 79 | print("Error \(error.localizedDescription) while saving objects \(String(describing: elements))") 80 | } else { 81 | print("Error \(error.localizedDescription) while opening realm.") 82 | } 83 | })) 84 | .disposed(by: bag) 85 | 86 | /* 87 | Bind bar item to increasing the ticker 88 | */ 89 | tickItemButton.rx.tap 90 | .subscribe(onNext: { [unowned self] _ in 91 | try! realm.write { 92 | self.ticker.ticks += 1 93 | } 94 | }) 95 | .disposed(by: bag) 96 | 97 | /* 98 | Observing a single object 99 | */ 100 | let tickerChanges$ = Observable.propertyChanges(object: ticker) 101 | tickerChanges$ 102 | .filter { $0.name == "ticks" } 103 | .map { "\($0.newValue!) ticks" } 104 | .bind(to: footer.rx.text) 105 | .disposed(by: bag) 106 | } 107 | } 108 | 109 | extension ViewController: UITableViewDataSource { 110 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 111 | return laps.count 112 | } 113 | 114 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 115 | let lap = laps[indexPath.row] 116 | 117 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")! 118 | cell.textLabel?.text = formatter.string(from: Date(timeIntervalSinceReferenceDate: lap.time)) 119 | return cell 120 | } 121 | 122 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 123 | return "Delete objects by tapping them, add ticks to trigger a footer update" 124 | } 125 | } 126 | 127 | extension ViewController: UITableViewDelegate { 128 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 129 | Observable.from([laps[indexPath.row]]) 130 | .subscribe(Realm.rx.delete()) 131 | .disposed(by: bag) 132 | } 133 | 134 | func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { 135 | return footer 136 | } 137 | } 138 | 139 | extension UITableView { 140 | func applyChangeset(_ changes: RealmChangeset) { 141 | beginUpdates() 142 | deleteRows(at: changes.deleted.map { IndexPath(row: $0, section: 0) }, with: .automatic) 143 | insertRows(at: changes.inserted.map { IndexPath(row: $0, section: 0) }, with: .automatic) 144 | reloadRows(at: changes.updated.map { IndexPath(row: $0, section: 0) }, with: .automatic) 145 | endUpdates() 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 025AFE3743CE73F806425BAF /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 26DE8661C8721C6102A4F087 /* LaunchScreen.xib */; }; 11 | 13BD54D2BB0EBD66F4F2DADC /* Pods_RxRealmDemo_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 562FE4AD26713C0C4DEF9226 /* Pods_RxRealmDemo_iOS.framework */; }; 12 | 3BB28BD3D65B1369D1A64960 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54597D6B66FBF23FCBF1A7DB /* Images.xcassets */; }; 13 | 84E694181E51B3B4646D1225 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49505648CF8974BEF397F106 /* Main.storyboard */; }; 14 | 8E1DB46DA84AA944224297EC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18CF27BE5242F49EDEA89577 /* ViewController.swift */; }; 15 | DAEB28CFA861A50143F84323 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6298C0EF6C7C86E33743B8B5 /* AppDelegate.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 18CF27BE5242F49EDEA89577 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 20 | 5050BD06022D9BA067993ACD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 21 | 54597D6B66FBF23FCBF1A7DB /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 22 | 562FE4AD26713C0C4DEF9226 /* Pods_RxRealmDemo_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxRealmDemo_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 6298C0EF6C7C86E33743B8B5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | 645D257F7774284AE08348E5 /* RxRealmDemo-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RxRealmDemo-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 82BCDF2341D78059DF2C89F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 26 | C430B976866CC45900487D76 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | C5A5D55D5CC50F77DE071B83 /* Pods-RxRealmDemo-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxRealmDemo-iOS.debug.xcconfig"; path = "Target Support Files/Pods-RxRealmDemo-iOS/Pods-RxRealmDemo-iOS.debug.xcconfig"; sourceTree = ""; }; 28 | E7759D109C2ABA35C4C5A7ED /* Pods-RxRealmDemo-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxRealmDemo-iOS.release.xcconfig"; path = "Target Support Files/Pods-RxRealmDemo-iOS/Pods-RxRealmDemo-iOS.release.xcconfig"; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | 0452B0E845B69604F8EFBE7C /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | 13BD54D2BB0EBD66F4F2DADC /* Pods_RxRealmDemo_iOS.framework in Frameworks */, 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 4A214DAA98C5247C1C84689A /* RxRealmDemo-iOS */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | 6298C0EF6C7C86E33743B8B5 /* AppDelegate.swift */, 47 | 54597D6B66FBF23FCBF1A7DB /* Images.xcassets */, 48 | 82BCDF2341D78059DF2C89F2 /* Info.plist */, 49 | 26DE8661C8721C6102A4F087 /* LaunchScreen.xib */, 50 | 49505648CF8974BEF397F106 /* Main.storyboard */, 51 | 18CF27BE5242F49EDEA89577 /* ViewController.swift */, 52 | ); 53 | path = "RxRealmDemo-iOS"; 54 | sourceTree = ""; 55 | }; 56 | 6531959FA0DFC568430ACAAB /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | 645D257F7774284AE08348E5 /* RxRealmDemo-iOS.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | A6E8CE38563E4EA24A16F2CD = { 65 | isa = PBXGroup; 66 | children = ( 67 | 4A214DAA98C5247C1C84689A /* RxRealmDemo-iOS */, 68 | 6531959FA0DFC568430ACAAB /* Products */, 69 | C0D0418E03D8CC5CCBCA74A2 /* Pods */, 70 | CFCCE2C0EDE6F7ECC0D0B468 /* Frameworks */, 71 | ); 72 | indentWidth = 2; 73 | sourceTree = ""; 74 | tabWidth = 2; 75 | usesTabs = 0; 76 | }; 77 | C0D0418E03D8CC5CCBCA74A2 /* Pods */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | C5A5D55D5CC50F77DE071B83 /* Pods-RxRealmDemo-iOS.debug.xcconfig */, 81 | E7759D109C2ABA35C4C5A7ED /* Pods-RxRealmDemo-iOS.release.xcconfig */, 82 | ); 83 | path = Pods; 84 | sourceTree = ""; 85 | }; 86 | CFCCE2C0EDE6F7ECC0D0B468 /* Frameworks */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 562FE4AD26713C0C4DEF9226 /* Pods_RxRealmDemo_iOS.framework */, 90 | ); 91 | name = Frameworks; 92 | sourceTree = ""; 93 | }; 94 | /* End PBXGroup section */ 95 | 96 | /* Begin PBXNativeTarget section */ 97 | 7063BECAEC79C49007CE0BA3 /* RxRealmDemo-iOS */ = { 98 | isa = PBXNativeTarget; 99 | buildConfigurationList = 960759C5E7AEB6ABF2CBF1A5 /* Build configuration list for PBXNativeTarget "RxRealmDemo-iOS" */; 100 | buildPhases = ( 101 | 9E09E32F7F593B502A0A9650 /* [CP] Check Pods Manifest.lock */, 102 | 9F070CB36A79D85BB2876A0D /* Sources */, 103 | 5C6A37C6E8CA2E27A0ECA6AA /* Resources */, 104 | 0452B0E845B69604F8EFBE7C /* Frameworks */, 105 | 21A0E7521E409A56D88F9065 /* [CP] Embed Pods Frameworks */, 106 | ); 107 | buildRules = ( 108 | ); 109 | dependencies = ( 110 | ); 111 | name = "RxRealmDemo-iOS"; 112 | productName = "RxRealmDemo-iOS"; 113 | productReference = 645D257F7774284AE08348E5 /* RxRealmDemo-iOS.app */; 114 | productType = "com.apple.product-type.application"; 115 | }; 116 | /* End PBXNativeTarget section */ 117 | 118 | /* Begin PBXProject section */ 119 | F0843B327272B2D17F77F290 /* Project object */ = { 120 | isa = PBXProject; 121 | attributes = { 122 | LastUpgradeCheck = 1220; 123 | ORGANIZATIONNAME = RxSwiftCommunity; 124 | }; 125 | buildConfigurationList = 94914E22EBC7A74B395EF186 /* Build configuration list for PBXProject "RxRealmDemo" */; 126 | compatibilityVersion = "Xcode 11.0"; 127 | developmentRegion = en; 128 | hasScannedForEncodings = 0; 129 | knownRegions = ( 130 | Base, 131 | en, 132 | ); 133 | mainGroup = A6E8CE38563E4EA24A16F2CD; 134 | productRefGroup = 6531959FA0DFC568430ACAAB /* Products */; 135 | projectDirPath = ""; 136 | projectRoot = ""; 137 | targets = ( 138 | 7063BECAEC79C49007CE0BA3 /* RxRealmDemo-iOS */, 139 | ); 140 | }; 141 | /* End PBXProject section */ 142 | 143 | /* Begin PBXResourcesBuildPhase section */ 144 | 5C6A37C6E8CA2E27A0ECA6AA /* Resources */ = { 145 | isa = PBXResourcesBuildPhase; 146 | buildActionMask = 2147483647; 147 | files = ( 148 | 3BB28BD3D65B1369D1A64960 /* Images.xcassets in Resources */, 149 | 025AFE3743CE73F806425BAF /* LaunchScreen.xib in Resources */, 150 | 84E694181E51B3B4646D1225 /* Main.storyboard in Resources */, 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | }; 154 | /* End PBXResourcesBuildPhase section */ 155 | 156 | /* Begin PBXShellScriptBuildPhase section */ 157 | 21A0E7521E409A56D88F9065 /* [CP] Embed Pods Frameworks */ = { 158 | isa = PBXShellScriptBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | ); 162 | inputFileListPaths = ( 163 | "${PODS_ROOT}/Target Support Files/Pods-RxRealmDemo-iOS/Pods-RxRealmDemo-iOS-frameworks-${CONFIGURATION}-input-files.xcfilelist", 164 | ); 165 | name = "[CP] Embed Pods Frameworks"; 166 | outputFileListPaths = ( 167 | "${PODS_ROOT}/Target Support Files/Pods-RxRealmDemo-iOS/Pods-RxRealmDemo-iOS-frameworks-${CONFIGURATION}-output-files.xcfilelist", 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | shellPath = /bin/sh; 171 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RxRealmDemo-iOS/Pods-RxRealmDemo-iOS-frameworks.sh\"\n"; 172 | showEnvVarsInLog = 0; 173 | }; 174 | 9E09E32F7F593B502A0A9650 /* [CP] Check Pods Manifest.lock */ = { 175 | isa = PBXShellScriptBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | ); 179 | inputFileListPaths = ( 180 | ); 181 | inputPaths = ( 182 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 183 | "${PODS_ROOT}/Manifest.lock", 184 | ); 185 | name = "[CP] Check Pods Manifest.lock"; 186 | outputFileListPaths = ( 187 | ); 188 | outputPaths = ( 189 | "$(DERIVED_FILE_DIR)/Pods-RxRealmDemo-iOS-checkManifestLockResult.txt", 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | shellPath = /bin/sh; 193 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 194 | showEnvVarsInLog = 0; 195 | }; 196 | /* End PBXShellScriptBuildPhase section */ 197 | 198 | /* Begin PBXSourcesBuildPhase section */ 199 | 9F070CB36A79D85BB2876A0D /* Sources */ = { 200 | isa = PBXSourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | DAEB28CFA861A50143F84323 /* AppDelegate.swift in Sources */, 204 | 8E1DB46DA84AA944224297EC /* ViewController.swift in Sources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXSourcesBuildPhase section */ 209 | 210 | /* Begin PBXVariantGroup section */ 211 | 26DE8661C8721C6102A4F087 /* LaunchScreen.xib */ = { 212 | isa = PBXVariantGroup; 213 | children = ( 214 | 5050BD06022D9BA067993ACD /* Base */, 215 | ); 216 | name = LaunchScreen.xib; 217 | sourceTree = ""; 218 | }; 219 | 49505648CF8974BEF397F106 /* Main.storyboard */ = { 220 | isa = PBXVariantGroup; 221 | children = ( 222 | C430B976866CC45900487D76 /* Base */, 223 | ); 224 | name = Main.storyboard; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXVariantGroup section */ 228 | 229 | /* Begin XCBuildConfiguration section */ 230 | 0FCE882B186F10CBD63A5EF1 /* Debug */ = { 231 | isa = XCBuildConfiguration; 232 | baseConfigurationReference = C5A5D55D5CC50F77DE071B83 /* Pods-RxRealmDemo-iOS.debug.xcconfig */; 233 | buildSettings = { 234 | ASSETCATALOG_COMPILER_APPICON_NAME = "$(APP_ICON_NAME)"; 235 | CODE_SIGN_IDENTITY = "iPhone Developer"; 236 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 237 | INFOPLIST_FILE = "RxRealmDemo-iOS/Info.plist"; 238 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 239 | LD_RUNPATH_SEARCH_PATHS = ( 240 | "$(inherited)", 241 | "@executable_path/Frameworks", 242 | ); 243 | OTHER_LDFLAGS = "-ObjC"; 244 | PRODUCT_BUNDLE_IDENTIFIER = RxSwiftCommunity.RxRealm.iOSDemo; 245 | PRODUCT_NAME = "RxRealmDemo-iOS"; 246 | SDKROOT = iphoneos; 247 | TARGETED_DEVICE_FAMILY = "1,2"; 248 | }; 249 | name = Debug; 250 | }; 251 | 6628CAC057A227BFABA81B75 /* Debug */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | ALWAYS_SEARCH_USER_PATHS = NO; 255 | CLANG_ANALYZER_NONNULL = YES; 256 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 258 | CLANG_CXX_LIBRARY = "libc++"; 259 | CLANG_ENABLE_MODULES = YES; 260 | CLANG_ENABLE_OBJC_ARC = YES; 261 | CLANG_ENABLE_OBJC_WEAK = YES; 262 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_COMMA = YES; 265 | CLANG_WARN_CONSTANT_CONVERSION = YES; 266 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 267 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 268 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 269 | CLANG_WARN_EMPTY_BODY = YES; 270 | CLANG_WARN_ENUM_CONVERSION = YES; 271 | CLANG_WARN_INFINITE_RECURSION = YES; 272 | CLANG_WARN_INT_CONVERSION = YES; 273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 277 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 279 | CLANG_WARN_STRICT_PROTOTYPES = YES; 280 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 281 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 282 | CLANG_WARN_UNREACHABLE_CODE = YES; 283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 284 | COPY_PHASE_STRIP = NO; 285 | DEBUG_INFORMATION_FORMAT = dwarf; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | ENABLE_TESTABILITY = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu11; 289 | GCC_DYNAMIC_NO_PIC = NO; 290 | GCC_NO_COMMON_BLOCKS = YES; 291 | GCC_OPTIMIZATION_LEVEL = 0; 292 | GCC_PREPROCESSOR_DEFINITIONS = ( 293 | "$(inherited)", 294 | "DEBUG=1", 295 | ); 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 303 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 304 | MTL_FAST_MATH = YES; 305 | ONLY_ACTIVE_ARCH = YES; 306 | PRODUCT_NAME = "$(TARGET_NAME)"; 307 | SDKROOT = iphoneos; 308 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 309 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 310 | SWIFT_VERSION = 5.0; 311 | }; 312 | name = Debug; 313 | }; 314 | 84000E9CA8521983711240B4 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | baseConfigurationReference = E7759D109C2ABA35C4C5A7ED /* Pods-RxRealmDemo-iOS.release.xcconfig */; 317 | buildSettings = { 318 | ASSETCATALOG_COMPILER_APPICON_NAME = "$(APP_ICON_NAME)"; 319 | CODE_SIGN_IDENTITY = "iPhone Developer"; 320 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 321 | INFOPLIST_FILE = "RxRealmDemo-iOS/Info.plist"; 322 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 323 | LD_RUNPATH_SEARCH_PATHS = ( 324 | "$(inherited)", 325 | "@executable_path/Frameworks", 326 | ); 327 | OTHER_LDFLAGS = "-ObjC"; 328 | PRODUCT_BUNDLE_IDENTIFIER = RxSwiftCommunity.RxRealm.iOSDemo; 329 | PRODUCT_NAME = "RxRealmDemo-iOS"; 330 | SDKROOT = iphoneos; 331 | TARGETED_DEVICE_FAMILY = "1,2"; 332 | }; 333 | name = Release; 334 | }; 335 | B540E817A24A570773BA37C0 /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | ALWAYS_SEARCH_USER_PATHS = NO; 339 | CLANG_ANALYZER_NONNULL = YES; 340 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 341 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 342 | CLANG_CXX_LIBRARY = "libc++"; 343 | CLANG_ENABLE_MODULES = YES; 344 | CLANG_ENABLE_OBJC_ARC = YES; 345 | CLANG_ENABLE_OBJC_WEAK = YES; 346 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_COMMA = YES; 349 | CLANG_WARN_CONSTANT_CONVERSION = YES; 350 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 351 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 352 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 353 | CLANG_WARN_EMPTY_BODY = YES; 354 | CLANG_WARN_ENUM_CONVERSION = YES; 355 | CLANG_WARN_INFINITE_RECURSION = YES; 356 | CLANG_WARN_INT_CONVERSION = YES; 357 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 358 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 359 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 361 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 362 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 363 | CLANG_WARN_STRICT_PROTOTYPES = YES; 364 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 365 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 366 | CLANG_WARN_UNREACHABLE_CODE = YES; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | COPY_PHASE_STRIP = NO; 369 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 370 | ENABLE_NS_ASSERTIONS = NO; 371 | ENABLE_STRICT_OBJC_MSGSEND = YES; 372 | GCC_C_LANGUAGE_STANDARD = gnu11; 373 | GCC_NO_COMMON_BLOCKS = YES; 374 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 375 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 376 | GCC_WARN_UNDECLARED_SELECTOR = YES; 377 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 378 | GCC_WARN_UNUSED_FUNCTION = YES; 379 | GCC_WARN_UNUSED_VARIABLE = YES; 380 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 381 | MTL_ENABLE_DEBUG_INFO = NO; 382 | MTL_FAST_MATH = YES; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | SDKROOT = iphoneos; 385 | SWIFT_COMPILATION_MODE = wholemodule; 386 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 387 | SWIFT_VERSION = 5.0; 388 | }; 389 | name = Release; 390 | }; 391 | /* End XCBuildConfiguration section */ 392 | 393 | /* Begin XCConfigurationList section */ 394 | 94914E22EBC7A74B395EF186 /* Build configuration list for PBXProject "RxRealmDemo" */ = { 395 | isa = XCConfigurationList; 396 | buildConfigurations = ( 397 | 6628CAC057A227BFABA81B75 /* Debug */, 398 | B540E817A24A570773BA37C0 /* Release */, 399 | ); 400 | defaultConfigurationIsVisible = 0; 401 | defaultConfigurationName = Release; 402 | }; 403 | 960759C5E7AEB6ABF2CBF1A5 /* Build configuration list for PBXNativeTarget "RxRealmDemo-iOS" */ = { 404 | isa = XCConfigurationList; 405 | buildConfigurations = ( 406 | 0FCE882B186F10CBD63A5EF1 /* Debug */, 407 | 84000E9CA8521983711240B4 /* Release */, 408 | ); 409 | defaultConfigurationIsVisible = 0; 410 | defaultConfigurationName = Release; 411 | }; 412 | /* End XCConfigurationList section */ 413 | }; 414 | rootObject = F0843B327272B2D17F77F290 /* Project object */; 415 | } 416 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo.xcodeproj/xcshareddata/xcschemes/RxRealmDemo-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/RxRealmDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/pod_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | export RELEASE_VERSION="$(git describe --abbrev=0 | tr -d '\n')" 5 | RELEASE_VERSION=${RELEASE_VERSION:1} 6 | xcodegen 7 | pod install --repo-update 8 | open RxRealmDemo.xcworkspace -------------------------------------------------------------------------------- /Examples/project.yml: -------------------------------------------------------------------------------- 1 | name: RxRealmDemo 2 | options: 3 | minimumXcodeGenVersion: "2.15.1" 4 | developmentLanguage: en 5 | usesTabs: false 6 | indentWidth: 2 7 | tabWidth: 2 8 | xcodeVersion: "1220" 9 | deploymentTarget: 10 | iOS: "12.0" 11 | defaultConfig: "Release" 12 | configs: 13 | Debug: debug 14 | Release: release 15 | attributes: 16 | ORGANIZATIONNAME: RxSwiftCommunity 17 | schemes: 18 | RxRealmDemo-iOS: 19 | scheme: {} 20 | build: 21 | parallelizeBuild: true 22 | buildImplicitDependencies: true 23 | targets: 24 | RxRealmDemo-iOS: all 25 | run: 26 | config: Debug 27 | profile: 28 | config: Release 29 | analyze: 30 | config: Debug 31 | archive: 32 | config: Release 33 | revealArchiveInOrganizer: true 34 | # RxRealmDemo-tvOS: 35 | # scheme: {} 36 | # build: 37 | # parallelizeBuild: true 38 | # buildImplicitDependencies: true 39 | # targets: 40 | # RxRealmDemo-tvOS: all 41 | # run: 42 | # config: Debug 43 | # profile: 44 | # config: Release 45 | # analyze: 46 | # config: Debug 47 | # archive: 48 | # config: Release 49 | # revealArchiveInOrganizer: true 50 | targets: 51 | RxRealmDemo-iOS: 52 | type: application 53 | platform: iOS 54 | sources: [RxRealmDemo-iOS] 55 | settings: 56 | INFOPLIST_FILE: RxRealmDemo-iOS/Info.plist 57 | PRODUCT_NAME: RxRealmDemo-iOS 58 | OTHER_LDFLAGS: -ObjC 59 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.iOSDemo 60 | ASSETCATALOG_COMPILER_APPICON_NAME: $(APP_ICON_NAME) 61 | DEBUG_INFORMATION_FORMAT: dwarf-with-dsym 62 | # RxRealmDemo-tvOS: 63 | # type: application 64 | # platform: tvOS 65 | # deploymentTarget: "10.0" 66 | # sources: [RxRealmDemo-tvOS] 67 | # settings: 68 | # INFOPLIST_FILE: RxRealmDemo-tvOS/Info.plist 69 | # PRODUCT_NAME: RxRealmDemo-tvOS 70 | # OTHER_LDFLAGS: -ObjC 71 | # PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.tvOSDemo 72 | # ASSETCATALOG_COMPILER_APPICON_NAME: $(APP_ICON_NAME) 73 | # DEBUG_INFORMATION_FORMAT: dwarf-with-dsym -------------------------------------------------------------------------------- /File: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxRealm/f64bf1c4b07612185eb0d4935b4ef9cf06679357/File -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods', '~> 1.14' 4 | gem 'danger', '~> 8.0' 5 | gem 'danger-swiftlint', '~> 0.20' 6 | gem 'jazzy', '~> 0.13' 7 | gem 'xcov', '~> 1.7.3' 8 | gem 'danger-xcov', '~> 0.5' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.6) 5 | rexml 6 | activesupport (7.1.1) 7 | base64 8 | bigdecimal 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | connection_pool (>= 2.2.5) 11 | drb 12 | i18n (>= 1.6, < 2) 13 | minitest (>= 5.1) 14 | mutex_m 15 | tzinfo (~> 2.0) 16 | addressable (2.8.5) 17 | public_suffix (>= 2.0.2, < 6.0) 18 | algoliasearch (1.27.5) 19 | httpclient (~> 2.8, >= 2.8.3) 20 | json (>= 1.5.1) 21 | artifactory (3.0.15) 22 | atomos (0.1.3) 23 | aws-eventstream (1.2.0) 24 | aws-partitions (1.834.0) 25 | aws-sdk-core (3.185.1) 26 | aws-eventstream (~> 1, >= 1.0.2) 27 | aws-partitions (~> 1, >= 1.651.0) 28 | aws-sigv4 (~> 1.5) 29 | jmespath (~> 1, >= 1.6.1) 30 | aws-sdk-kms (1.72.0) 31 | aws-sdk-core (~> 3, >= 3.184.0) 32 | aws-sigv4 (~> 1.1) 33 | aws-sdk-s3 (1.136.0) 34 | aws-sdk-core (~> 3, >= 3.181.0) 35 | aws-sdk-kms (~> 1) 36 | aws-sigv4 (~> 1.6) 37 | aws-sigv4 (1.6.0) 38 | aws-eventstream (~> 1, >= 1.0.2) 39 | babosa (1.0.4) 40 | base64 (0.1.1) 41 | bigdecimal (3.1.4) 42 | claide (1.1.0) 43 | claide-plugins (0.9.2) 44 | cork 45 | nap 46 | open4 (~> 1.3) 47 | cocoapods (1.14.0) 48 | addressable (~> 2.8) 49 | claide (>= 1.0.2, < 2.0) 50 | cocoapods-core (= 1.14.0) 51 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 52 | cocoapods-downloader (>= 2.0) 53 | cocoapods-plugins (>= 1.0.0, < 2.0) 54 | cocoapods-search (>= 1.0.0, < 2.0) 55 | cocoapods-trunk (>= 1.6.0, < 2.0) 56 | cocoapods-try (>= 1.1.0, < 2.0) 57 | colored2 (~> 3.1) 58 | escape (~> 0.0.4) 59 | fourflusher (>= 2.3.0, < 3.0) 60 | gh_inspector (~> 1.0) 61 | molinillo (~> 0.8.0) 62 | nap (~> 1.0) 63 | ruby-macho (>= 2.3.0, < 3.0) 64 | xcodeproj (>= 1.23.0, < 2.0) 65 | cocoapods-core (1.14.0) 66 | activesupport (>= 5.0, < 8) 67 | addressable (~> 2.8) 68 | algoliasearch (~> 1.0) 69 | concurrent-ruby (~> 1.1) 70 | fuzzy_match (~> 2.0.4) 71 | nap (~> 1.0) 72 | netrc (~> 0.11) 73 | public_suffix (~> 4.0) 74 | typhoeus (~> 1.0) 75 | cocoapods-deintegrate (1.0.5) 76 | cocoapods-downloader (2.0) 77 | cocoapods-plugins (1.0.0) 78 | nap 79 | cocoapods-search (1.0.1) 80 | cocoapods-trunk (1.6.0) 81 | nap (>= 0.8, < 2.0) 82 | netrc (~> 0.11) 83 | cocoapods-try (1.2.0) 84 | colored (1.2) 85 | colored2 (3.1.2) 86 | commander (4.6.0) 87 | highline (~> 2.0.0) 88 | concurrent-ruby (1.2.2) 89 | connection_pool (2.4.1) 90 | cork (0.3.0) 91 | colored2 (~> 3.1) 92 | danger (8.6.1) 93 | claide (~> 1.0) 94 | claide-plugins (>= 0.9.2) 95 | colored2 (~> 3.1) 96 | cork (~> 0.1) 97 | faraday (>= 0.9.0, < 2.0) 98 | faraday-http-cache (~> 2.0) 99 | git (~> 1.7) 100 | kramdown (~> 2.3) 101 | kramdown-parser-gfm (~> 1.0) 102 | no_proxy_fix 103 | octokit (~> 4.7) 104 | terminal-table (>= 1, < 4) 105 | danger-swiftlint (0.33.0) 106 | danger 107 | rake (> 10) 108 | thor (~> 0.19) 109 | danger-xcov (0.5.0) 110 | danger (>= 2.1) 111 | xcov (>= 1.7.3) 112 | declarative (0.0.20) 113 | digest-crc (0.6.5) 114 | rake (>= 12.0.0, < 14.0.0) 115 | domain_name (0.5.20190701) 116 | unf (>= 0.0.5, < 1.0.0) 117 | dotenv (2.8.1) 118 | drb (2.1.1) 119 | ruby2_keywords 120 | emoji_regex (3.2.3) 121 | escape (0.0.4) 122 | ethon (0.16.0) 123 | ffi (>= 1.15.0) 124 | excon (0.104.0) 125 | faraday (1.10.3) 126 | faraday-em_http (~> 1.0) 127 | faraday-em_synchrony (~> 1.0) 128 | faraday-excon (~> 1.1) 129 | faraday-httpclient (~> 1.0) 130 | faraday-multipart (~> 1.0) 131 | faraday-net_http (~> 1.0) 132 | faraday-net_http_persistent (~> 1.0) 133 | faraday-patron (~> 1.0) 134 | faraday-rack (~> 1.0) 135 | faraday-retry (~> 1.0) 136 | ruby2_keywords (>= 0.0.4) 137 | faraday-cookie_jar (0.0.7) 138 | faraday (>= 0.8.0) 139 | http-cookie (~> 1.0.0) 140 | faraday-em_http (1.0.0) 141 | faraday-em_synchrony (1.0.0) 142 | faraday-excon (1.1.0) 143 | faraday-http-cache (2.5.0) 144 | faraday (>= 0.8) 145 | faraday-httpclient (1.0.1) 146 | faraday-multipart (1.0.4) 147 | multipart-post (~> 2) 148 | faraday-net_http (1.0.1) 149 | faraday-net_http_persistent (1.2.0) 150 | faraday-patron (1.0.0) 151 | faraday-rack (1.0.0) 152 | faraday-retry (1.0.3) 153 | faraday_middleware (1.2.0) 154 | faraday (~> 1.0) 155 | fastimage (2.2.7) 156 | fastlane (2.216.0) 157 | CFPropertyList (>= 2.3, < 4.0.0) 158 | addressable (>= 2.8, < 3.0.0) 159 | artifactory (~> 3.0) 160 | aws-sdk-s3 (~> 1.0) 161 | babosa (>= 1.0.3, < 2.0.0) 162 | bundler (>= 1.12.0, < 3.0.0) 163 | colored 164 | commander (~> 4.6) 165 | dotenv (>= 2.1.1, < 3.0.0) 166 | emoji_regex (>= 0.1, < 4.0) 167 | excon (>= 0.71.0, < 1.0.0) 168 | faraday (~> 1.0) 169 | faraday-cookie_jar (~> 0.0.6) 170 | faraday_middleware (~> 1.0) 171 | fastimage (>= 2.1.0, < 3.0.0) 172 | gh_inspector (>= 1.1.2, < 2.0.0) 173 | google-apis-androidpublisher_v3 (~> 0.3) 174 | google-apis-playcustomapp_v1 (~> 0.1) 175 | google-cloud-storage (~> 1.31) 176 | highline (~> 2.0) 177 | http-cookie (~> 1.0.5) 178 | json (< 3.0.0) 179 | jwt (>= 2.1.0, < 3) 180 | mini_magick (>= 4.9.4, < 5.0.0) 181 | multipart-post (>= 2.0.0, < 3.0.0) 182 | naturally (~> 2.2) 183 | optparse (~> 0.1.1) 184 | plist (>= 3.1.0, < 4.0.0) 185 | rubyzip (>= 2.0.0, < 3.0.0) 186 | security (= 0.1.3) 187 | simctl (~> 1.6.3) 188 | terminal-notifier (>= 2.0.0, < 3.0.0) 189 | terminal-table (~> 3) 190 | tty-screen (>= 0.6.3, < 1.0.0) 191 | tty-spinner (>= 0.8.0, < 1.0.0) 192 | word_wrap (~> 1.0.0) 193 | xcodeproj (>= 1.13.0, < 2.0.0) 194 | xcpretty (~> 0.3.0) 195 | xcpretty-travis-formatter (>= 0.0.3) 196 | ffi (1.16.3) 197 | fourflusher (2.3.1) 198 | fuzzy_match (2.0.4) 199 | gh_inspector (1.1.3) 200 | git (1.18.0) 201 | addressable (~> 2.8) 202 | rchardet (~> 1.8) 203 | google-apis-androidpublisher_v3 (0.50.0) 204 | google-apis-core (>= 0.11.0, < 2.a) 205 | google-apis-core (0.11.1) 206 | addressable (~> 2.5, >= 2.5.1) 207 | googleauth (>= 0.16.2, < 2.a) 208 | httpclient (>= 2.8.1, < 3.a) 209 | mini_mime (~> 1.0) 210 | representable (~> 3.0) 211 | retriable (>= 2.0, < 4.a) 212 | rexml 213 | webrick 214 | google-apis-iamcredentials_v1 (0.17.0) 215 | google-apis-core (>= 0.11.0, < 2.a) 216 | google-apis-playcustomapp_v1 (0.13.0) 217 | google-apis-core (>= 0.11.0, < 2.a) 218 | google-apis-storage_v1 (0.19.0) 219 | google-apis-core (>= 0.9.0, < 2.a) 220 | google-cloud-core (1.6.0) 221 | google-cloud-env (~> 1.0) 222 | google-cloud-errors (~> 1.0) 223 | google-cloud-env (1.6.0) 224 | faraday (>= 0.17.3, < 3.0) 225 | google-cloud-errors (1.3.1) 226 | google-cloud-storage (1.44.0) 227 | addressable (~> 2.8) 228 | digest-crc (~> 0.4) 229 | google-apis-iamcredentials_v1 (~> 0.1) 230 | google-apis-storage_v1 (~> 0.19.0) 231 | google-cloud-core (~> 1.6) 232 | googleauth (>= 0.16.2, < 2.a) 233 | mini_mime (~> 1.0) 234 | googleauth (1.8.1) 235 | faraday (>= 0.17.3, < 3.a) 236 | jwt (>= 1.4, < 3.0) 237 | multi_json (~> 1.11) 238 | os (>= 0.9, < 2.0) 239 | signet (>= 0.16, < 2.a) 240 | highline (2.0.3) 241 | http-cookie (1.0.5) 242 | domain_name (~> 0.5) 243 | httpclient (2.8.3) 244 | i18n (1.14.1) 245 | concurrent-ruby (~> 1.0) 246 | jazzy (0.14.4) 247 | cocoapods (~> 1.5) 248 | mustache (~> 1.1) 249 | open4 (~> 1.3) 250 | redcarpet (~> 3.4) 251 | rexml (~> 3.2) 252 | rouge (>= 2.0.6, < 5.0) 253 | sassc (~> 2.1) 254 | sqlite3 (~> 1.3) 255 | xcinvoke (~> 0.3.0) 256 | jmespath (1.6.2) 257 | json (2.6.3) 258 | jwt (2.7.1) 259 | kramdown (2.4.0) 260 | rexml 261 | kramdown-parser-gfm (1.1.0) 262 | kramdown (~> 2.0) 263 | liferaft (0.0.6) 264 | mini_magick (4.12.0) 265 | mini_mime (1.1.5) 266 | mini_portile2 (2.8.4) 267 | minitest (5.20.0) 268 | molinillo (0.8.0) 269 | multi_json (1.15.0) 270 | multipart-post (2.3.0) 271 | mustache (1.1.1) 272 | mutex_m (0.1.2) 273 | nanaimo (0.3.0) 274 | nap (1.1.0) 275 | naturally (2.2.1) 276 | netrc (0.11.0) 277 | no_proxy_fix (0.1.2) 278 | octokit (4.25.1) 279 | faraday (>= 1, < 3) 280 | sawyer (~> 0.9) 281 | open4 (1.3.4) 282 | optparse (0.1.1) 283 | os (1.1.4) 284 | plist (3.7.0) 285 | public_suffix (4.0.7) 286 | rake (13.0.6) 287 | rchardet (1.8.0) 288 | redcarpet (3.6.0) 289 | representable (3.2.0) 290 | declarative (< 0.1.0) 291 | trailblazer-option (>= 0.1.1, < 0.2.0) 292 | uber (< 0.2.0) 293 | retriable (3.1.2) 294 | rexml (3.2.6) 295 | rouge (2.0.7) 296 | ruby-macho (2.5.1) 297 | ruby2_keywords (0.0.5) 298 | rubyzip (2.3.2) 299 | sassc (2.4.0) 300 | ffi (~> 1.9) 301 | sawyer (0.9.2) 302 | addressable (>= 2.3.5) 303 | faraday (>= 0.17.3, < 3) 304 | security (0.1.3) 305 | signet (0.18.0) 306 | addressable (~> 2.8) 307 | faraday (>= 0.17.5, < 3.a) 308 | jwt (>= 1.5, < 3.0) 309 | multi_json (~> 1.10) 310 | simctl (1.6.10) 311 | CFPropertyList 312 | naturally 313 | slack-notifier (2.4.0) 314 | sqlite3 (1.6.6) 315 | mini_portile2 (~> 2.8.0) 316 | terminal-notifier (2.0.0) 317 | terminal-table (3.0.2) 318 | unicode-display_width (>= 1.1.1, < 3) 319 | thor (0.20.3) 320 | trailblazer-option (0.1.2) 321 | tty-cursor (0.7.1) 322 | tty-screen (0.8.1) 323 | tty-spinner (0.9.3) 324 | tty-cursor (~> 0.7) 325 | typhoeus (1.4.0) 326 | ethon (>= 0.9.0) 327 | tzinfo (2.0.6) 328 | concurrent-ruby (~> 1.0) 329 | uber (0.1.0) 330 | unf (0.1.4) 331 | unf_ext 332 | unf_ext (0.0.8.2) 333 | unicode-display_width (2.5.0) 334 | webrick (1.8.1) 335 | word_wrap (1.0.0) 336 | xcinvoke (0.3.0) 337 | liferaft (~> 0.0.6) 338 | xcodeproj (1.23.0) 339 | CFPropertyList (>= 2.3.3, < 4.0) 340 | atomos (~> 0.1.3) 341 | claide (>= 1.0.2, < 2.0) 342 | colored2 (~> 3.1) 343 | nanaimo (~> 0.3.0) 344 | rexml (~> 3.2.4) 345 | xcov (1.7.5) 346 | fastlane (>= 2.141.0, < 3.0.0) 347 | multipart-post 348 | slack-notifier 349 | terminal-table 350 | xcodeproj 351 | xcresult (~> 0.2.0) 352 | xcpretty (0.3.0) 353 | rouge (~> 2.0.7) 354 | xcpretty-travis-formatter (1.0.1) 355 | xcpretty (~> 0.2, >= 0.0.7) 356 | xcresult (0.2.1) 357 | 358 | PLATFORMS 359 | ruby 360 | 361 | DEPENDENCIES 362 | cocoapods (~> 1.14) 363 | danger (~> 8.0) 364 | danger-swiftlint (~> 0.20) 365 | danger-xcov (~> 0.5) 366 | jazzy (~> 0.13) 367 | xcov (~> 1.7.3) 368 | 369 | BUNDLED WITH 370 | 2.1.4 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 RxSwiftCommunity 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Junior B. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "RealmDatabase", 6 | "repositoryURL": "https://github.com/realm/realm-core.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "9cf7ef4ad8e2f4c7a519c9a395ca3d253bb87aa8", 10 | "version": "14.6.2" 11 | } 12 | }, 13 | { 14 | "package": "Realm", 15 | "repositoryURL": "https://github.com/realm/realm-swift.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "2000569f03948c281afc83c36c710ab15e5dd33c", 19 | "version": "10.50.0" 20 | } 21 | }, 22 | { 23 | "package": "RxSwift", 24 | "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "b06a8c8596e4c3e8e7788e08e720e3248563ce6a", 28 | "version": "6.7.1" 29 | } 30 | } 31 | ] 32 | }, 33 | "version": 1 34 | } 35 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package(name: "RxRealm", 7 | platforms: [ 8 | .macOS(.v10_10), .iOS(.v11), .tvOS(.v9), .watchOS(.v3) 9 | ], 10 | products: [ 11 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 12 | .library(name: "RxRealm", 13 | targets: ["RxRealm"]) 14 | ], 15 | 16 | dependencies: [ 17 | // Dependencies declare other packages that this package depends on. 18 | .package(url: "https://github.com/realm/realm-swift.git", .upToNextMajor(from: "10.21.1")), 19 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.5.0")) 20 | ], 21 | 22 | targets: [ 23 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 24 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 25 | .target(name: "RxRealm", 26 | dependencies: [ 27 | .product(name: "RxSwift", package: "RxSwift"), 28 | .product(name: "RealmSwift", package: "Realm"), 29 | .product(name: "RxCocoa", package: "RxSwift") 30 | ], 31 | path: "Sources"), 32 | .testTarget(name: "RxRealmTests", 33 | dependencies: [ 34 | .byName(name: "RxRealm"), 35 | .product(name: "RxSwift", package: "RxSwift"), 36 | .product(name: "RxBlocking", package: "RxSwift"), 37 | .product(name: "RealmSwift", package: "Realm"), 38 | .product(name: "RxCocoa", package: "RxSwift") 39 | ]) 40 | ], 41 | swiftLanguageVersions: [.v5]) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxRealm 2 | 3 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![Version](https://img.shields.io/cocoapods/v/RxRealm.svg?style=flat)](http://cocoapods.org/pods/RxRealm) 5 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 6 | [![License](https://img.shields.io/cocoapods/l/RxRealm.svg?style=flat)](http://cocoapods.org/pods/RxRealm) 7 | [![Platform](https://img.shields.io/cocoapods/p/RxRealm.svg?style=flat)](http://cocoapods.org/pods/RxRealm) 8 | 9 | This library is a thin wrapper around __RealmSwift__ ( [Realm Docs](https://realm.io/docs/swift/latest/) ). 10 | 11 | **Table of contents:** 12 | 13 | 1. [Observing object collections](https://github.com/RxSwiftCommunity/RxRealm#observing-object-collections) 14 | 2. [Observing a single object](https://github.com/RxSwiftCommunity/RxRealm#observing-a-single-object) 15 | 3. [Write transactions](https://github.com/RxSwiftCommunity/RxRealm#write-transactions) 16 | 4. [Automatically binding table and collection views](https://github.com/RxSwiftCommunity/RxRealm#automatically-binding-table-and-collection-views) 17 | 5. [Example app](https://github.com/RxSwiftCommunity/RxRealm#example-app) 18 | 19 | ## Observing object collections 20 | 21 | RxRealm can be used to create `Observable`s from objects of type `Results`, `List`, `LinkingObjects` or `AnyRealmCollection`. These types are typically used to load and observe object collections from the Realm Mobile Database. 22 | 23 | ##### `Observable.collection(from:synchronousStart:)` 24 | Emits an event each time the collection changes: 25 | 26 | ```swift 27 | let realm = try! Realm() 28 | let laps = realm.objects(Lap.self) 29 | 30 | Observable.collection(from: laps) 31 | .map { 32 | laps in "\(laps.count) laps" 33 | } 34 | .subscribe(onNext: { text in 35 | print(text) 36 | }) 37 | ``` 38 | 39 | The above prints out "X laps" each time a lap is added or removed from the database. If you set `synchronousStart` to `true` (the default value), the first element will be emitted synchronously - e.g. when you're binding UI it might not be possible for an asynchronous notification to come through. 40 | 41 | ##### `Observable.array(from:synchronousStart:)` 42 | Upon each change fetches a snapshot of the Realm collection and converts it to an array value (for example if you want to use array methods on the collection): 43 | 44 | ```swift 45 | let realm = try! Realm() 46 | let laps = realm.objects(Lap.self) 47 | 48 | Observable.array(from: laps) 49 | .map { array in 50 | return array.prefix(3) //slice of first 3 items 51 | } 52 | .subscribe(onNext: { text in 53 | print(text) 54 | }) 55 | ``` 56 | 57 | ##### `Observable.changeset(from:synchronousStart:)` 58 | Emits every time the collection changes and provides the exact indexes that has been deleted, inserted or updated: 59 | 60 | ```swift 61 | let realm = try! Realm() 62 | let laps = realm.objects(Lap.self) 63 | 64 | Observable.changeset(from: laps) 65 | .subscribe(onNext: { results, changes in 66 | if let changes = changes { 67 | // it's an update 68 | print(results) 69 | print("deleted: \(changes.deleted)") 70 | print("inserted: \(changes.inserted)") 71 | print("updated: \(changes.updated)") 72 | } else { 73 | // it's the initial data 74 | print(results) 75 | } 76 | }) 77 | ``` 78 | 79 | ##### `Observable.arrayWithChangeset(from:synchronousStart:)` 80 | Combines the result of `Observable.array(from:)` and `Observable.changeset(from:)` returning an `Observable, RealmChangeset?>` 81 | 82 | ```swift 83 | let realm = try! Realm() 84 | let laps = realm.objects(Lap.self)) 85 | 86 | Observable.arrayWithChangeset(from: laps) 87 | .subscribe(onNext: { array, changes in 88 | if let changes = changes { 89 | // it's an update 90 | print(array.first) 91 | print("deleted: \(changes.deleted)") 92 | print("inserted: \(changes.inserted)") 93 | print("updated: \(changes.updated)") 94 | } else { 95 | // it's the initial data 96 | print(array) 97 | } 98 | }) 99 | ``` 100 | 101 | ## Observing a single object 102 | 103 | There's a separate API to make it easier to observe a single object: 104 | 105 | ```swift 106 | Observable.from(object: ticker) 107 | .map { ticker -> String in 108 | return "\(ticker.ticks) ticks" 109 | } 110 | .bindTo(footer.rx.text) 111 | ``` 112 | 113 | This API uses the [Realm object notifications](https://realm.io/news/realm-objc-swift-2.4/) under the hood to listen for changes. 114 | 115 | This method will by default emit the object initial state as its first `next` event. You can disable this behavior by using the `emitInitialValue` parameter and setting it to `false`. 116 | 117 | Finally you can set changes to which properties constitute an object change you'd like to observe for: 118 | 119 | ```swift 120 | Observable.from(object: ticker, properties: ["name", "id", "family"]) ... 121 | ``` 122 | 123 | ## Write transactions 124 | 125 | ##### `rx.add()` 126 | 127 | Writing objects to **existing** realm reference. You can add newly created objects to a Realm that you already have initialized: 128 | 129 | ```swift 130 | let realm = try! Realm() 131 | let messages = [Message("hello"), Message("world")] 132 | 133 | Observable.from(messages) 134 | .subscribe(realm.rx.add()) 135 | ``` 136 | 137 | Be careful, this will retain your Realm until the `Observable` completes or errors out. 138 | 139 | ##### `Realm.rx.add()` 140 | 141 | Writing to the default Realm. You can leave it to RxRealm to grab the default Realm on any thread your subscribe and write objects to it: 142 | 143 | ```swift 144 | let messages = [Message("hello"), Message("world")] 145 | 146 | Observable.from(messages) 147 | .subscribe(Realm.rx.add()) 148 | ``` 149 | 150 | ###### `Realm.rx.add(configuration:)` 151 | 152 | Writing to a **custom** Realm. If you want to switch threads and not use the default Realm, provide a `Realm.Configuration`. You an also provide an error handler for the observer to be called if either creating the realm reference or the write transaction raise an error: 153 | 154 | ```swift 155 | var config = Realm.Configuration() 156 | /* custom configuration settings */ 157 | 158 | let messages = [Message("hello"), Message("world")] 159 | Observable.from(messages) 160 | .observeOn( /* you can switch threads here */ ) 161 | .subscribe(Realm.rx.add(configuration: config, onError: {elements, error in 162 | if let elements = elements { 163 | print("Error \(error.localizedDescription) while saving objects \(String(describing: elements))") 164 | } else { 165 | print("Error \(error.localizedDescription) while opening realm.") 166 | } 167 | })) 168 | ``` 169 | 170 | If you want to create a Realm on a different thread manually, allowing you to handle errors, you can do that too: 171 | 172 | ```swift 173 | let messages = [Message("hello"), Message("world")] 174 | 175 | Observable.from(messages) 176 | .observeOn( /* you can switch threads here */ ) 177 | .subscribe(onNext: {messages in 178 | let realm = try! Realm() 179 | try! realm.write { 180 | realm.add(messages) 181 | } 182 | }) 183 | ``` 184 | 185 | ##### `rx.delete()` 186 | 187 | Deleting object(s) from an existing realm reference: 188 | 189 | ```swift 190 | let realm = try! Realm() 191 | let messages = realm.objects(Message.self) 192 | Observable.from(messages) 193 | .subscribe(realm.rx.delete()) 194 | ``` 195 | 196 | Be careful, this will retain your realm until the `Observable` completes or errors out. 197 | 198 | ##### `Realm.rx.delete()` 199 | 200 | Deleting from the object's realm automatically. You can leave it to RxRealm to grab the Realm from the first object and use it: 201 | 202 | ```swift 203 | Observable.from(someCollectionOfPersistedObjects) 204 | .subscribe(Realm.rx.delete()) 205 | ``` 206 | 207 | ## Automatically binding table and collection views 208 | 209 | RxRealm does not depend on UIKit/Cocoa and it doesn't provide built-in way to bind Realm collections to UI components. 210 | 211 | #### a) Non-animated binding 212 | 213 | You can use the built-in RxCocoa `bindTo(_:)` method, which will automatically drive your table view from your Realm results: 214 | 215 | ```swift 216 | Observable.from( [Realm collection] ) 217 | .bindTo(tableView.rx.items) {tv, ip, element in 218 | let cell = tv.dequeueReusableCell(withIdentifier: "Cell")! 219 | cell.textLabel?.text = element.text 220 | return cell 221 | } 222 | .addDisposableTo(bag) 223 | ``` 224 | 225 | #### b) Animated binding with RxRealmDataSources 226 | 227 | The separate library [RxRealmDataSources](https://github.com/RxSwiftCommunity/RxRealmDataSources) mimics the default data sources library behavior for RxSwift. 228 | 229 | `RxRealmDataSources` allows you to bind an observable collection of Realm objects directly to a table or collection view: 230 | 231 | ```swift 232 | // create data source 233 | let dataSource = RxTableViewRealmDataSource( 234 | cellIdentifier: "Cell", cellType: PersonCell.self) {cell, ip, lap in 235 | cell.customLabel.text = "\(ip.row). \(lap.text)" 236 | } 237 | 238 | // RxRealm to get Observable 239 | let realm = try! Realm() 240 | let lapsList = realm.objects(Timer.self).first!.laps 241 | let laps = Observable.changeset(from: lapsList) 242 | 243 | // bind to table view 244 | laps 245 | .bindTo(tableView.rx.realmChanges(dataSource)) 246 | .addDisposableTo(bag) 247 | ``` 248 | 249 | The data source will reflect all changes via animations to the table view: 250 | 251 | ![RxRealm animated changes](assets/animatedChanges.gif) 252 | 253 | If you want to learn more about the features beyond animating changes, check the __`RxRealmDataSources`__ [README](https://github.com/RxSwiftCommunity/RxRealmDataSources). 254 | 255 | ## Example app 256 | 257 | To run the example project, clone the repo, and run `pod install` from the Example directory first. The app uses RxSwift, RxCocoa using RealmSwift, RxRealm to observe Results from Realm. 258 | 259 | Further you're welcome to peak into the __RxRealmTests__ folder of the example app, which features the library's unit tests. 260 | 261 | ## Installation 262 | 263 | This library depends on both __RxSwift__ and __RealmSwift__ 1.0+. 264 | 265 | #### CocoaPods 266 | 267 | RxRealm requires CocoaPods 1.1.x or higher. 268 | 269 | RxRealm is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: 270 | 271 | ```ruby 272 | pod "RxRealm" 273 | ``` 274 | 275 | #### Carthage 276 | 277 | To integrate RxRealm into your Xcode project using Carthage, specify it in your `Cartfile`: 278 | 279 | ```ogdl 280 | github "RxSwiftCommunity/RxRealm" 281 | ``` 282 | 283 | Run `carthage update` to build the framework and drag the built `RxRealm.framework` into your Xcode project. 284 | 285 | #### Swift Package Manager 286 | 287 | In your Package.swift: 288 | 289 | ```swift 290 | let package = Package( 291 | name: "Example", 292 | dependencies: [ 293 | .package(url: "https://github.com/RxSwiftCommunity/RxRealm.git", from: "1.0.1") 294 | ], 295 | targets: [ 296 | .target(name: "Example", dependencies: ["RxRealm"]) 297 | ] 298 | ) 299 | ``` 300 | 301 | ## TODO 302 | 303 | * Test add platforms and add compatibility for the pod 304 | 305 | ## License 306 | 307 | This library belongs to _RxSwiftCommunity_. Maintainer is [Marin Todorov](https://github.com/icanzilb). 308 | 309 | RxRealm is available under the MIT license. See the LICENSE file for more info. 310 | -------------------------------------------------------------------------------- /RxRealm.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RxRealm" 3 | # Version to always follow latest tag, with fallback to major 4 | s.version = "5.0.8" 5 | s.license = "MIT" 6 | s.description = <<-DESC 7 | This is an Rx extension that provides an easy and straight-forward way 8 | to use Realm's natively reactive collection type as an Observable 9 | DESC 10 | 11 | s.summary = "An Rx wrapper of Realm's notifications and write bindings" 12 | s.homepage = "https://github.com/RxSwiftCommunity/RxRealm" 13 | s.authors = { "RxSwift Community" => "community@rxswift.org" } 14 | s.source = { :git => "https://github.com/RxSwiftCommunity/RxRealm.git", :tag => "v" + s.version.to_s } 15 | s.swift_version = "5.1" 16 | 17 | s.ios.deployment_target = "12.0" 18 | s.osx.deployment_target = "10.13" 19 | s.tvos.deployment_target = "12.0" 20 | s.watchos.deployment_target = "4.0" 21 | 22 | s.requires_arc = true 23 | 24 | s.source_files = "Sources/RxRealm/*.swift" 25 | 26 | s.frameworks = "Foundation" 27 | s.dependency "Realm", "~> 10.50" 28 | s.dependency "RealmSwift", "~> 10.50" 29 | s.dependency "RxSwift", "~> 6.1" 30 | s.dependency "RxCocoa", "~> 6.1" 31 | end 32 | -------------------------------------------------------------------------------- /RxRealm.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RxRealm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxRealm.xcodeproj/xcshareddata/xcschemes/RxRealm iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 52 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RxRealm.xcodeproj/xcshareddata/xcschemes/RxRealm macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 52 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RxRealm.xcodeproj/xcshareddata/xcschemes/RxRealm tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 52 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /RxRealm.xcodeproj/xcshareddata/xcschemes/RxRealm watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 53 | 59 | 60 | 61 | 62 | 68 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Sources/RxRealm/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sources/RxRealm/RealmObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealm extensions 3 | // 4 | // Copyright (c) 2016 RxSwiftCommunity. All rights reserved. 5 | // Created by sergdort on 6/3/16. 6 | // 7 | 8 | import Foundation 9 | import RealmSwift 10 | import RxSwift 11 | 12 | /** 13 | `RealmObserver` retains target realm object until it receives a .Completed or .Error event 14 | or the observer is being deinitialized 15 | */ 16 | class RealmObserver: ObserverType { 17 | var realm: Realm? 18 | var configuration: Realm.Configuration? 19 | 20 | let binding: (Realm?, Element, Error?) -> Void 21 | 22 | init(realm: Realm, binding: @escaping (Realm?, Element, Error?) -> Void) { 23 | self.realm = realm 24 | self.binding = binding 25 | } 26 | 27 | init(configuration: Realm.Configuration, binding: @escaping (Realm?, Element, Error?) -> Void) { 28 | self.configuration = configuration 29 | self.binding = binding 30 | } 31 | 32 | /** 33 | Binds next element 34 | */ 35 | func on(_ event: Event) { 36 | switch event { 37 | case let .next(element): 38 | // this will "cache" the realm on this thread, until completed/errored 39 | if let configuration = configuration, realm == nil { 40 | do { 41 | let realm = try Realm(configuration: configuration) 42 | binding(realm, element, nil) 43 | } catch let e { 44 | binding(nil, element, e) 45 | } 46 | return 47 | } 48 | 49 | guard let realm = realm else { 50 | fatalError("No realm in RealmObserver at time of a .Next event") 51 | } 52 | 53 | binding(realm, element, nil) 54 | 55 | case .error: 56 | realm = nil 57 | case .completed: 58 | realm = nil 59 | } 60 | } 61 | 62 | /** 63 | Erases the type of observer 64 | 65 | - returns: AnyObserver, type erased observer 66 | */ 67 | func asObserver() -> AnyObserver { 68 | return AnyObserver(eventHandler: on) 69 | } 70 | 71 | deinit { 72 | realm = nil 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/RxRealm/RxRealm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealm extensions 3 | // 4 | // Copyright (c) 2016 RxSwiftCommunity. All rights reserved. 5 | // Check the LICENSE file for details 6 | // Created by Marin Todorov 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | import RxSwift 12 | 13 | public typealias Observable = RxSwift.Observable 14 | 15 | public enum RxRealmError: Error { 16 | case objectDeleted 17 | case unknown 18 | } 19 | 20 | // MARK: Realm Collections type extensions 21 | 22 | /** 23 | `NotificationEmitter` is a protocol to allow for Realm's collections to be handled in a generic way. 24 | 25 | All collections already include a `addNotificationBlock(_:)` method - making them conform to `NotificationEmitter` just makes it easier to add Rx methods to them. 26 | 27 | The methods of essence in this protocol are `asObservable(...)`, which allow for observing for changes on Realm's collections. 28 | */ 29 | public protocol NotificationEmitter { 30 | associatedtype ElementType: RealmCollectionValue 31 | 32 | /** 33 | Returns a `NotificationToken`, which while retained enables change notifications for the current collection. 34 | 35 | - returns: `NotificationToken` - retain this value to keep notifications being emitted for the current collection. 36 | */ 37 | func observe(keyPaths: [String]?, 38 | on queue: DispatchQueue?, 39 | _ block: @escaping (RealmCollectionChange) -> Void) -> NotificationToken 40 | 41 | func toArray() -> [ElementType] 42 | 43 | func toAnyCollection() -> AnyRealmCollection 44 | } 45 | 46 | extension List: NotificationEmitter { 47 | public func toAnyCollection() -> AnyRealmCollection { 48 | return AnyRealmCollection(self) 49 | } 50 | 51 | public typealias ElementType = Element 52 | public func toArray() -> [Element] { 53 | return Array(self) 54 | } 55 | } 56 | 57 | extension AnyRealmCollection: NotificationEmitter { 58 | public func toAnyCollection() -> AnyRealmCollection { 59 | return AnyRealmCollection(self) 60 | } 61 | 62 | public typealias ElementType = Element 63 | public func toArray() -> [Element] { 64 | return Array(self) 65 | } 66 | } 67 | 68 | extension Results: NotificationEmitter { 69 | public func toAnyCollection() -> AnyRealmCollection { 70 | return AnyRealmCollection(self) 71 | } 72 | 73 | public typealias ElementType = Element 74 | public func toArray() -> [Element] { 75 | return Array(self) 76 | } 77 | } 78 | 79 | extension LinkingObjects: NotificationEmitter { 80 | public func toAnyCollection() -> AnyRealmCollection { 81 | return AnyRealmCollection(self) 82 | } 83 | 84 | public typealias ElementType = Element 85 | public func toArray() -> [Element] { 86 | return Array(self) 87 | } 88 | } 89 | 90 | /** 91 | `RealmChangeset` is a struct that contains the data about a single realm change set. 92 | 93 | It includes the insertions, modifications, and deletions indexes in the data set that the current notification is about. 94 | */ 95 | public struct RealmChangeset { 96 | /// the indexes in the collection that were deleted 97 | public let deleted: [Int] 98 | 99 | /// the indexes in the collection that were inserted 100 | public let inserted: [Int] 101 | 102 | /// the indexes in the collection that were modified 103 | public let updated: [Int] 104 | } 105 | 106 | public extension ObservableType where Element: NotificationEmitter { 107 | @available(*, deprecated, renamed: "collection(from:synchronousStart:)") 108 | static func from(_ collection: Element, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable { 109 | return self.collection(from: collection) 110 | } 111 | 112 | /** 113 | Returns an `Observable` that emits each time the collection data changes. 114 | The observable emits an initial value upon subscription. 115 | 116 | - parameter from: A Realm collection of type `Element`: either `Results`, `List`, `LinkingObjects` or `AnyRealmCollection`. 117 | - parameter synchronousStart: whether the resulting `Observable` should emit its first element synchronously (e.g. better for UI bindings) 118 | - parameter keyPaths: Only properties contained in the key paths array will trigger 119 | the block when they are modified. See description above for more detail on linked properties. 120 | - parameter queue: The serial dispatch queue to receive notification on. If `nil`, notifications are delivered to the current thread. 121 | 122 | - returns: `Observable`, e.g. when called on `Results` it will return `Observable>`, on a `List` it will return `Observable>`, etc. 123 | */ 124 | static func collection(from collection: Element, synchronousStart: Bool = true, keyPaths: [String]? = nil, on queue: DispatchQueue? = nil) 125 | -> Observable { 126 | return Observable.create { observer in 127 | if synchronousStart { 128 | observer.onNext(collection) 129 | } 130 | 131 | let token = collection.observe(keyPaths: keyPaths, on: queue) { changeset in 132 | 133 | let value: Element 134 | 135 | switch changeset { 136 | case let .initial(latestValue): 137 | guard !synchronousStart else { return } 138 | value = latestValue 139 | 140 | case let .update(latestValue, _, _, _): 141 | value = latestValue 142 | 143 | case let .error(error): 144 | observer.onError(error) 145 | return 146 | } 147 | 148 | observer.onNext(value) 149 | } 150 | 151 | return Disposables.create { 152 | token.invalidate() 153 | } 154 | } 155 | } 156 | 157 | @available(*, deprecated, renamed: "array(from:synchronousStart:)") 158 | static func arrayFrom(_ collection: Element, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<[Element.ElementType]> { 159 | return array(from: collection) 160 | } 161 | 162 | /** 163 | Returns an `Observable>` that emits each time the collection data changes. The observable emits an initial value upon subscription. 164 | The result emits an array containing all objects from the source collection. 165 | 166 | - parameter from: A Realm collection of type `Element`: either `Results`, `List`, `LinkingObjects` or `AnyRealmCollection`. 167 | - parameter synchronousStart: whether the resulting Observable should emit its first element synchronously (e.g. better for UI bindings) 168 | - parameter queue: The serial dispatch queue to receive notification on. If `nil`, notifications are delivered to the current thread. 169 | 170 | - returns: `Observable>`, e.g. when called on `Results` it will return `Observable>`, on a `List` it will return `Observable>`, etc. 171 | */ 172 | static func array(from collection: Element, synchronousStart: Bool = true, on queue: DispatchQueue? = nil) 173 | -> Observable<[Element.ElementType]> { 174 | return Observable.collection(from: collection, synchronousStart: synchronousStart, on: queue) 175 | .map { $0.toArray() } 176 | } 177 | 178 | @available(*, deprecated, renamed: "changeset(from:synchronousStart:)") 179 | static func changesetFrom(_ collection: Element, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<(AnyRealmCollection, RealmChangeset?)> { 180 | return changeset(from: collection) 181 | } 182 | 183 | /** 184 | Returns an `Observable<(Element, RealmChangeset?)>` that emits each time the collection data changes. The observable emits an initial value upon subscription. 185 | 186 | When the observable emits for the first time (if the initial notification is not coalesced with an update) the second tuple value will be `nil`. 187 | 188 | Each following emit will include a `RealmChangeset` with the indexes inserted, deleted or modified. 189 | 190 | - parameter from: A Realm collection of type `Element`: either `Results`, `List`, `LinkingObjects` or `AnyRealmCollection`. 191 | - parameter synchronousStart: whether the resulting Observable should emit its first element synchronously (e.g. better for UI bindings) 192 | - parameter queue: The serial dispatch queue to receive notification on. If `nil`, notifications are delivered to the current thread. 193 | 194 | - returns: `Observable<(AnyRealmCollection, RealmChangeset?)>` 195 | */ 196 | static func changeset(from collection: Element, synchronousStart: Bool = true, on queue: DispatchQueue? = nil) 197 | -> Observable<(AnyRealmCollection, RealmChangeset?)> { 198 | return Observable.create { observer in 199 | if synchronousStart { 200 | observer.onNext((collection.toAnyCollection(), nil)) 201 | } 202 | 203 | let token = collection.toAnyCollection().observe(on: queue) { changeset in 204 | 205 | switch changeset { 206 | case let .initial(value): 207 | guard !synchronousStart else { return } 208 | observer.onNext((value, nil)) 209 | case let .update(value, deletes, inserts, updates): 210 | observer.onNext((value, RealmChangeset(deleted: deletes, inserted: inserts, updated: updates))) 211 | case let .error(error): 212 | observer.onError(error) 213 | return 214 | } 215 | } 216 | 217 | return Disposables.create { 218 | token.invalidate() 219 | } 220 | } 221 | } 222 | 223 | @available(*, deprecated, renamed: "arrayWithChangeset(from:synchronousStart:)") 224 | static func changesetArrayFrom(_ collection: Element, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<([Element.ElementType], RealmChangeset?)> { 225 | return arrayWithChangeset(from: collection) 226 | } 227 | 228 | /** 229 | Returns an `Observable<(Array, RealmChangeset?)>` that emits each time the collection data changes. The observable emits an initial value upon subscription. 230 | 231 | This method emits an `Array` containing all the realm collection objects, this means they all live in the memory. If you're using this method to observe large collections you might hit memory warnings. 232 | 233 | When the observable emits for the first time (if the initial notification is not coalesced with an update) the second tuple value will be `nil`. 234 | 235 | Each following emit will include a `RealmChangeset` with the indexes inserted, deleted or modified. 236 | 237 | - parameter from: A Realm collection of type `Element`: either `Results`, `List`, `LinkingObjects` or `AnyRealmCollection`. 238 | - parameter synchronousStart: whether the resulting Observable should emit its first element synchronously (e.g. better for UI bindings) 239 | - parameter queue: The serial dispatch queue to receive notification on. If `nil`, notifications are delivered to the current thread. 240 | 241 | - returns: `Observable<(Array, RealmChangeset?)>` 242 | */ 243 | static func arrayWithChangeset(from collection: Element, synchronousStart: Bool = true, on queue: DispatchQueue? = nil) 244 | -> Observable<([Element.ElementType], RealmChangeset?)> { 245 | return Observable.changeset(from: collection, on: queue) 246 | .map { ($0.toArray(), $1) } 247 | } 248 | } 249 | 250 | public extension Observable { 251 | @available(*, deprecated, renamed: "from(realm:)") 252 | static func from(_ realm: Realm, scheduler: ImmediateSchedulerType = CurrentThreadScheduler.instance) -> Observable<(Realm, Realm.Notification)> { 253 | return from(realm: realm) 254 | } 255 | 256 | /** 257 | Returns an `Observable<(Realm, Realm.Notification)>` that emits each time the Realm emits a notification. 258 | 259 | The Observable you will get emits a tuple made out of: 260 | 261 | * the realm that emitted the event 262 | * the notification type: this can be either `.didChange` which occurs after a refresh or a write transaction ends, 263 | or `.refreshRequired` which happens when a write transaction occurs from a different thread on the same realm file 264 | 265 | For more information look up: [Realm.Notification](https://realm.io/docs/swift/latest/api/Enums/Notification.html) 266 | 267 | - parameter realm: A Realm instance 268 | - returns: `Observable<(Realm, Realm.Notification)>`, which you can subscribe to 269 | */ 270 | static func from(realm: Realm) -> RxSwift.Observable<(Realm, Realm.Notification)> { 271 | return RxSwift.Observable<(Realm, Realm.Notification)>.create { observer in 272 | let token = realm.observe { (notification: Realm.Notification, realm: Realm) in 273 | observer.onNext((realm, notification)) 274 | } 275 | 276 | return Disposables.create { 277 | token.invalidate() 278 | } 279 | } 280 | } 281 | } 282 | 283 | // MARK: Realm type extensions 284 | 285 | extension Realm: ReactiveCompatible {} 286 | 287 | public extension Reactive where Base == Realm { 288 | /** 289 | Returns bindable sink wich adds object sequence to the current Realm 290 | 291 | - parameter: update - update according to Realm.UpdatePolicy 292 | - parameter: onError - closure to implement custom error handling 293 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 294 | */ 295 | func add(update: Realm.UpdatePolicy = .error, onError: ((S?, Error) -> Void)? = nil) 296 | -> AnyObserver where S.Iterator.Element: Object { 297 | return RealmObserver(realm: base) { realm, elements, error in 298 | guard let realm = realm else { 299 | onError?(nil, error ?? RxRealmError.unknown) 300 | return 301 | } 302 | 303 | do { 304 | try realm.write { 305 | realm.add(elements, update: update) 306 | } 307 | } catch let e { 308 | onError?(elements, e) 309 | } 310 | } 311 | .asObserver() 312 | } 313 | 314 | /** 315 | Returns bindable sink wich adds an object to Realm 316 | 317 | - parameter: update - update according to Realm.UpdatePolicy 318 | - parameter: onError - closure to implement custom error handling 319 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 320 | */ 321 | func add(update: Realm.UpdatePolicy = .error, 322 | onError: ((O?, Error) -> Void)? = nil) -> AnyObserver { 323 | return RealmObserver(realm: base) { realm, element, error in 324 | guard let realm = realm else { 325 | onError?(nil, error ?? RxRealmError.unknown) 326 | return 327 | } 328 | 329 | do { 330 | try realm.write { 331 | realm.add(element, update: update) 332 | } 333 | } catch let e { 334 | onError?(element, e) 335 | } 336 | }.asObserver() 337 | } 338 | 339 | /** 340 | Returns bindable sink wich deletes objects in sequence from Realm. 341 | 342 | - parameter: onError - closure to implement custom error handling 343 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 344 | */ 345 | func delete(onError: ((S?, Error) -> Void)? = nil) 346 | -> AnyObserver where S.Iterator.Element: Object { 347 | return RealmObserver(realm: base, binding: { realm, elements, error in 348 | guard let realm = realm else { 349 | onError?(nil, error ?? RxRealmError.unknown) 350 | return 351 | } 352 | 353 | do { 354 | try realm.write { 355 | realm.delete(elements) 356 | } 357 | } catch let e { 358 | onError?(elements, e) 359 | } 360 | }).asObserver() 361 | } 362 | 363 | /** 364 | Returns bindable sink wich deletes objects in sequence from Realm. 365 | 366 | - parameter: onError - closure to implement custom error handling 367 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 368 | */ 369 | func delete(onError: ((O?, Error) -> Void)? = nil) -> AnyObserver { 370 | return RealmObserver(realm: base, binding: { realm, element, error in 371 | guard let realm = realm else { 372 | onError?(nil, error ?? RxRealmError.unknown) 373 | return 374 | } 375 | 376 | do { 377 | try realm.write { 378 | realm.delete(element) 379 | } 380 | } catch let e { 381 | onError?(element, e) 382 | } 383 | }).asObserver() 384 | } 385 | } 386 | 387 | public extension Reactive where Base == Realm { 388 | /** 389 | Returns bindable sink wich adds object sequence to a Realm 390 | 391 | - parameter: configuration (by default uses `Realm.Configuration.defaultConfiguration`) 392 | to use to get a Realm for the write operations 393 | - parameter: update - update according to Realm.UpdatePolicy 394 | - parameter: onError - closure to implement custom error handling 395 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 396 | */ 397 | static func add(configuration: Realm.Configuration = Realm.Configuration.defaultConfiguration, 398 | update: Realm.UpdatePolicy = .error, 399 | onError: ((S?, Error) -> Void)? = nil) -> AnyObserver where S.Iterator.Element: Object { 400 | return RealmObserver(configuration: configuration) { realm, elements, error in 401 | guard let realm = realm else { 402 | onError?(nil, error ?? RxRealmError.unknown) 403 | return 404 | } 405 | 406 | do { 407 | try realm.write { 408 | realm.add(elements, update: update) 409 | } 410 | } catch let e { 411 | onError?(elements, e) 412 | } 413 | }.asObserver() 414 | } 415 | 416 | /** 417 | Returns bindable sink which adds an object to a Realm 418 | 419 | - parameter: configuration (by default uses `Realm.Configuration.defaultConfiguration`) 420 | to use to get a Realm for the write operations 421 | - parameter: update - update according to Realm.UpdatePolicy 422 | - parameter: onError - closure to implement custom error handling 423 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 424 | */ 425 | static func add(configuration: Realm.Configuration = Realm.Configuration.defaultConfiguration, 426 | update: Realm.UpdatePolicy = .error, 427 | onError: ((O?, Error) -> Void)? = nil) -> AnyObserver { 428 | return RealmObserver(configuration: configuration) { realm, element, error in 429 | guard let realm = realm else { 430 | onError?(nil, error ?? RxRealmError.unknown) 431 | return 432 | } 433 | 434 | do { 435 | try realm.write { 436 | realm.add(element, update: update) 437 | } 438 | } catch let e { 439 | onError?(element, e) 440 | } 441 | }.asObserver() 442 | } 443 | 444 | /** 445 | Returns bindable sink, which deletes objects in sequence from Realm. 446 | 447 | - parameter: onError - closure to implement custom error handling 448 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 449 | */ 450 | static func delete(onError: ((S?, Error) -> Void)? = nil) 451 | -> AnyObserver where S.Iterator.Element: Object { 452 | return AnyObserver { event in 453 | 454 | guard let elements = event.element, 455 | var generator = elements.makeIterator() as S.Iterator?, 456 | let first = generator.next(), 457 | let realm = first.realm 458 | else { 459 | onError?(nil, RxRealmError.unknown) 460 | return 461 | } 462 | 463 | do { 464 | try realm.write { 465 | realm.delete(elements) 466 | } 467 | } catch let e { 468 | onError?(elements, e) 469 | } 470 | } 471 | } 472 | 473 | /** 474 | Returns bindable sink, which deletes object from Realm 475 | 476 | - parameter: onError - closure to implement custom error handling 477 | - returns: `AnyObserver`, which you can use to subscribe an `Observable` to 478 | */ 479 | static func delete(onError: ((O?, Error) -> Void)? = nil) -> AnyObserver { 480 | return AnyObserver { event in 481 | 482 | guard let element = event.element, let realm = element.realm else { 483 | onError?(nil, RxRealmError.unknown) 484 | return 485 | } 486 | 487 | do { 488 | try realm.write { 489 | realm.delete(element) 490 | } 491 | } catch let e { 492 | onError?(element, e) 493 | } 494 | } 495 | } 496 | } 497 | 498 | // MARK: Realm Object type extensions 499 | 500 | public extension Observable where Element: Object { 501 | @available(*, deprecated, renamed: "from(object:)") 502 | static func from(_ object: Element) -> Observable { 503 | return from(object: object) 504 | } 505 | 506 | /** 507 | Returns an `Observable` that emits each time the object changes. The observable emits an initial value upon subscription. 508 | 509 | - parameter object: A Realm Object to observe 510 | - parameter emitInitialValue: whether the resulting `Observable` should emit its first element synchronously (e.g. better for UI bindings) 511 | - parameter properties: changes to which properties would triger emitting a .next event 512 | - parameter queue: The serial dispatch queue to receive notification on. If `nil`, notifications are delivered to the current thread. 513 | - returns: `Observable` will emit any time the observed object changes + one initial emit upon subscription 514 | */ 515 | 516 | static func from(object: Element, emitInitialValue: Bool = true, 517 | properties: [String]? = nil, 518 | on queue: DispatchQueue? = nil) -> Observable { 519 | return RxSwift.Observable.create { observer in 520 | if emitInitialValue { 521 | observer.onNext(object) 522 | } 523 | 524 | let token = object.observe(on: queue) { change in 525 | switch change { 526 | case let .change(_, changedProperties): 527 | if let properties = properties, !changedProperties.contains(where: { properties.contains($0.name) }) { 528 | // if change property isn't an observed one, just return 529 | return 530 | } 531 | observer.onNext(object) 532 | case .deleted: 533 | observer.onError(RxRealmError.objectDeleted) 534 | case let .error(error): 535 | observer.onError(error) 536 | } 537 | } 538 | 539 | return Disposables.create { 540 | token.invalidate() 541 | } 542 | } 543 | } 544 | 545 | /** 546 | Returns an `Observable` that emits the object `PropertyChange`s. 547 | 548 | - parameter object: A Realm Object to observe 549 | - parameter queue: The serial dispatch queue to receive notification on. If `nil`, notifications are delivered to the current thread. 550 | - returns: `Observable` will emit any time a change is detected on the object 551 | */ 552 | 553 | static func propertyChanges(object: Element, 554 | on queue: DispatchQueue? = nil) -> Observable { 555 | return RxSwift.Observable.create { observer in 556 | let token = object.observe(on: queue) { change in 557 | switch change { 558 | case let .change(_, changes): 559 | for change in changes { 560 | observer.onNext(change) 561 | } 562 | case .deleted: 563 | observer.onError(RxRealmError.objectDeleted) 564 | case let .error(error): 565 | observer.onError(error) 566 | } 567 | } 568 | 569 | return Disposables.create { 570 | token.invalidate() 571 | } 572 | } 573 | } 574 | } 575 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import RxRealmTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += RxRealmLinkingObjectsTests.allTests() 7 | tests += RxRealmListTests.allTests() 8 | tests += RxRealmObjectTests.allTests() 9 | tests += RxRealmOnQueueTests.allTests() 10 | tests += RxRealmRealmTests.allTests() 11 | tests += RxRealmResultsTests.allTests() 12 | tests += RxRealm_Tests.allTests() 13 | tests += RxRealmWriteSinks.allTests() 14 | XCTMain(tests) 15 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmLinkingObjectsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmLinkingObjectsTests.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 5/1/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import RealmSwift 12 | import RxRealm 13 | import RxSwift 14 | 15 | class RxRealmLinkingObjectsTests: XCTestCase { 16 | let allTests = [ 17 | "testLinkingObjectsType": testLinkingObjectsType, 18 | "testLinkingObjectsTypeChangeset": testLinkingObjectsTypeChangeset 19 | ] 20 | 21 | func testLinkingObjectsType() { 22 | let realm = realmInMemory(#function) 23 | 24 | let message = Message("first") 25 | try! realm.write { 26 | realm.add(message) 27 | } 28 | 29 | let users = Observable.collection(from: message.mentions) 30 | .map { $0.count } 31 | 32 | XCTAssertEqual(try! users.toBlocking().first()!, 0) 33 | 34 | let user1 = User("user1") 35 | user1.lastMessage = message 36 | 37 | DispatchQueue.main.async { 38 | try! realm.write { 39 | realm.add(user1) 40 | } 41 | } 42 | 43 | XCTAssertEqual(try! users.skip(1).toBlocking().first()!, 1) 44 | 45 | DispatchQueue.global(qos: .background).async { 46 | let realm = realmInMemory(#function) 47 | let user1 = realm.objects(User.self).first! 48 | try! realm.write { 49 | realm.delete(user1) 50 | } 51 | } 52 | 53 | XCTAssertEqual(try! users.skip(1).toBlocking().first()!, 0) 54 | } 55 | 56 | func testLinkingObjectsTypeChangeset() { 57 | let realm = realmInMemory(#function) 58 | 59 | let message = Message("first") 60 | try! realm.write { 61 | realm.add(message) 62 | } 63 | 64 | let users = Observable.changeset(from: message.mentions) 65 | .map(stringifyChanges) 66 | 67 | XCTAssertEqual(try! users.toBlocking().first()!, "count:0") 68 | 69 | let user1 = User("user1") 70 | user1.lastMessage = message 71 | 72 | DispatchQueue.main.async { 73 | try! realm.write { 74 | realm.add(user1) 75 | } 76 | } 77 | 78 | XCTAssertEqual(try! users.skip(1).toBlocking().first()!, "count:1 inserted:[0] deleted:[] updated:[]") 79 | 80 | DispatchQueue.global(qos: .background).async { 81 | let realm = realmInMemory(#function) 82 | let user1 = realm.objects(User.self).first! 83 | try! realm.write { 84 | realm.delete(user1) 85 | } 86 | } 87 | 88 | XCTAssertEqual(try! users.skip(1).toBlocking().first()!, "count:0 inserted:[] deleted:[0] updated:[]") 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmListTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmListTests.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 5/1/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import RealmSwift 12 | import RxRealm 13 | import RxSwift 14 | 15 | class RxRealmListTests: XCTestCase { 16 | let allTests = [ 17 | "testListType": testListType, 18 | "testListTypeChangeset": testListTypeChangeset 19 | ] 20 | func testListType() { 21 | let realm = realmInMemory(#function) 22 | 23 | let message = Message("first") 24 | try! realm.write { 25 | realm.add(message) 26 | } 27 | 28 | let users = Observable.collection(from: message.recipients) 29 | .skip(1) 30 | .map { $0.count } 31 | 32 | DispatchQueue.main.async { 33 | try! realm.write { 34 | message.recipients.append(User("user1")) 35 | } 36 | } 37 | 38 | XCTAssertEqual(try! users.toBlocking().first()!, 1) 39 | 40 | DispatchQueue.global(qos: .background).async { 41 | let realm = realmInMemory(#function) 42 | let message = realm.objects(Message.self).first! 43 | try! realm.write { 44 | message.recipients.remove(at: 0) 45 | } 46 | } 47 | 48 | XCTAssertEqual(try! users.toBlocking().first()!, 0) 49 | } 50 | 51 | func testListTypeChangeset() { 52 | let realm = realmInMemory(#function) 53 | 54 | let message = Message("first") 55 | try! realm.write { 56 | realm.add(message) 57 | } 58 | 59 | let users = Observable.changeset(from: message.recipients) 60 | .map(stringifyChanges) 61 | 62 | XCTAssertEqual(try! users.toBlocking().first()!, "count:0") 63 | 64 | DispatchQueue.main.async { 65 | try! realm.write { 66 | message.recipients.append(User("user1")) 67 | } 68 | } 69 | 70 | XCTAssertEqual(try! users.skip(1).toBlocking().first()!, "count:1 inserted:[0] deleted:[] updated:[]") 71 | 72 | DispatchQueue.global(qos: .background).async { 73 | let realm = realmInMemory(#function) 74 | let message = realm.objects(Message.self).first! 75 | try! realm.write { 76 | message.recipients.remove(at: 0) 77 | } 78 | } 79 | 80 | XCTAssertEqual(try! users.skip(1).toBlocking().first()!, "count:0 inserted:[] deleted:[0] updated:[]") 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmObjectTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmObjectTests.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 10/31/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import RealmSwift 12 | import RxRealm 13 | import RxSwift 14 | 15 | class RxRealmObjectTests: XCTestCase { 16 | let allTests = [ 17 | "testObjectChangeNotifications": testObjectChangeNotifications, 18 | "testObjectEmitsInitialChange": testObjectEmitsInitialChange, 19 | "testObjectDoesntEmitInitialValue": testObjectDoesntEmitInitialValue, 20 | "testObjectPropertyChangeNotifications": testObjectPropertyChangeNotifications, 21 | "testObjectChangeNotificationsForProperties": testObjectChangeNotificationsForProperties 22 | ] 23 | func testObjectChangeNotifications() { 24 | let realm = realmInMemory(#function) 25 | 26 | // create object 27 | let idValue = 1024 28 | let obj = UniqueObject(idValue) 29 | try! realm.write { 30 | realm.add(obj) 31 | } 32 | 33 | let objectNotifications = Observable.from(object: obj) 34 | .map { $0.name } 35 | 36 | XCTAssertEqual(try! objectNotifications.toBlocking().first()!, "") 37 | 38 | DispatchQueue.main.async { 39 | try! realm.write { 40 | obj.name = "test1" 41 | } 42 | } 43 | XCTAssertEqual(try! objectNotifications.skip(1).toBlocking().first()!, "test1") 44 | 45 | DispatchQueue.global(qos: .background).async { 46 | let realm = realmInMemory(#function) 47 | try! realm.write { 48 | realm.objects(UniqueObject.self).filter("id == %@", idValue).first!.name = "test2" 49 | } 50 | } 51 | 52 | XCTAssertEqual(try! objectNotifications.skip(1).toBlocking().first()!, "test2") 53 | 54 | // delete the object to trigger an error 55 | DispatchQueue.main.async { 56 | try! realm.write { 57 | realm.delete(obj) 58 | } 59 | } 60 | 61 | XCTAssertThrowsError(try objectNotifications.skip(1).toBlocking().first()!) { error in 62 | XCTAssertEqual(error as! RxRealmError, RxRealmError.objectDeleted) 63 | } 64 | } 65 | 66 | func testObjectEmitsInitialChange() { 67 | let realm = realmInMemory(#function) 68 | 69 | let obj = UniqueObject(1024) 70 | try! realm.write { 71 | realm.add(obj) 72 | } 73 | 74 | var result = false 75 | 76 | // emits upon subscription 77 | _ = Observable.from(object: obj, emitInitialValue: true) 78 | .subscribe(onNext: { _ in 79 | result = true 80 | }) 81 | 82 | XCTAssertEqual(result, true) 83 | } 84 | 85 | func testObjectDoesntEmitInitialValue() { 86 | let realm = realmInMemory(#function) 87 | 88 | let obj = UniqueObject(1024) 89 | try! realm.write { 90 | realm.add(obj) 91 | } 92 | 93 | var result = false 94 | 95 | // doesn't emit upon subscription 96 | _ = Observable.from(object: obj, emitInitialValue: false) 97 | .subscribe(onNext: { _ in 98 | result = true 99 | }) 100 | 101 | XCTAssertEqual(result, false) 102 | } 103 | 104 | func testObjectPropertyChangeNotifications() { 105 | let realm = realmInMemory(#function) 106 | 107 | let obj = UniqueObject(1024) 108 | try! realm.write { 109 | realm.add(obj) 110 | } 111 | 112 | let objectNotifications = Observable.propertyChanges(object: obj) 113 | .map { "\($0.name):\($0.newValue!)" } 114 | 115 | DispatchQueue.main.async { 116 | try! realm.write { 117 | obj.name = "test1" 118 | } 119 | } 120 | XCTAssertEqual(try! objectNotifications.toBlocking().first()!, "name:test1") 121 | 122 | DispatchQueue.global(qos: .background).async { 123 | let realm = realmInMemory(#function) 124 | try! realm.write { 125 | realm.objects(UniqueObject.self).first!.name = "test2" 126 | } 127 | } 128 | XCTAssertEqual(try! objectNotifications.toBlocking().first()!, "name:test2") 129 | 130 | // delete the object to trigger an error 131 | DispatchQueue.main.async { 132 | try! realm.write { 133 | realm.delete(obj) 134 | } 135 | } 136 | XCTAssertThrowsError(try objectNotifications.toBlocking().first()!) { error in 137 | XCTAssertEqual(error as! RxRealmError, RxRealmError.objectDeleted) 138 | } 139 | } 140 | 141 | func testObjectChangeNotificationsForProperties() { 142 | let realm = realmInMemory(#function) 143 | 144 | let obj = UniqueObject(1024) 145 | try! realm.write { 146 | realm.add(obj) 147 | } 148 | 149 | let objectNotifications = Observable.from(object: obj, emitInitialValue: false, properties: ["name"]) 150 | .map { "\($0.name)" } 151 | 152 | DispatchQueue.main.async { 153 | try! realm.write { 154 | obj.name = "test1" 155 | } 156 | } 157 | XCTAssertEqual(try! objectNotifications.toBlocking().first()!, "test1") 158 | 159 | DispatchQueue.global(qos: .background).async { 160 | let realm = realmInMemory(#function) 161 | try! realm.write { 162 | realm.objects(UniqueObject.self).first!.name = "test2" 163 | } 164 | } 165 | XCTAssertEqual(try! objectNotifications.toBlocking().first()!, "test2") 166 | 167 | // delete the object to trigger an error 168 | DispatchQueue.main.async { 169 | try! realm.write { 170 | realm.delete(obj) 171 | } 172 | } 173 | XCTAssertThrowsError(try objectNotifications.toBlocking().first()!) { error in 174 | XCTAssertEqual(error as! RxRealmError, RxRealmError.objectDeleted) 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmOnQueueTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmOnQueueTests.swift 3 | // RxRealm_Tests 4 | // 5 | // Created by Anton Nazarov on 18.05.2020. 6 | // Copyright © 2020 CocoaPods. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | import RxSwift 11 | import XCTest 12 | 13 | final class RxRealmOnQueueTests: XCTestCase { 14 | let allTests = [ 15 | "testCollectionOnQueue": testCollectionOnQueue, 16 | "testArrayOnQueue": testArrayOnQueue, 17 | "testChangesetOnQueue": testChangesetOnQueue 18 | ] 19 | func testCollectionOnQueue() { 20 | verifyObservableEmitOnBackground { 21 | Observable.collection(from: $0, synchronousStart: false, on: DispatchQueue(label: #function)) 22 | } 23 | } 24 | 25 | func testArrayOnQueue() { 26 | verifyObservableEmitOnBackground { 27 | Observable.array(from: $0, synchronousStart: false, on: DispatchQueue(label: #function)) 28 | } 29 | } 30 | 31 | func testChangesetOnQueue() { 32 | verifyObservableEmitOnBackground { 33 | Observable.changeset(from: $0, synchronousStart: false, on: DispatchQueue(label: #function)) 34 | } 35 | } 36 | 37 | private func verifyObservableEmitOnBackground(factory: (Results) -> Observable) { 38 | let realm = realmInMemory() 39 | DispatchQueue.main.async { 40 | try! realm.write { 41 | realm.add(UniqueObject(1)) 42 | } 43 | } 44 | let dispatchedOnMainTread = try! factory(realm.objects(UniqueObject.self)) 45 | .map { _ in Thread.isMainThread } 46 | .toBlocking(timeout: 2) 47 | .first() 48 | XCTAssertFalse(dispatchedOnMainTread!) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmRealmTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmRealmTests.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 5/22/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import RealmSwift 12 | import RxRealm 13 | import RxSwift 14 | 15 | 16 | class RxRealmRealmTests: XCTestCase { 17 | let allTests = [ 18 | "testRealmDidChangeNotifications": testRealmDidChangeNotifications, 19 | "testRealmRefreshRequiredNotifications": testRealmRefreshRequiredNotifications 20 | ] 21 | func testRealmDidChangeNotifications() { 22 | let realm = realmInMemory(#function) 23 | 24 | let realmNotifications = RxSwift.Observable<(Realm, Realm.Notification)>.from(realm: realm) 25 | 26 | DispatchQueue.main.async { 27 | try! realm.write { 28 | realm.add(Message("first")) 29 | } 30 | } 31 | 32 | XCTAssertEqual(try! realmNotifications.toBlocking().first()!.1, Realm.Notification.didChange) 33 | 34 | DispatchQueue.global(qos: .background).async { 35 | let realm = realmInMemory(#function) 36 | try! realm.write { 37 | realm.add(Message("second")) 38 | } 39 | } 40 | 41 | XCTAssertEqual(try! realmNotifications.toBlocking().first()!.1, Realm.Notification.didChange) 42 | } 43 | 44 | func testRealmRefreshRequiredNotifications() { 45 | let realm = realmInMemory(#function) 46 | realm.autorefresh = false 47 | 48 | let realmNotifications = RxSwift.Observable<(Realm, Realm.Notification)>.from(realm: realm) 49 | 50 | DispatchQueue.main.async { 51 | try! realm.write { 52 | realm.add(Message("first")) 53 | } 54 | } 55 | 56 | // on same thread change is delivered 57 | XCTAssertEqual(try! realmNotifications.toBlocking().first()!.1, Realm.Notification.didChange) 58 | 59 | DispatchQueue.global(qos: .background).async { 60 | let realm = realmInMemory(#function) 61 | try! realm.write { 62 | realm.add(Message("second")) 63 | } 64 | } 65 | 66 | // on different thread - refresh required 67 | XCTAssertEqual(try! realmNotifications.toBlocking().first()!.1, Realm.Notification.refreshRequired) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmResultsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmCollectionsTests.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 4/30/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | import RealmSwift 12 | import RxRealm 13 | import RxSwift 14 | 15 | class RxRealmResultsTests: XCTestCase { 16 | let allTests = [ 17 | "testResultsType": testResultsType, 18 | "testResultsTypeChangeset": testResultsTypeChangeset, 19 | "testResultsEmitsCollectionSynchronously": testResultsEmitsCollectionSynchronously, 20 | "testResultsEmitsChangesetSynchronously": testResultsEmitsChangesetSynchronously, 21 | "testResultsEmitsCollectionAsynchronously": testResultsEmitsCollectionAsynchronously, 22 | "testResultsEmitsChangesetAsynchronously": testResultsEmitsChangesetAsynchronously 23 | ] 24 | func testResultsType() { 25 | let realm = realmInMemory(#function) 26 | let messages = Observable.collection(from: realm.objects(Message.self)) 27 | .map { Array($0.map { $0.text }) } 28 | 29 | XCTAssertEqual(try! messages.toBlocking().first()!, []) 30 | 31 | DispatchQueue.main.async { 32 | try! realm.write { 33 | realm.add(Message("first")) 34 | } 35 | } 36 | 37 | XCTAssertEqual(try! messages.skip(1).toBlocking().first()!, ["first"]) 38 | 39 | DispatchQueue.main.async { 40 | try! realm.write { 41 | realm.delete(realm.objects(Message.self).first!) 42 | } 43 | } 44 | 45 | XCTAssertEqual(try! messages.skip(1).toBlocking().first()!, []) 46 | } 47 | 48 | func testResultsTypeChangeset() { 49 | let realm = realmInMemory(#function) 50 | let messages = Observable.changeset(from: realm.objects(Message.self)) 51 | .map(stringifyChanges) 52 | 53 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:0") 54 | 55 | DispatchQueue.main.async { 56 | try! realm.write { 57 | realm.add(Message("first")) 58 | } 59 | } 60 | 61 | XCTAssertEqual(try! messages.skip(1).toBlocking().first()!, "count:1 inserted:[0] deleted:[] updated:[]") 62 | 63 | DispatchQueue.global(qos: .background).async { 64 | let realm = realmInMemory(#function) 65 | try! realm.write { 66 | realm.delete(realm.objects(Message.self).first!) 67 | } 68 | } 69 | 70 | XCTAssertEqual(try! messages.skip(1).toBlocking().first()!, "count:0 inserted:[] deleted:[0] updated:[]") 71 | } 72 | 73 | func testResultsEmitsCollectionSynchronously() { 74 | let realm = realmInMemory(#function) 75 | let messages = Observable.collection(from: realm.objects(Message.self), synchronousStart: true) 76 | var result = false 77 | 78 | // synchornously set to true 79 | _ = messages.subscribe(onNext: { _ in 80 | result = true 81 | }) 82 | 83 | XCTAssertEqual(result, true) 84 | } 85 | 86 | func testResultsEmitsChangesetSynchronously() { 87 | let realm = realmInMemory(#function) 88 | let messages = Observable.changeset(from: realm.objects(Message.self), synchronousStart: true) 89 | var result = false 90 | 91 | // synchornously set to true 92 | _ = messages.subscribe(onNext: { _ in 93 | result = true 94 | }) 95 | 96 | XCTAssertEqual(result, true) 97 | } 98 | 99 | func testResultsEmitsCollectionAsynchronously() { 100 | let realm = realmInMemory(#function) 101 | let messages = Observable.collection(from: realm.objects(Message.self), synchronousStart: false) 102 | var result = false 103 | 104 | // synchornously set to true 105 | _ = messages.subscribe(onNext: { _ in 106 | result = true 107 | }) 108 | 109 | XCTAssertEqual(result, false) 110 | } 111 | 112 | func testResultsEmitsChangesetAsynchronously() { 113 | let realm = realmInMemory(#function) 114 | let messages = Observable.changeset(from: realm.objects(Message.self), synchronousStart: false) 115 | var result = false 116 | 117 | // synchornously set to true 118 | _ = messages.subscribe(onNext: { _ in 119 | result = true 120 | }) 121 | 122 | XCTAssertEqual(result, false) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealm extensions 3 | // 4 | // Copyright (c) 2016 RxSwiftCommunity. All rights reserved. 5 | // 6 | 7 | import XCTest 8 | 9 | import RealmSwift 10 | import RxRealm 11 | import RxSwift 12 | 13 | // TODO: remove 14 | func delay(_ delay: Double, closure: @escaping () -> Void) { 15 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure) 16 | } 17 | 18 | func delayInBackground(_ delay: Double, closure: @escaping () -> Void) { 19 | DispatchQueue.global(qos: DispatchQoS.QoSClass.background).asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure) 20 | } 21 | 22 | func addMessage(_ realm: Realm, text: String) { 23 | try! realm.write { 24 | realm.add(Message(text)) 25 | } 26 | } 27 | 28 | class RxRealm_Tests: XCTestCase { 29 | let allTests = [ 30 | "testEmittedResultsValues": testEmittedResultsValues, 31 | "testEmittedArrayValues": testEmittedArrayValues, 32 | "testEmittedChangeset": testEmittedChangeset, 33 | "testEmittedArrayChangeset": testEmittedArrayChangeset 34 | ] 35 | func testEmittedResultsValues() { 36 | let realm = realmInMemory(#function) 37 | let messages = Observable.collection(from: realm.objects(Message.self)) 38 | .skip(1) 39 | .map { Array($0.map { $0.text }) } 40 | 41 | DispatchQueue.main.async { 42 | addMessage(realm, text: "first(Results)") 43 | } 44 | XCTAssertEqual(try! messages.toBlocking().first()!, ["first(Results)"]) 45 | 46 | DispatchQueue.main.async { 47 | addMessage(realm, text: "second(Results)") 48 | } 49 | XCTAssertEqual(try! messages.toBlocking().first()!, ["first(Results)", "second(Results)"]) 50 | } 51 | 52 | func testEmittedArrayValues() { 53 | let realm = realmInMemory(#function) 54 | let messages = Observable.array(from: realm.objects(Message.self)) 55 | .skip(1) 56 | .map { $0.map { $0.text } } 57 | 58 | DispatchQueue.main.async { 59 | addMessage(realm, text: "first(Results)") 60 | } 61 | XCTAssertEqual(try! messages.toBlocking().first()!, ["first(Results)"]) 62 | 63 | DispatchQueue.main.async { 64 | addMessage(realm, text: "second(Results)") 65 | } 66 | XCTAssertEqual(try! messages.toBlocking().first()!, ["first(Results)", "second(Results)"]) 67 | } 68 | 69 | func testEmittedChangeset() { 70 | let realm = realmInMemory(#function) 71 | 72 | // initial data 73 | addMessage(realm, text: "first(Changeset)") 74 | 75 | let messagesInitial = Observable.changeset(from: realm.objects(Message.self).sorted(byKeyPath: "text")) 76 | .map(stringifyChanges) 77 | 78 | XCTAssertEqual(try! messagesInitial.toBlocking().first()!, "count:1") 79 | 80 | let messages = messagesInitial.skip(1) 81 | 82 | // insert 83 | DispatchQueue.main.async { 84 | addMessage(realm, text: "second(Changeset)") 85 | } 86 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:2 inserted:[1] deleted:[] updated:[]") 87 | 88 | // update 89 | DispatchQueue.main.async { 90 | try! realm.write { 91 | realm.objects(Message.self).filter("text='second(Changeset)'").first!.text = "third(Changeset)" 92 | } 93 | } 94 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:2 inserted:[] deleted:[] updated:[1]") 95 | 96 | // coalesced + delete 97 | DispatchQueue.main.async { 98 | try! realm.write { 99 | realm.add(Message("zzzzz(Changeset)")) 100 | realm.delete(realm.objects(Message.self).filter("text='first(Changeset)'").first!) 101 | } 102 | } 103 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:2 inserted:[1] deleted:[0] updated:[]") 104 | } 105 | 106 | func testEmittedArrayChangeset() { 107 | let realm = realmInMemory(#function) 108 | 109 | // initial data 110 | addMessage(realm, text: "first(Changeset)") 111 | 112 | let messagesInitial = Observable.arrayWithChangeset(from: realm.objects(Message.self).sorted(byKeyPath: "text")) 113 | .map { (arg) -> String in 114 | let (result, changes) = arg 115 | if let changes = changes { 116 | return "count:\(result.count) inserted:\(changes.inserted) deleted:\(changes.deleted) updated:\(changes.updated)" 117 | } else { 118 | return "count:\(result.count)" 119 | } 120 | } 121 | 122 | XCTAssertEqual(try! messagesInitial.toBlocking().first()!, "count:1") 123 | 124 | let messages = messagesInitial.skip(1) 125 | 126 | // insert 127 | DispatchQueue.main.async { 128 | addMessage(realm, text: "second(Changeset)") 129 | } 130 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:2 inserted:[1] deleted:[] updated:[]") 131 | 132 | // update 133 | DispatchQueue.main.async { 134 | try! realm.write { 135 | realm.objects(Message.self).filter("text='second(Changeset)'").first!.text = "third(Changeset)" 136 | } 137 | } 138 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:2 inserted:[] deleted:[] updated:[1]") 139 | 140 | // coalesced + delete 141 | DispatchQueue.main.async { 142 | try! realm.write { 143 | realm.add(Message("zzzzz(Changeset)")) 144 | realm.delete(realm.objects(Message.self).filter("text='first(Changeset)'").first!) 145 | } 146 | } 147 | XCTAssertEqual(try! messages.toBlocking().first()!, "count:2 inserted:[1] deleted:[0] updated:[]") 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/RxRealmWriteSinks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RxRealmWriteSinks.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 6/4/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | import RxBlocking 11 | import RxCocoa 12 | import RxRealm 13 | import RxSwift 14 | import XCTest 15 | 16 | enum WriteError: Error { 17 | case def 18 | } 19 | 20 | class RxRealmWriteSinks: XCTestCase { 21 | let allTests = [ 22 | "testRxAddObjectWithSuccess": testRxAddObjectWithSuccess, 23 | "testRxAddObjectWithError": testRxAddObjectWithError, 24 | "testRxAddSequenceWithSuccess": testRxAddSequenceWithSuccess, 25 | "testRxAddSequenceWithError": testRxAddSequenceWithError, 26 | "testRxAddUpdateObjectsWithSucess": testRxAddUpdateObjectsWithSucess, 27 | "testRxDeleteItem": testRxDeleteItem, 28 | "testRxDeleteItemWithError": testRxDeleteItemWithError, 29 | "testRxDeleteItemsWithSuccess": testRxDeleteItemsWithSuccess, 30 | "testRxDeleteItemsWithError": testRxDeleteItemsWithError, 31 | "testRxAddObjectsFromDifferentThreads": testRxAddObjectsFromDifferentThreads 32 | ] 33 | func testRxAddObjectWithSuccess() { 34 | let realm = realmInMemory(#function) 35 | let items = Observable.array(from: realm.objects(Message.self)) 36 | .map { $0.map { $0.text } } 37 | 38 | // show all speakers 39 | DispatchQueue.main.async { 40 | _ = Observable.just(Message("1")).subscribe(realm.rx.add()) 41 | } 42 | 43 | let result = try! items.skip(1).toBlocking(timeout: 1).first()! 44 | XCTAssertEqual(result[0], "1") 45 | } 46 | 47 | func testRxAddObjectWithError() { 48 | var conf = Realm.Configuration() 49 | conf.fileURL = URL(string: "/asdasdasdsad")! 50 | 51 | let recordedError = BehaviorRelay(value: nil) 52 | 53 | DispatchQueue.main.async { 54 | _ = Observable.just(Message("0")) 55 | .subscribe(Realm.rx.add(configuration: conf, update: .all, onError: { _, error in 56 | recordedError.accept(error) 57 | })) 58 | } 59 | 60 | let error = try! recordedError.asObservable().skip(1).toBlocking(timeout: 1).first()! 61 | XCTAssertNotNil(error) 62 | XCTAssertEqual((error! as NSError).code, 3) 63 | } 64 | 65 | func testRxAddSequenceWithSuccess() { 66 | let realm = realmInMemory(#function) 67 | let items = Observable.array(from: realm.objects(Message.self)) 68 | .map { $0.map { $0.text } } 69 | 70 | // show all speakers 71 | DispatchQueue.main.async { 72 | _ = Observable.just([Message("1"), Message("2")]).subscribe(realm.rx.add()) 73 | } 74 | 75 | let result = try! items.skip(1).toBlocking(timeout: 1).first()! 76 | XCTAssertEqual(result[0], "1") 77 | XCTAssertEqual(result[1], "2") 78 | } 79 | 80 | func testRxAddSequenceWithError() { 81 | var conf = Realm.Configuration() 82 | conf.fileURL = URL(string: "/asdasdasdsad")! 83 | 84 | let recordedError = BehaviorRelay(value: nil) 85 | 86 | // show all speakers 87 | DispatchQueue.main.async { 88 | _ = Observable.from([Message("1"), Message("2")]) 89 | .subscribe(Realm.rx.add(configuration: conf, update: .all, onError: { _, error in 90 | recordedError.accept(error) 91 | })) 92 | } 93 | 94 | let error = try! recordedError.asObservable().skip(1).toBlocking(timeout: 1).first()! 95 | XCTAssertNotNil(error) 96 | XCTAssertEqual((error! as NSError).code, 3) 97 | } 98 | 99 | func testRxAddUpdateObjectsWithSucess() { 100 | let realm = realmInMemory(#function) 101 | let items = Observable.array(from: realm.objects(UniqueObject.self).sorted(byKeyPath: "id")) 102 | .map { $0.map { $0.id } } 103 | 104 | // show all speakers 105 | DispatchQueue.main.async { 106 | _ = Observable.just([UniqueObject(1), UniqueObject(2)]).subscribe(realm.rx.add()) 107 | _ = Observable.just([UniqueObject(1), UniqueObject(3)]).subscribe(realm.rx.add(update: .all)) 108 | } 109 | 110 | let result = try! items.skip(1).take(2).toBlocking(timeout: 1).toArray() 111 | XCTAssertEqual(result[0], [1, 2]) 112 | XCTAssertEqual(result[1], [1, 2, 3]) 113 | } 114 | 115 | func testRxDeleteItem() { 116 | let realm = realmInMemory(#function) 117 | let items = Observable.array(from: realm.objects(UniqueObject.self)) 118 | .map { $0.map { $0.id } } 119 | 120 | let object1 = UniqueObject(1) 121 | let object2 = UniqueObject(2) 122 | try! realm.write { 123 | realm.add([object1, object2]) 124 | } 125 | 126 | // show all speakers 127 | DispatchQueue.main.async { 128 | _ = Observable.just(object1).subscribe(realm.rx.delete()) 129 | _ = Observable.just(object2).subscribe(Realm.rx.delete()) 130 | } 131 | 132 | let result = try! items.take(3).toBlocking(timeout: 1).toArray() 133 | XCTAssertEqual(result[0], [1, 2]) 134 | XCTAssertEqual(result[1], [2]) 135 | XCTAssertEqual(result[2], []) 136 | } 137 | 138 | func testRxDeleteItemWithError() { 139 | let object1 = UniqueObject(1) 140 | let recordedError = BehaviorRelay(value: nil) 141 | 142 | DispatchQueue.main.async { 143 | _ = Observable.just(object1) 144 | .subscribe(Realm.rx.delete(onError: { _, error in 145 | recordedError.accept(error) 146 | })) 147 | } 148 | 149 | let error = try! recordedError.asObservable().skip(1).toBlocking(timeout: 1).first()! 150 | XCTAssertNotNil(error) 151 | } 152 | 153 | func testRxDeleteItemsWithSuccess() { 154 | let realm = realmInMemory(#function) 155 | let items = Observable.array(from: realm.objects(UniqueObject.self).sorted(byKeyPath: "id")) 156 | .map { $0.map { $0.id } } 157 | 158 | let object1 = UniqueObject(1) 159 | let object2 = UniqueObject(2) 160 | let object3 = UniqueObject(3) 161 | let object4 = UniqueObject(4) 162 | 163 | try! realm.write { 164 | realm.add([object1, object2, object3, object4]) 165 | } 166 | 167 | // show all speakers 168 | DispatchQueue.main.async { 169 | _ = Observable.just([object1, object2]).subscribe(realm.rx.delete()) 170 | _ = Observable.just([object3, object4]).subscribe(Realm.rx.delete()) 171 | } 172 | 173 | let result = try! items.take(3).toBlocking(timeout: 1).toArray() 174 | XCTAssertEqual(result[0], [1, 2, 3, 4]) 175 | XCTAssertEqual(result[1], [3, 4]) 176 | XCTAssertEqual(result[2], []) 177 | } 178 | 179 | func testRxDeleteItemsWithError() { 180 | let object1 = UniqueObject(1) 181 | let object2 = UniqueObject(2) 182 | let recordedError = BehaviorRelay(value: nil) 183 | 184 | DispatchQueue.main.async { 185 | _ = Observable.just([object1, object2]) 186 | .subscribe(Realm.rx.delete(onError: { _, error in 187 | recordedError.accept(error) 188 | })) 189 | } 190 | 191 | let error = try! recordedError.asObservable().skip(1).toBlocking(timeout: 1).first()! 192 | XCTAssertNotNil(error) 193 | } 194 | 195 | func testRxAddObjectsFromDifferentThreads() { 196 | let realm = realmInMemory(#function) 197 | let conf = realm.configuration 198 | 199 | let items = Observable.array(from: realm.objects(UniqueObject.self).sorted(byKeyPath: "id")) 200 | .map { $0.map { $0.id } } 201 | 202 | // write on current thread 203 | _ = Observable.just(UniqueObject(1)).subscribe(realm.rx.add()) 204 | 205 | // write on background thread 206 | DispatchQueue.global(qos: .background).async { 207 | let realm = try! Realm(configuration: conf) 208 | _ = Observable.just(UniqueObject(2)) 209 | .subscribe(realm.rx.add()) 210 | } 211 | 212 | // write on main scheduler 213 | DispatchQueue.global(qos: .background).async { 214 | _ = Observable.just(UniqueObject(3)) 215 | .observe(on: MainScheduler.instance) 216 | .subscribe(Realm.rx.add(configuration: conf)) 217 | } 218 | 219 | // write on bg scheduler 220 | DispatchQueue.main.async { 221 | _ = Observable.just(UniqueObject(4)) 222 | .observe(on: ConcurrentDispatchQueueScheduler(queue: DispatchQueue.global(qos: .background))) 223 | .subscribe(Realm.rx.add(configuration: conf)) 224 | } 225 | 226 | // subscribe on main, write in bg 227 | DispatchQueue.main.async { 228 | _ = Observable.just([UniqueObject(5), UniqueObject(6)]) 229 | .observe(on: ConcurrentDispatchQueueScheduler(queue: DispatchQueue.global(qos: .background))) 230 | .subscribe(Realm.rx.add(configuration: conf)) 231 | } 232 | 233 | let until = Observable.array(from: realm.objects(UniqueObject.self)) 234 | .filter { $0.count == 6 } 235 | .delay(.milliseconds(100), scheduler: MainScheduler.instance) 236 | 237 | let result = try! items.take(until: until).toBlocking(timeout: 1).toArray() 238 | XCTAssertEqual(result.last!, [1, 2, 3, 4, 5, 6]) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/TestModels.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestModels.swift 3 | // RxRealm 4 | // 5 | // Created by Marin Todorov on 4/30/16. 6 | // Copyright © 2016 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | import RxRealm 12 | 13 | func realmInMemory(_ name: String = UUID().uuidString) -> Realm { 14 | var conf = Realm.Configuration() 15 | conf.inMemoryIdentifier = name 16 | return try! Realm(configuration: conf) 17 | } 18 | 19 | func stringifyChanges(_ arg: (AnyRealmCollection, RealmChangeset?)) -> String { 20 | let (result, changes) = arg 21 | if let changes = changes { 22 | return "count:\(result.count) inserted:\(changes.inserted) deleted:\(changes.deleted) updated:\(changes.updated)" 23 | } else { 24 | return "count:\(result.count)" 25 | } 26 | } 27 | 28 | // MARK: Message 29 | class Message: Object { 30 | @objc dynamic var text = "" 31 | 32 | let recipients = List() 33 | let mentions = LinkingObjects(fromType: User.self, property: "lastMessage") 34 | 35 | convenience init(_ text: String) { 36 | self.init() 37 | self.text = text 38 | } 39 | } 40 | 41 | // MARK: User 42 | class User: Object { 43 | @objc dynamic var name = "" 44 | @objc dynamic var lastMessage: Message? 45 | 46 | convenience init(_ name: String) { 47 | self.init() 48 | self.name = name 49 | } 50 | } 51 | 52 | // MARK: UniqueObject 53 | class UniqueObject: Object { 54 | @objc dynamic var id = 0 55 | @objc dynamic var name = "" 56 | 57 | convenience init(_ id: Int) { 58 | self.init() 59 | self.id = id 60 | } 61 | 62 | override class func primaryKey() -> String? { 63 | return "id" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/RxRealmTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(RxRealmLinkingObjectsTests.allTests), 7 | testCase(RxRealmListTests.allTests), 8 | testCase(RxRealmObjectTests.allTests), 9 | testCase(RxRealmOnQueueTests.allTests), 10 | testCase(RxRealmRealmTests.allTests), 11 | testCase(RxRealmResultsTests.allTests), 12 | testCase(RxRealm_Tests.allTests), 13 | testCase(RxRealmWriteSinks.allTests) 14 | ] 15 | } 16 | #endif 17 | -------------------------------------------------------------------------------- /assets/animatedChanges.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxSwiftCommunity/RxRealm/f64bf1c4b07612185eb0d4935b4ef9cf06679357/assets/animatedChanges.gif -------------------------------------------------------------------------------- /cleanup.sh: -------------------------------------------------------------------------------- 1 | if which swiftformat >/dev/null; then 2 | `which swiftformat` "Sources" --config .swiftformat --quiet 3 | `which swiftformat` "Tests" --config .swiftformat --quiet 4 | `which swiftformat` "Examples/RxRealmDemo-iOS" --config .swiftformat --quiet 5 | `which swiftformat` "Package.swift" --config .swiftformat --quiet 6 | else 7 | echo "error: swiftformat not installed, do `sh setup.sh` to install swiftformat." 8 | exit 1 9 | fi 10 | 11 | if which swiftlint >/dev/null; then 12 | `which swiftlint` autocorrect --quiet 13 | else 14 | echo "error: SwiftLint not installed, do `sh setup.sh` to install SwiftLint." 15 | exit 1 16 | fi 17 | 18 | if which xcodegen >/dev/null; then 19 | `which xcodegen` --spec project-carthage.yml 20 | else 21 | echo "error: XcodeGen not installed, do `sh setup.sh` to install XcodeGen." 22 | exit 1 23 | fi -------------------------------------------------------------------------------- /pods.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | export RELEASE_VERSION="$(git describe --abbrev=0 | tr -d '\n')" 4 | RELEASE_VERSION=${RELEASE_VERSION:1} 5 | pod lib lint --allow-warnings 6 | pod trunk push --allow-warnings -------------------------------------------------------------------------------- /project-carthage.yml: -------------------------------------------------------------------------------- 1 | name: RxRealm 2 | options: 3 | minimumXcodeGenVersion: "2.15.1" 4 | developmentLanguage: en 5 | usesTabs: false 6 | indentWidth: 2 7 | tabWidth: 2 8 | xcodeVersion: "1220" 9 | deploymentTarget: 10 | iOS: "11.0" 11 | macOS: "10.10" 12 | tvOS: "9.0" 13 | watchOS: "3.0" 14 | carthageExecutablePath: "`which carthage`" 15 | defaultConfig: "Release" 16 | configs: 17 | Debug: debug 18 | Release: release 19 | attributes: 20 | ORGANIZATIONNAME: RxSwiftCommunity 21 | schemes: 22 | RxRealm iOS: 23 | scheme: {} 24 | build: 25 | parallelizeBuild: true 26 | buildImplicitDependencies: true 27 | targets: 28 | RxRealm iOS: all 29 | RxRealmTests iOS: [test] 30 | run: 31 | config: Debug 32 | test: 33 | config: Debug 34 | gatherCoverageData: true 35 | targets: 36 | - RxRealmTests iOS 37 | profile: 38 | config: Release 39 | analyze: 40 | config: Debug 41 | archive: 42 | config: Release 43 | revealArchiveInOrganizer: true 44 | RxRealm macOS: 45 | scheme: {} 46 | build: 47 | parallelizeBuild: true 48 | buildImplicitDependencies: true 49 | targets: 50 | RxRealm macOS: all 51 | RxRealmTests macOS: [test] 52 | run: 53 | config: Debug 54 | test: 55 | config: Debug 56 | gatherCoverageData: true 57 | targets: 58 | - RxRealmTests macOS 59 | profile: 60 | config: Release 61 | analyze: 62 | config: Debug 63 | archive: 64 | config: Release 65 | revealArchiveInOrganizer: true 66 | RxRealm watchOS: 67 | scheme: {} 68 | build: 69 | parallelizeBuild: true 70 | buildImplicitDependencies: true 71 | targets: 72 | RxRealm watchOS: all 73 | run: 74 | config: Debug 75 | profile: 76 | config: Release 77 | analyze: 78 | config: Debug 79 | archive: 80 | config: Release 81 | revealArchiveInOrganizer: true 82 | RxRealm tvOS: 83 | scheme: {} 84 | build: 85 | parallelizeBuild: true 86 | buildImplicitDependencies: true 87 | targets: 88 | RxRealm tvOS: all 89 | RxRealmTests tvOS: [test] 90 | run: 91 | config: Debug 92 | test: 93 | config: Debug 94 | gatherCoverageData: true 95 | targets: 96 | - RxRealmTests tvOS 97 | profile: 98 | config: Release 99 | analyze: 100 | config: Debug 101 | archive: 102 | config: Release 103 | revealArchiveInOrganizer: true 104 | targets: 105 | RxRealm iOS: 106 | settings: 107 | PRODUCT_NAME: RxRealm 108 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-iOS 109 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 110 | SKIP_INSTALL: NO 111 | SUPPORTS_MACCATALYST: NO 112 | platform: iOS 113 | type: framework 114 | sources: 115 | - Sources/RxRealm 116 | dependencies: 117 | - carthage: Realm 118 | - carthage: RealmSwift 119 | - carthage: RxCocoa 120 | - carthage: RxSwift 121 | - carthage: RxRelay 122 | RxRealm macOS: 123 | settings: 124 | PRODUCT_NAME: RxRealm 125 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-macOS 126 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 127 | SKIP_INSTALL: NO 128 | platform: macOS 129 | type: framework 130 | sources: 131 | - Sources/RxRealm 132 | dependencies: 133 | - carthage: Realm 134 | - carthage: RealmSwift 135 | - carthage: RxCocoa 136 | - carthage: RxSwift 137 | - carthage: RxRelay 138 | RxRealm tvOS: 139 | settings: 140 | PRODUCT_NAME: RxRealm 141 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-tvOS 142 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 143 | SKIP_INSTALL: NO 144 | platform: tvOS 145 | type: framework 146 | sources: 147 | - Sources/RxRealm 148 | dependencies: 149 | - carthage: Realm 150 | - carthage: RealmSwift 151 | - carthage: RxCocoa 152 | - carthage: RxSwift 153 | - carthage: RxRelay 154 | RxRealm watchOS: 155 | settings: 156 | PRODUCT_NAME: RxRealm 157 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-watchOS 158 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 159 | SKIP_INSTALL: NO 160 | platform: watchOS 161 | type: framework 162 | sources: 163 | - Sources/RxRealm 164 | dependencies: 165 | - carthage: Realm 166 | - carthage: RealmSwift 167 | - carthage: RxCocoa 168 | - carthage: RxSwift 169 | - carthage: RxRelay 170 | RxRealmTests iOS: 171 | platform: iOS 172 | type: bundle.unit-test 173 | sources: 174 | - path: Tests/RxRealmTests 175 | dependencies: 176 | - target: RxRealm iOS 177 | - carthage: Realm 178 | - carthage: RealmSwift 179 | - carthage: RxCocoa 180 | - carthage: RxSwift 181 | - carthage: RxRelay 182 | - carthage: RxBlocking 183 | - carthage: RxTest 184 | RxRealmTests macOS: 185 | platform: macOS 186 | type: bundle.unit-test 187 | settings: 188 | CODE_SIGN_IDENTITY: "" 189 | sources: 190 | - path: Tests/RxRealmTests 191 | dependencies: 192 | - target: RxRealm macOS 193 | - carthage: Realm 194 | - carthage: RealmSwift 195 | - carthage: RxCocoa 196 | - carthage: RxSwift 197 | - carthage: RxRelay 198 | - carthage: RxBlocking 199 | - carthage: RxTest 200 | RxRealmTests tvOS: 201 | platform: tvOS 202 | type: bundle.unit-test 203 | sources: 204 | - path: Tests/RxRealmTests 205 | dependencies: 206 | - target: RxRealm tvOS 207 | - carthage: Realm 208 | - carthage: RealmSwift 209 | - carthage: RxCocoa 210 | - carthage: RxSwift 211 | - carthage: RxRelay 212 | - carthage: RxBlocking 213 | - carthage: RxTest 214 | 215 | -------------------------------------------------------------------------------- /project-spm.yml: -------------------------------------------------------------------------------- 1 | name: RxRealm-SPM 2 | options: 3 | minimumXcodeGenVersion: "2.15.1" 4 | developmentLanguage: en 5 | usesTabs: false 6 | indentWidth: 2 7 | tabWidth: 2 8 | xcodeVersion: "1220" 9 | deploymentTarget: 10 | iOS: "11.0" 11 | macOS: "10.10" 12 | tvOS: "9.0" 13 | watchOS: "3.0" 14 | carthageExecutablePath: "`which carthage`" 15 | defaultConfig: "Release" 16 | configs: 17 | Debug: debug 18 | Release: release 19 | attributes: 20 | ORGANIZATIONNAME: RxSwiftCommunity 21 | schemes: 22 | RxRealm iOS: 23 | scheme: {} 24 | build: 25 | parallelizeBuild: true 26 | buildImplicitDependencies: true 27 | targets: 28 | RxRealm iOS: all 29 | run: 30 | config: Debug 31 | test: 32 | config: Debug 33 | gatherCoverageData: true 34 | profile: 35 | config: Release 36 | analyze: 37 | config: Debug 38 | archive: 39 | config: Release 40 | revealArchiveInOrganizer: true 41 | RxRealm macOS: 42 | scheme: {} 43 | build: 44 | parallelizeBuild: true 45 | buildImplicitDependencies: true 46 | targets: 47 | RxRealm macOS: all 48 | run: 49 | config: Debug 50 | test: 51 | config: Debug 52 | gatherCoverageData: true 53 | profile: 54 | config: Release 55 | analyze: 56 | config: Debug 57 | archive: 58 | config: Release 59 | revealArchiveInOrganizer: true 60 | RxRealm watchOS: 61 | scheme: {} 62 | build: 63 | parallelizeBuild: true 64 | buildImplicitDependencies: true 65 | targets: 66 | RxRealm watchOS: all 67 | run: 68 | config: Debug 69 | profile: 70 | config: Release 71 | analyze: 72 | config: Debug 73 | archive: 74 | config: Release 75 | revealArchiveInOrganizer: true 76 | RxRealm tvOS: 77 | scheme: {} 78 | build: 79 | parallelizeBuild: true 80 | buildImplicitDependencies: true 81 | targets: 82 | RxRealm tvOS: all 83 | run: 84 | config: Debug 85 | test: 86 | config: Debug 87 | gatherCoverageData: true 88 | profile: 89 | config: Release 90 | analyze: 91 | config: Debug 92 | archive: 93 | config: Release 94 | revealArchiveInOrganizer: true 95 | packages: 96 | RxSwift: 97 | url: https://github.com/ReactiveX/RxSwift.git 98 | from: 6.0.0 99 | Realm: 100 | url: https://github.com/realm/realm-cocoa.git 101 | from: 10.21.1 102 | targets: 103 | RxRealm iOS: 104 | settings: 105 | PRODUCT_NAME: RxRealm 106 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-iOS 107 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 108 | SKIP_INSTALL: NO 109 | SUPPORTS_MACCATALYST: NO 110 | platform: iOS 111 | type: framework 112 | sources: 113 | - Sources/RxRealm 114 | dependencies: 115 | - package: Realm 116 | product: Realm 117 | - package: Realm 118 | product: RealmSwift 119 | - package: RxSwift 120 | product: RxSwift 121 | - package: RxSwift 122 | product: RxCocoa 123 | - package: RxSwift 124 | product: RxRelay 125 | RxRealm macOS: 126 | settings: 127 | PRODUCT_NAME: RxRealm 128 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-macOS 129 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 130 | SKIP_INSTALL: NO 131 | platform: macOS 132 | type: framework 133 | sources: 134 | - Sources/RxRealm 135 | dependencies: 136 | - package: Realm 137 | product: Realm 138 | - package: Realm 139 | product: RealmSwift 140 | - package: RxSwift 141 | product: RxSwift 142 | - package: RxSwift 143 | product: RxCocoa 144 | - package: RxSwift 145 | product: RxRelay 146 | RxRealm tvOS: 147 | settings: 148 | PRODUCT_NAME: RxRealm 149 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-tvOS 150 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 151 | SKIP_INSTALL: NO 152 | platform: tvOS 153 | type: framework 154 | sources: 155 | - Sources/RxRealm 156 | dependencies: 157 | - package: Realm 158 | product: Realm 159 | - package: Realm 160 | product: RealmSwift 161 | - package: RxSwift 162 | product: RxSwift 163 | - package: RxSwift 164 | product: RxCocoa 165 | - package: RxSwift 166 | product: RxRelay 167 | RxRealm watchOS: 168 | settings: 169 | PRODUCT_NAME: RxRealm 170 | PRODUCT_BUNDLE_IDENTIFIER: RxSwiftCommunity.RxRealm.RxRealm-watchOS 171 | BUILD_LIBRARY_FOR_DISTRIBUTION: YES 172 | SKIP_INSTALL: NO 173 | platform: watchOS 174 | type: framework 175 | sources: 176 | - Sources/RxRealm 177 | dependencies: 178 | - package: Realm 179 | product: Realm 180 | - package: Realm 181 | product: RealmSwift 182 | - package: RxSwift 183 | product: RxSwift 184 | - package: RxSwift 185 | product: RxCocoa 186 | - package: RxSwift 187 | product: RxRelay -------------------------------------------------------------------------------- /scripts/bump-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | git config --global user.name "RxRealm Maintainers" 5 | git config --global user.email "rx-realm@rxswift.org" 6 | npx standard-version 7 | echo "RELEASE_VERSION=$(git describe --abbrev=0 | tr -d '\n')" >> $GITHUB_ENV 8 | export VERSION="$(git describe --abbrev=0 | tr -d '\n')" 9 | VERSION=${VERSION:1} 10 | echo $VERSION 11 | npx podspec-bump -w -i "$VERSION" 12 | git add -A 13 | git commit --amend --no-edit -------------------------------------------------------------------------------- /scripts/carthage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # carthage.sh 4 | # Usage example: ./carthage.sh build --platform iOS 5 | 6 | set -euo pipefail 7 | 8 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) 9 | trap 'rm -f "$xcconfig"' INT TERM HUP EXIT 10 | 11 | # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise 12 | # the build will fail on lipo due to duplicate architectures. 13 | 14 | CURRENT_XCODE_VERSION=$(xcodebuild -version | grep "Build version" | cut -d' ' -f3) 15 | echo "EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$CURRENT_XCODE_VERSION = arm64 arm64e armv7 armv7s armv6 armv8" >> $xcconfig 16 | 17 | echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200__BUILD_$(XCODE_PRODUCT_BUILD_VERSION))' >> $xcconfig 18 | echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig 19 | 20 | export XCODE_XCCONFIG_FILE="$xcconfig" 21 | carthage build "$@" -------------------------------------------------------------------------------- /scripts/xcframeworks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | brew bundle 5 | rm -rf RxRealm-SPM.xcodeproj 6 | rm -rf xcarchives/* 7 | rm -rf RxRealm.xcframework.zip 8 | rm -rf RxRealm.xcframework 9 | 10 | xcodegen --spec project-spm.yml 11 | 12 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm iOS" -destination "generic/platform=iOS" -archivePath "xcarchives/RxRealm-iOS" SKIP_INSTALL=NO SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 13 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm iOS" -destination "generic/platform=iOS Simulator" -archivePath "xcarchives/RxRealm-iOS-Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 14 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm tvOS" -destination "generic/platform=tvOS" -archivePath "xcarchives/RxRealm-tvOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 15 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm tvOS" -destination "generic/platform=tvOS Simulator" -archivePath "xcarchives/RxRealm-tvOS-Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 16 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm macOS" -destination "generic/platform=macOS" -archivePath "xcarchives/RxRealm-macOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 17 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm watchOS" -destination "generic/platform=watchOS" -archivePath "xcarchives/RxRealm-watchOS" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 18 | xcodebuild archive -quiet -project RxRealm-SPM.xcodeproj -configuration Release -scheme "RxRealm watchOS" -destination "generic/platform=watchOS Simulator" -archivePath "xcarchives/RxRealm-watchOS-Simulator" SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE="bitcode" ENABLE_BITCODE=YES | xcpretty --color --simple 19 | 20 | xcodebuild -create-xcframework \ 21 | -framework "xcarchives/RxRealm-iOS-Simulator.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 22 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-iOS-Simulator.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 23 | -framework "xcarchives/RxRealm-iOS.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 24 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-iOS.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 25 | -framework "xcarchives/RxRealm-tvOS-Simulator.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 26 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-tvOS-Simulator.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 27 | -framework "xcarchives/RxRealm-tvOS.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 28 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-tvOS.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 29 | -framework "xcarchives/RxRealm-macOS.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 30 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-macOS.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 31 | -framework "xcarchives/RxRealm-watchOS-Simulator.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 32 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-watchOS-Simulator.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 33 | -framework "xcarchives/RxRealm-watchOS.xcarchive/Products/Library/Frameworks/RxRealm.framework" \ 34 | -debug-symbols ""$(pwd)"/xcarchives/RxRealm-watchOS.xcarchive/dSYMs/RxRealm.framework.dSYM" \ 35 | -output "RxRealm.xcframework" 36 | 37 | zip -r RxRealm.xcframework.zip RxRealm.xcframework 38 | rm -rf xcarchives/* 39 | rm -rf RxRealm.xcframework 40 | rm -rf RxRealm-SPM.xcodeproj --------------------------------------------------------------------------------