Showing preview only (707K chars total). Download the full file or copy to clipboard to get everything.
Repository: realm/realm-browser-osx
Branch: master
Commit: 7d36d6fe6f4c
Files: 171
Total size: 655.8 KB
Directory structure:
gitextract_izcrtqob/
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── Jenkinsfile
├── Podfile
├── README.md
├── RealmBrowser/
│ ├── Application/
│ │ ├── RLMApplicationDelegate+CrashReporting.h
│ │ ├── RLMApplicationDelegate+CrashReporting.m
│ │ ├── RLMApplicationDelegate.h
│ │ └── RLMApplicationDelegate.m
│ ├── Controllers/
│ │ ├── RLMDocumentController.h
│ │ ├── RLMDocumentController.m
│ │ ├── RLMEncryptionKeyWindowController.h
│ │ ├── RLMEncryptionKeyWindowController.m
│ │ ├── RLMExportIndicatorWindowController.h
│ │ ├── RLMExportIndicatorWindowController.m
│ │ ├── RLMInstanceTableViewController.h
│ │ ├── RLMInstanceTableViewController.m
│ │ ├── RLMObjectLinkSelectionViewController.h
│ │ ├── RLMObjectLinkSelectionViewController.m
│ │ ├── RLMRealmBrowserWindowController.h
│ │ ├── RLMRealmBrowserWindowController.m
│ │ ├── RLMTypeOutlineViewController.h
│ │ ├── RLMTypeOutlineViewController.m
│ │ ├── RLMViewController.h
│ │ └── RLMViewController.m
│ ├── Models/
│ │ ├── RLMArrayNavigationState.h
│ │ ├── RLMArrayNavigationState.m
│ │ ├── RLMArrayNode.h
│ │ ├── RLMArrayNode.m
│ │ ├── RLMClassNode.h
│ │ ├── RLMClassNode.m
│ │ ├── RLMClassProperty.h
│ │ ├── RLMClassProperty.m
│ │ ├── RLMDocument.h
│ │ ├── RLMDocument.m
│ │ ├── RLMNavigationStack.h
│ │ ├── RLMNavigationStack.m
│ │ ├── RLMNavigationState.h
│ │ ├── RLMNavigationState.m
│ │ ├── RLMObjectNode.h
│ │ ├── RLMObjectNode.m
│ │ ├── RLMQueryNavigationState.h
│ │ ├── RLMQueryNavigationState.m
│ │ ├── RLMRealmNode.h
│ │ ├── RLMRealmNode.m
│ │ ├── RLMRealmOutlineNode.h
│ │ ├── RLMResultsNode.h
│ │ ├── RLMResultsNode.m
│ │ ├── RLMTableColumn.h
│ │ ├── RLMTableColumn.m
│ │ ├── RLMTypeNode.h
│ │ └── RLMTypeNode.m
│ ├── Resources/
│ │ ├── Documents/
│ │ │ └── en.lproj/
│ │ │ └── Credits.rtf
│ │ ├── Icons/
│ │ │ └── RealmFileIcon.icns
│ │ ├── Images.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Mavericks10.9.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── RealmLocked.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── RealmUnlocked.imageset/
│ │ │ └── Contents.json
│ │ └── UI/
│ │ ├── Base.lproj/
│ │ │ ├── EncryptionKeyWindow.xib
│ │ │ ├── MainMenu.xib
│ │ │ └── RLMDocument.xib
│ │ ├── RLMExportIndicatorWindowController.xib
│ │ └── RLMObjectLinkSelectionViewController.xib
│ ├── Support/
│ │ ├── NSColor+ByteSizeFactory.h
│ │ ├── NSColor+ByteSizeFactory.m
│ │ ├── RLMBrowserConstants.h
│ │ ├── RLMBrowserConstants.m
│ │ ├── RLMDescriptions.h
│ │ ├── RLMDescriptions.m
│ │ ├── RLMModelExporter.h
│ │ ├── RLMModelExporter.m
│ │ ├── RLMTestDataGenerator.h
│ │ ├── RLMTestDataGenerator.m
│ │ ├── RLMUtils.h
│ │ ├── RLMUtils.m
│ │ ├── TestClasses.h
│ │ └── TestClasses.m
│ ├── Supporting Files/
│ │ ├── Realm Browser.entitlements
│ │ ├── RealmBrowser-Info.plist
│ │ ├── RealmBrowser-Prefix.pch
│ │ ├── en.lproj/
│ │ │ └── InfoPlist.strings
│ │ └── main.m
│ └── Views/
│ ├── RLMBadgeTableCellView.h
│ ├── RLMBadgeTableCellView.m
│ ├── RLMBasicTableCellView.h
│ ├── RLMBasicTableCellView.m
│ ├── RLMBoolTableCellView.h
│ ├── RLMBoolTableCellView.m
│ ├── RLMImageTableCellView.h
│ ├── RLMImageTableCellView.m
│ ├── RLMLinkTableCellView.h
│ ├── RLMLinkTableCellView.m
│ ├── RLMNumberTableCellView.h
│ ├── RLMNumberTableCellView.m
│ ├── RLMOptionalBoolTableCellView.h
│ ├── RLMOptionalBoolTableCellView.m
│ ├── RLMSidebarTableCellView.h
│ ├── RLMSidebarTableCellView.m
│ ├── RLMTableCellView.h
│ ├── RLMTableCellView.m
│ ├── RLMTableHeaderCell.h
│ ├── RLMTableHeaderCell.m
│ ├── RLMTableView.h
│ └── RLMTableView.m
├── RealmBrowser.xcodeproj/
│ ├── project.pbxproj
│ └── xcshareddata/
│ └── xcschemes/
│ └── Realm Browser.xcscheme
├── RealmBrowserSync/
│ ├── Controllers/
│ │ ├── RLMAccessTokenCredentialViewController.h
│ │ ├── RLMAccessTokenCredentialViewController.m
│ │ ├── RLMCloudKitCredentialViewController.h
│ │ ├── RLMCloudKitCredentialViewController.m
│ │ ├── RLMConnectToServerWindowController.h
│ │ ├── RLMConnectToServerWindowController.m
│ │ ├── RLMConnectionIndicatorWindowController.h
│ │ ├── RLMConnectionIndicatorWindowController.m
│ │ ├── RLMCredentialViewController+Private.h
│ │ ├── RLMCredentialViewController.h
│ │ ├── RLMCredentialViewController.m
│ │ ├── RLMCredentialsViewController.h
│ │ ├── RLMCredentialsViewController.m
│ │ ├── RLMFacebookCredentialViewController.h
│ │ ├── RLMFacebookCredentialViewController.m
│ │ ├── RLMGoogleCredentialViewController.h
│ │ ├── RLMGoogleCredentialViewController.m
│ │ ├── RLMLoginWindowController.h
│ │ ├── RLMLoginWindowController.m
│ │ ├── RLMOpenSyncURLWindowController.h
│ │ ├── RLMOpenSyncURLWindowController.m
│ │ ├── RLMSyncServerBrowserWindowController.h
│ │ ├── RLMSyncServerBrowserWindowController.m
│ │ ├── RLMUsernameCredentialViewController.h
│ │ ├── RLMUsernameCredentialViewController.m
│ │ ├── RLMWelcomeWindowController.h
│ │ └── RLMWelcomeWindowController.m
│ ├── Library/
│ │ ├── NSView+RLMExtensions.h
│ │ ├── NSView+RLMExtensions.m
│ │ ├── RLMKeychainInfo+RLMSyncCredentials.h
│ │ ├── RLMKeychainInfo+RLMSyncCredentials.m
│ │ ├── RLMKeychainInfo.h
│ │ ├── RLMKeychainInfo.m
│ │ ├── RLMKeychainStore.h
│ │ ├── RLMKeychainStore.m
│ │ ├── RLMSyncURLValueTransformer.h
│ │ ├── RLMSyncURLValueTransformer.m
│ │ ├── RLMSyncUtils.h
│ │ ├── RLMSyncUtils.m
│ │ ├── RLMWindowController.h
│ │ └── RLMWindowController.m
│ └── Resources/
│ ├── AccessTokenCredentialView.xib
│ ├── CloudKitCredentialView.xib
│ ├── ConnectToServerWindow.xib
│ ├── ConnectionIndicatorWindow.xib
│ ├── CredentialsView.xib
│ ├── FacebookCredentialView.xib
│ ├── GoogleCredentialView.xib
│ ├── LoginWindow.xib
│ ├── OpenSyncURLWindow.xib
│ ├── SyncServerBrowserWindow.xib
│ ├── UsernameCredentialView.xib
│ └── WelcomeWindow.xib
├── RealmBrowserTests/
│ ├── RLMTestObjects.h
│ ├── RLMTestObjects.m
│ ├── RealmBrowserTests.m
│ └── Supporting Files/
│ ├── RealmBrowserTests-Info.plist
│ └── en.lproj/
│ └── InfoPlist.strings
└── fastlane/
├── Appfile
├── Fastfile
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Xcode
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
Pods/
# fastlane
fastlane/report.xml
fastlane/test_output
================================================
FILE: .travis.yml
================================================
language: objective-c
osx_image: xcode8.3
install: bundle install
script: bundle exec fastlane test
================================================
FILE: CHANGELOG.md
================================================
3.0.1 Release notes (2017-11-03)
=============================================================
* Fix crash when deleting rows.
* Show the size of data properties in their cell.
3.0.0 Release notes (2017-10-16)
=============================================================
* Update to Realm 3.0.0.
2.1.12 Release notes (2017-07-27)
=============================================================
* Update to Realm 2.9.0 that fixes an issue connecting to multiple servers with the same user.
2.1.11 Release notes (2017-07-17)
=============================================================
* Fix crash when import from CSV/XLSX
* Fix crash when deleting currently displayed array from another process
* Fix crash when open realm while Simulator startup
2.1.10 Release notes (2017-06-27)
=============================================================
* Improved user experience when connect to the Realm Object Server
* Hide internal realms from server browser window
* Adds an ability to add object links to lists
2.1.9 Release notes (2017-06-13)
=============================================================
* Fixed several issues related to opening and displaying synced realms after schema migration
* Fixed issue with logging out the user when working with multiple realms simultaneously
2.1.8 Release notes (2017-05-17)
=============================================================
* Fixed crash when opening realms from Object Server
* Fixed crash when searching for special characters
* Fixed C# model definitions export
* Save credentials when connecting to Object Server
* Object Server Browser window doesn’t close after opening realm
2.1.7 Release notes (2017-03-29)
=============================================================
* Updated to Realm 2.5 to support new file format
* Fixed the issue with selection when filter realms on Realm Object Server
2.1.6 Release notes (2017-03-02)
=============================================================
* Fixed an issue with token expiration for Realms opened from Realm Mobile Platform
* Stability improvements
2.1.5 Release notes (2017-02-10)
=============================================================
* Added the ability to save C# and JavaScript (@ksibod) model definitions
* Added an option to copy object's property value in context menu (@fergusean)
* Fixed compatibility issue with OS X 10.9
* Minor UI improvements
2.1.4 Release notes (2017-01-31)
=============================================================
* Updated to Realm 2.4.1 that fixes an authentication issue when working with synced Realms.
2.1.3 Release notes (2017-01-17)
=============================================================
* Fixed CSV export of synced realms
* Stability and performance improvements
2.1.2 Release notes (2016-12-09)
=============================================================
* Added the ability to filter Realms on Realm Object Server
* Improved error description if Realm file failed to open and for other errors
* Disabled editing values for primary key properties
* Fixed crash when removed object is being edited while table view reloading
* Fixed multiline text editing
* Fixed dates editing
* Fixed other UI issues
2.1.1 Release notes (2016-11-29)
=============================================================
* Added in-app crash reporting
* Added the ability to connect to Realm Object Server with admin username/password
* Improved property type detection when import from CSV files
* Fixed export to Compacted Realm
* Fixed crash related to sync metadata reset
2.0.1 Release notes (2016-10-06)
=============================================================
This release introduces support for the Realm Mobile Platform!
See <https://realm.io/news/introducing-realm-mobile-platform/> for an overview
of these great new features.
* Fixed opening encryped Realms
* Fixed export to compressed Realm
* Fixed some UI issues
2.0.0 Release notes (2016-09-27)
=============================================================
This release introduces support for the Realm Mobile Platform!
See <https://realm.io/news/introducing-realm-mobile-platform/> for an overview
of these great new features.
* Support for Realm Mobile Platform
* Fixed export to CSV files
1.0.2 Release notes (2016-08-30)
=============================================================
* Updated Realm Browser app and file format icons to Realm's new look
* Adding support for optional fields (Java exporter)
* Improved Realm file discovery in "Open Common Location"
* Added the ability for multiple CSV file importing
0.103.1 Release notes (2016-05-20)
=============================================================
* Updated to Realm Objective-C 0.103.1
* UI layout and performance enhancements
0.102.1 Release notes (2016-05-17)
=============================================================
* Updated to Realm Objective-C 0.102.1
0.102.0 Release notes (2016-05-12)
=============================================================
* Updated to Realm Objective-C 0.102.0
0.100.0 Release notes (2016-05-02)
=============================================================
* Updated to Realm Objective-C 0.100.0
* *(This fixes a bug where encrypted Realm files opened in the Browser would sometimes become corrupted.)*
* Fixed a crash in OS X Mavericks
0.98.6 Release notes (2016-03-29)
=============================================================
* Updated to Realm 0.98.6
0.98.5 Release notes (2016-03-15)
=============================================================
* Updated to Realm 0.98.5
* Added the ability to generate Swift model classes from a Realm file.
* Improvements to the Objective-C model generation, including generics and Realm's latest features.
* Improvements to the Java model generation, handling primary, indexed and required fields.
* Fixed a rendering issue involving properties in different objects that had the same name.
0.97.0 Release notes (2016-01-14)
=============================================================
* Updated to Realm to Objective-C 0.97.0.
* Improvements to the scrolling performance of the main table view in Realm documents.
* String-based searches in Realm files made non-case sensitive.
* Added 'Cut', 'Copy', 'Paste' and 'Select All' menu options, initially to make editing text values easier.
* Fixed an issue where editing properties in Realm objects weren't properly saving to disk.
* Added tooltip for boolean valued columns.
* Improved user experience when opening Ream files.
0.96.2 Release notes (2015-10-12)
=============================================================
Enhancements:
* Updated Realm to Objective-C 0.96.2
* Added a prompt to alert users about the mandatory format upgrade in Realm Objective-C 0.96.
Bug Fixes:
* Fixed an issue where the width of text fields wouldn't update when resizing columns in OS X El Capitan.
0.95.2 Release notes (2015-10-12)
=============================================================
Enhancements:
* Updated to Realm 0.95.2
* Add the ability to set child Realm objects in parent objects.
* Export compressed copies of Realm files.
* Open encrypted Realm files.
Bug Fixes:
* Update project for Xcode 7
* Fix several warnings when compiling in OS X 10.11
* Fixed a bug where object number badges were displaying incorrectly in OS X 10.11
* Added a Retina version of the "Lock" icon.
0.93.0 App Store Release Notes (2015-08-4)
=============================================================
Enhancements:
* OSX sandbox handling added to file read/write operations.
* New file format icon
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## Filing Issues
Whether you find a bug, typo or an API call that could be clarified, please [file an issue](https://github.com/realm/realm-browser-osx/issues) on our GitHub repository.
When filing an issue, please provide as much of the following information as possible in order to help others fix it:
1. **Goals**
2. **Expected results**
3. **Actual results**
4. **Steps to reproduce**
5. **Code sample that highlights the issue** (full Xcode projects that we can compile ourselves are ideal)
6. **Version of Realm/Xcode/OSX**
If you'd like to send us sensitive sample code to help troubleshoot your issue, you can email <help@realm.io> directly.
## Contributing Enhancements
We love contributions to Realm Browser! If you'd like to contribute code, documentation, or any other improvements, please [file a Pull Request](https://github.com/realm/realm-browser-osx/pulls) on our GitHub repository. Make sure to accept our [CLA](#CLA) and to follow our [style guide](https://github.com/realm/realm-cocoa/wiki/Objective-C-Style-Guide).
### CLA
Realm welcomes all contributions! The only requirement we have is that, like many other projects, we need to have a [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_License_Agreement) (CLA) in place before we can accept any external code. Our own CLA is a modified version of the Apache Software Foundation’s CLA.
[Please submit your CLA electronically using our Google form](https://docs.google.com/forms/d/1bVp-Wp5nmNFz9Nx-ngTmYBVWVdwTyKj4T0WtfVm0Ozs/viewform?fbzx=4154977190905366979) so we can accept your submissions. The GitHub username you file there will need to match that of your Pull Requests. If you have any questions or cannot file the CLA electronically, you can email <help@realm.io>.
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
gem 'cocoapods'
gem 'fastlane'
================================================
FILE: Jenkinsfile
================================================
#!groovy
node('osx_vegas') {
dir('realm-browser') {
wrap([$class: 'AnsiColorBuildWrapper']) {
stage('SCM') {
checkout([
$class: 'GitSCM',
branches: scm.branches,
gitTool: 'native git',
extensions: scm.extensions + [[$class: 'CleanCheckout']],
userRemoteConfigs: scm.userRemoteConfigs
])
}
sh "bundle install"
stage('Test') {
// FIXME: enable tests
// sh "bundle exec fastlane test"
}
stage('Build') {
withEnv([
'DEVELOPER_DIR=/Applications/Xcode-8.2.app/Contents/Developer/'
]) {
sh "bundle exec fastlane build"
}
}
stage('Archive') {
def currentVersion = getVersion()
def archiveName = "realm-browser-${currentVersion}-build-artifacts.zip"
dir("build") {
sh "zip --symlinks -r ${archiveName} *"
archive "${archiveName}"
}
}
}
}
}
def getVersion() {
sh '''
awk '/<key>CFBundleShortVersionString<\\/key>/ { getline; gsub("<[^>]*>", ""); gsub(/\\t/,""); print $0 }' RealmBrowser/Supporting\\ Files/RealmBrowser-Info.plist > currentversion
'''
def versionNumber = readFile('currentversion').readLines()[0]
sh "git rev-parse HEAD | cut -b1-8 > sha.txt"
def sha = readFile('sha.txt').readLines().last().trim()
return "${versionNumber}-${sha}"
}
================================================
FILE: Podfile
================================================
source 'https://github.com/CocoaPods/Specs.git'
platform :osx, '10.9'
use_frameworks!
target 'RealmBrowser' do
pod 'AppSandboxFileAccess'
pod 'Realm', '~> 3.0.2'
pod 'RealmConverter', '~> 0.5.0'
pod 'HockeySDK-Mac'
target 'RealmBrowserTests' do
# It looks like that inheritance via search paths is still broken with frameworks, see https://github.com/CocoaPods/CocoaPods/issues/4944
# inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.0'
end
end
end
================================================
FILE: README.md
================================================
# NOTE - This tool is now deprecated!
Our future development efforts will go into the new cross platform Realm Studio, now available for Mac, Linux and Windows users!
You can download it from the [Realm website](https://realm.io/docs/realm-object-server/latest/#realm-studio).
Should you have any feedback or issues, please create issues in the [Realm Object Server](https://github.com/realm/realm-object-server) repo.
# Realm Browser
Realm Browser is a small utility for Mac OS X that lets you open .realm files to view and modify their contents.

[](https://travis-ci.org/realm/realm-browser-osx)
## Installing
### Mac App Store (Recommended)
Download the app in the [Mac App Store](https://itunes.apple.com/us/app/realm-browser/id1007457278?mt=12).
### Manual Build
Download the project, run `pod install` and build the `Realm Browser` scheme using Xcode 8.
### GitHub Releases
Download the built app in [releases](https://github.com/realm/realm-browser-osx/releases/).
### Homebrew Cask
If you have [homebrew](http://brew.sh) installed, simply run `brew cask install realm-browser`. You may need to run `brew cask update` if homebrew says `realm-browser` is not available.
## Design Goals
The main design goals of Realm Browser are:
* Allow quick and easy access to the contents of .realm files.
* Be able to modify the contents of .realm files without needing to use code.
* Make it easier to automatically generate Realm Object source files.
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for more details!
This project adheres to the [Contributor Covenant Code of Conduct](https://realm.io/conduct).
By participating, you are expected to uphold this code. Please report
unacceptable behavior to [info@realm.io](mailto:info@realm.io).
## License
The source code to Realm Browser is licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
================================================
FILE: RealmBrowser/Application/RLMApplicationDelegate+CrashReporting.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMApplicationDelegate.h"
@interface RLMApplicationDelegate (CrashReporting)
- (void)setupCrashReporting;
@end
================================================
FILE: RealmBrowser/Application/RLMApplicationDelegate+CrashReporting.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import HockeySDK;
#import "RLMApplicationDelegate+CrashReporting.h"
static NSString * const RLMHockeyAppIdentifier = @"ca846ff98222427fa286a4bc76bc7533";
@implementation RLMApplicationDelegate (CrashReporting)
- (void)setupCrashReporting {
BITHockeyManager *manager = [BITHockeyManager sharedHockeyManager];
[manager configureWithIdentifier:RLMHockeyAppIdentifier];
[manager startManager];
}
@end
================================================
FILE: RealmBrowser/Application/RLMApplicationDelegate.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Foundation;
@interface RLMApplicationDelegate : NSObject <NSApplicationDelegate>
- (IBAction)openSyncURL:(id)sender;
- (IBAction)connectToSyncServer:(id)sender;
@end
================================================
FILE: RealmBrowser/Application/RLMApplicationDelegate.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Realm;
@import Realm.Private;
@import RealmConverter;
@import AppSandboxFileAccess;
#import "RLMApplicationDelegate.h"
#import "RLMApplicationDelegate+CrashReporting.h"
#import "RLMBrowserConstants.h"
#import "RLMDocumentController.h"
#import "RLMTestDataGenerator.h"
#import "RLMUtils.h"
#import "TestClasses.h"
#import "RLMWelcomeWindowController.h"
#import "RLMOpenSyncURLWindowController.h"
#import "RLMConnectToServerWindowController.h"
#import "RLMSyncServerBrowserWindowController.h"
#import "RLMSyncUtils.h"
@interface RLMApplicationDelegate ()
@property (nonatomic, weak) IBOutlet NSMenu *fileMenu;
@property (nonatomic, weak) IBOutlet NSMenuItem *openMenuItem;
@property (nonatomic, weak) IBOutlet NSMenuItem *openEncryptedMenuItem;
@property (nonatomic, weak) IBOutlet NSMenu *openAnyRealmMenu;
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSMetadataQuery *realmQuery;
@property (nonatomic, strong) NSMetadataQuery *appQuery;
@property (nonatomic, strong) NSMetadataQuery *projQuery;
@property (nonatomic, strong) NSArray *groupedFileItems;
@property (nonatomic, strong) NSMutableArray *auxiliaryWindowControllers;
@end
@implementation RLMApplicationDelegate
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
NSString* urlString = [event paramDescriptorForKeyword:keyDirectObject].stringValue;
NSURL *realmURL = [NSURL URLWithString:urlString];
[self openSyncURL:realmURL credentials:nil authServerURL:nil];
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
// Will set sharedController
[RLMDocumentController new];
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[self setupCrashReporting];
[[NSUserDefaults standardUserDefaults] setObject:@(kTopTipDelay) forKey:@"NSInitialToolTipDelay"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self logOutSyncUsers];
[RLMSyncManager sharedManager].errorHandler = ^(NSError *error, RLMSyncSession *session) {
[NSApp presentError:error];
};
self.realmQuery = [[NSMetadataQuery alloc] init];
[self.realmQuery setSortDescriptors:@[[[NSSortDescriptor alloc] initWithKey:(id)kMDItemContentModificationDate ascending:NO]]];
NSPredicate *realmPredicate = [NSPredicate predicateWithFormat:@"kMDItemFSName like[c] '*.realm'"];
self.realmQuery.predicate = realmPredicate;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(realmQueryNote:) name:nil object:self.realmQuery];
[self.realmQuery startQuery];
self.appQuery = [[NSMetadataQuery alloc] init];
NSPredicate *appPredicate = [NSPredicate predicateWithFormat:@"kMDItemFSName like[c] '*.app'"];
self.appQuery.predicate = appPredicate;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherQueryNote:) name:nil object:self.appQuery];
self.projQuery = [[NSMetadataQuery alloc] init];
NSPredicate *projPredicate = [NSPredicate predicateWithFormat:@"kMDItemFSName like[c] '*.xcodeproj'"];
self.projQuery.predicate = projPredicate;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(otherQueryNote:) name:nil object:self.projQuery];
self.dateFormatter = [[NSDateFormatter alloc] init];
self.dateFormatter.dateStyle = NSDateFormatterMediumStyle;
self.dateFormatter.timeStyle = NSDateFormatterShortStyle;
if ([notification.userInfo[NSApplicationLaunchIsDefaultLaunchKey] boolValue] && ![[NSProcessInfo processInfo] environment][@"TESTING"]) {
[self showWelcomeWindow:nil];
}
}
- (void)applicationWillTerminate:(NSNotification *)notification {
[self logOutSyncUsers];
}
- (BOOL)application:(NSApplication *)application openFile:(NSString *)filename {
[self openFileAtURL:[NSURL fileURLWithPath:filename]];
return YES;
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
if (filenames.count > kMaxNumberOfFilesAtOnce) {
NSString *message = [NSString stringWithFormat:@"Are you sure you wish to open all %lu Realm files?", (unsigned long)filenames.count];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:message];
[alert setInformativeText:@"Opening too many files at once may result in Realm Browser becoming unstable."];
[alert addButtonWithTitle:@"Yes"];
[alert addButtonWithTitle:@"Cancel"];
[alert setAlertStyle:NSWarningAlertStyle];
if ([alert runModal] != NSAlertFirstButtonReturn)
return;
}
for (NSString *filename in filenames)
[self openFileAtURL:[NSURL fileURLWithPath:filename]];
}
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)application
{
return NO;
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)application hasVisibleWindows:(BOOL)flag
{
if (!flag) {
[self showWelcomeWindow:nil];
}
return NO;
}
- (NSError *)application:(NSApplication *)application willPresentError:(NSError *)error {
NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];
if (!underlyingError || error.userInfo[NSLocalizedRecoverySuggestionErrorKey]) {
return error;
}
// Add recovery suggestion from underlying error
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
userInfo[NSLocalizedRecoverySuggestionErrorKey] = underlyingError.userInfo[NSLocalizedDescriptionKey];
return [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
}
#pragma mark - Welcome Window
- (IBAction)showWelcomeWindow:(id)sender {
static BOOL toutDisplayedDuringAppRun = NO;
static NSString *toutSuppressionKey = @"RealmStudio_suppress_tout";
RLMWelcomeWindowController *welcomeWindowController = [[RLMWelcomeWindowController alloc] init];
[welcomeWindowController.window center];
[welcomeWindowController showWindow:nil completionHandler:^(NSModalResponse returnCode) {
[self removeAuxiliaryWindowController:welcomeWindowController];
}];
[self addAuxiliaryWindowController:welcomeWindowController];
if (!toutDisplayedDuringAppRun && ![[NSUserDefaults standardUserDefaults] boolForKey:toutSuppressionKey]) {
toutDisplayedDuringAppRun = YES;
NSAlert *upgradeAlert = [[NSAlert alloc] init];
[upgradeAlert addButtonWithTitle:@"Get Realm Studio..."];
[upgradeAlert addButtonWithTitle:@"Not now"];
[upgradeAlert setShowsSuppressionButton:YES];
[upgradeAlert setMessageText:@"Realm Browser is deprecated"];
[upgradeAlert setInformativeText:@"Realm Browser has been replaced by Realm Studio. Bug fixes and enhancements will only be available via Realm Studio going forwards."];
NSInteger result = [upgradeAlert runModal];
if (result == NSAlertFirstButtonReturn) {
// Open the web site.
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://realm.io/products/realm-studio/"]];
}
if ([upgradeAlert suppressionButton].state == NSOnState) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:toutSuppressionKey];
}
}
}
#pragma mark - Event handling
- (void)realmQueryNote:(NSNotification *)notification {
if ([[notification name] isEqualToString:NSMetadataQueryDidFinishGatheringNotification]) {
[self updateFileItems];
[self.appQuery startQuery];
[self.projQuery startQuery];
}
else if ([[notification name] isEqualToString:NSMetadataQueryDidUpdateNotification]) {
[self updateFileItems];
[self.appQuery startQuery];
}
}
- (void)otherQueryNote:(NSNotification *)notification {
if ([[notification name] isEqualToString:NSMetadataQueryDidFinishGatheringNotification]) {
[self updateFileItems];
}
else if ([[notification name] isEqualToString:NSMetadataQueryDidUpdateNotification]) {
[self updateFileItems];
}
}
-(void)menuNeedsUpdate:(NSMenu *)menu
{
if (menu == self.openAnyRealmMenu) {
[menu removeAllItems];
NSArray *allItems = [self.groupedFileItems valueForKeyPath:@"Items.@unionOfArrays.self"];
[self updateMenu:menu withItems:allItems indented:YES];
}
}
-(void)updateMenu:(NSMenu *)menu withItems:(NSArray *)items indented:(BOOL)indented
{
NSImage *image = [NSImage imageNamed:@"RealmFileIcon"];
image.size = NSMakeSize(kMenuImageSize, kMenuImageSize);
for (id item in items) {
// Category heading, create disabled menu item with corresponding name
if ([item isKindOfClass:[NSString class]]) {
NSMenuItem *categoryItem = [[NSMenuItem alloc] init];
categoryItem.title = (NSString *)item;
categoryItem.enabled = NO;
[menu addItem:categoryItem];
}
// Array of items, create cubmenu and set them up there by calling this method recursively
else if ([item isKindOfClass:[NSArray class]]) {
NSMenuItem *submenuItem = [[NSMenuItem alloc] init];
submenuItem.title = @"More";
submenuItem.indentationLevel = 1;
[menu addItem:submenuItem];
NSMenu *submenu = [[NSMenu alloc] initWithTitle:@"More"];
NSArray *subitems = item;
[self updateMenu:submenu withItems:subitems indented:NO];
[menu setSubmenu:submenu forItem:submenuItem];
}
// Normal file item, just create a menu item for it and wire it up
else if ([item isMemberOfClass:[NSMetadataItem class]]) {
NSMetadataItem *metadataItem = (NSMetadataItem *)item;
// Get the path to the realm and see if there is additional info for it, such as app name
NSString *path = [metadataItem valueForAttribute:NSMetadataItemPathKey];
NSString *title = [[path lastPathComponent] stringByAppendingString:[self extraInfoForRealmWithPath:path]];
// Create a menu item using the title and link it with opening the file
NSMenuItem *menuItem = [[NSMenuItem alloc] init];
menuItem.title = title;
menuItem.representedObject = [NSURL fileURLWithPath:path];
menuItem.target = self;
menuItem.action = @selector(openFileWithMenuItem:);
menuItem.image = image;
menuItem.indentationLevel = indented ? 1 : 0;
// Give the menu item a tooltip with modification date and full path
NSDate *date = [metadataItem valueForAttribute:NSMetadataItemFSContentChangeDateKey];
NSString *dateString = [self.dateFormatter stringFromDate:date];
menuItem.toolTip = [NSString stringWithFormat:@"%@\n\nModified: %@", path, dateString];
[menu addItem:menuItem];
}
}
}
-(NSString *)extraInfoForRealmWithPath:(NSString *)realmPath
{
NSArray *searchPaths;
NSString *searchEndPath;
NSString *developerPrefix = [NSHomeDirectory() stringByAppendingPathComponent:kDeveloperFolder];
NSString *simulatorPrefix = [NSHomeDirectory() stringByAppendingPathComponent:kSimulatorFolder];
if ([realmPath hasPrefix:developerPrefix]) {
// The realm file is in the simulator, so we are looking for *.xcodeproj files
searchPaths = [self.projQuery results];
searchEndPath = developerPrefix;
}
else if ([realmPath hasPrefix:simulatorPrefix]) {
// The realm file is in the simulator, so we are looking for *.app files
searchPaths = [self.appQuery results];
searchEndPath = simulatorPrefix;
}
else {
// We have no extra info for this containing folder
return @"";
}
// Search at most four levels up for a corresponding app/project file
for (NSUInteger i = 0; i < 4; i++) {
// Go up one level in the file hierachy by deleting last path component
realmPath = [[realmPath stringByDeletingLastPathComponent] copy];
if ([realmPath isEqualToString:searchEndPath]) {
// Reached end of iteration, the respective folder we are searching within
return @"";
}
for (NSString *pathItem in searchPaths) {
NSMetadataItem *metadataItem = (NSMetadataItem *)pathItem;
NSString *foundPath = [metadataItem valueForAttribute:NSMetadataItemPathKey];
if ([[foundPath stringByDeletingLastPathComponent] isEqualToString:realmPath]) {
// Found a project/app file, returning it in formatted form
NSString *extraInfo = [[[foundPath pathComponents] lastObject] stringByDeletingPathExtension];
return [NSString stringWithFormat: @" - %@", extraInfo];
}
}
}
// Tried four levels up and still found nothing, nor reached containing folder. Giving up
return @"";
}
-(void)updateFileItems
{
NSString *homeDir = RLMRealHomeDirectory();
NSString *kPrefix = @"Prefix";
NSString *kItems = @"Items";
NSString *simPrefix = [homeDir stringByAppendingPathComponent:kSimulatorFolder];
NSDictionary *simDict = @{kPrefix : simPrefix, kItems : [NSMutableArray arrayWithObject:@"iPhone Simulator"]};
NSString *devPrefix = [homeDir stringByAppendingPathComponent:kDeveloperFolder];
NSDictionary *devDict = @{kPrefix : devPrefix, kItems : [NSMutableArray arrayWithObject:@"Developer"]};
NSString *desktopPrefix = [homeDir stringByAppendingPathComponent:kDesktopFolder];
NSDictionary *desktopDict = @{kPrefix : desktopPrefix, kItems : [NSMutableArray arrayWithObject:@"Desktop"]};
NSString *downloadPrefix = [homeDir stringByAppendingPathComponent:kDownloadFolder];
NSDictionary *downloadDict = @{kPrefix : downloadPrefix, kItems : [NSMutableArray arrayWithObject:@"Download"]};
NSString *documentsPrefix = [homeDir stringByAppendingPathComponent:kDocumentsFolder];
NSDictionary *documentsdDict = @{kPrefix : documentsPrefix, kItems : [NSMutableArray arrayWithObject:@"Documents"]};
NSString *allPrefix = @"/";
NSDictionary *otherDict = @{kPrefix : allPrefix, kItems : [NSMutableArray arrayWithObject:@"Other"]};
// Create array of dictionaries, each corresponding to search folders
NSArray *tempGroupedFileItems = @[simDict, devDict, desktopDict, documentsdDict, downloadDict, otherDict];
// Iterate through all search results
for (NSMetadataItem *fileItem in self.realmQuery.results) {
// Iterate through the different prefixes and add item to corresponding array within dictionary
for (NSDictionary *dict in tempGroupedFileItems) {
if ([[fileItem valueForAttribute:NSMetadataItemPathKey] hasPrefix:dict[kPrefix]]) {
NSMutableArray *items = dict[kItems];
// The first few items are just added
if (items.count - 1 < kMaxFilesPerCategory) {
[items addObject:fileItem];
}
// When we reach the maximum number of files to show in the overview we create an array...
else if (items.count - 1 == kMaxFilesPerCategory) {
NSMutableArray *moreFileItems = [NSMutableArray arrayWithObject:fileItem];
[items addObject:moreFileItems];
}
// ... and henceforth we put fileItems here instead - the menu method will create a submenu.
else {
NSMutableArray *moreFileItems = [items lastObject];
[moreFileItems addObject:fileItem];
}
// We have already found a matching prefix, we can stop considering this item
break;
}
}
}
// Do not include empty groups
self.groupedFileItems = [tempGroupedFileItems filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K.@count > 1", kItems]];
}
- (IBAction)generatedDemoDatabase:(id)sender
{
// Find the document directory using it as default location for realm file.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *directories = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *url = [directories firstObject];
// Prompt the user for location af new realm file.
[self showSavePanelStringFromDirectory:url completionHandler:^(BOOL userSelectedFile, NSURL *selectedFile) {
NSURL *directoryURL = [selectedFile URLByDeletingLastPathComponent];
AppSandboxFileAccess *fileAccess = [AppSandboxFileAccess fileAccess];
[fileAccess requestAccessPermissionsForFileURL:directoryURL persistPermission:YES withBlock:^(NSURL *securelyScopedURL, NSData *bookmarkData) {
[securelyScopedURL startAccessingSecurityScopedResource];
// If the user has selected a file url for storing the demo database, we first check if the
// file already exists (and is actually a file) we delete the old file before creating the
// new demo file.
if (userSelectedFile) {
NSString *path = selectedFile.path;
BOOL isDirectory = NO;
if ([fileManager fileExistsAtPath:path isDirectory:&isDirectory]) {
if (!isDirectory) {
NSError *error;
[fileManager removeItemAtURL:selectedFile error:&error];
}
}
NSArray *classNames = @[[RealmTestClass0 className], [RealmTestClass1 className], [RealmTestClass2 className]];
BOOL success = [RLMTestDataGenerator createRealmAtUrl:selectedFile withClassesNamed:classNames objectCount:1000];
if (success) {
NSAlert *alert = [[NSAlert alloc] init];
alert.alertStyle = NSInformationalAlertStyle;
alert.showsHelp = NO;
alert.informativeText = @"A demo database has been generated. Would you like to open it?";
alert.messageText = @"Open demo database?";
[alert addButtonWithTitle:@"Open"];
[alert addButtonWithTitle:@"Cancel"];
NSUInteger response = [alert runModal];
if (response == NSAlertFirstButtonReturn) {
[self openFileAtURL:selectedFile];
}
}
}
//As realm files perform some file-system level cleanup during their dealloc phase,
//make sure the sandbox access is removed in the next run loop to give it some time to finish.
dispatch_async(dispatch_get_main_queue(), ^{
[securelyScopedURL stopAccessingSecurityScopedResource];
});
}];
}];
}
#pragma mark - Import Methods -
- (IBAction)importFileFromXLSX:(id)sender
{
// Get the file to import
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.canChooseDirectories = NO;
openPanel.canChooseFiles = YES;
openPanel.canCreateDirectories = YES;
openPanel.allowsMultipleSelection = NO;
openPanel.message = @"Please choose the XLSX file you wish to import.";
openPanel.allowedFileTypes = @[@"xlsx"];
NSInteger result = [openPanel runModal];
if (result != NSFileHandlingPanelOKButton) {
return;
}
NSURL *targetFileURL = openPanel.URL;
// Get the destination folder to save the Realm file
NSOpenPanel *savePanel = [NSOpenPanel openPanel];
savePanel.canChooseDirectories = YES;
savePanel.canChooseFiles = NO;
savePanel.canCreateDirectories = YES;
savePanel.allowsMultipleSelection = NO;
savePanel.message = @"Please choose the destination folder for the new Realm file.";
result = [savePanel runModal];
if (result != NSFileHandlingPanelOKButton) {
return;
}
NSURL *targetDirectoryURL = savePanel.URL;
NSString *realmFilePath = [targetDirectoryURL.path stringByAppendingPathComponent:@"default.realm"];
if ([[NSFileManager defaultManager] fileExistsAtPath:realmFilePath]) {
NSAlert *alert = [NSAlert alertWithMessageText:@"A Realm file named \"default.realm\" already exists in that location. Do you wish to proceed?"
defaultButton:@"Cancel"
alternateButton:@"OK"
otherButton:nil
informativeTextWithFormat:@"The existing file will be deleted and replaced with a new one. This operation cannot be undone."];
NSInteger result = [alert runModal];
if (result > 0) {
return;
}
[[NSFileManager defaultManager] removeItemAtPath:realmFilePath error:nil];
}
AppSandboxFileAccess *fileAccess = [AppSandboxFileAccess fileAccess];
[fileAccess requestAccessPermissionsForFileURL:targetDirectoryURL persistPermission:YES withBlock:^(NSURL *securelyScopedURL, NSData *bookmarkData) {
[securelyScopedURL startAccessingSecurityScopedResource];
@autoreleasepool {
RLMImportSchemaGenerator *schemaGenerator = [[RLMImportSchemaGenerator alloc] initWithFile:targetFileURL.path encoding:EncodingUtf8];
RLMImportSchema *schema = [schemaGenerator generatedSchemaWithError:nil];
if (schema == nil) {
NSAlert *alert = [NSAlert alertWithMessageText:@"Unable to Generate Schema" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"Please check the file is in the correct format and try again."];
[alert runModal];
return;
}
RLMXLSXDataImporter *importer = [[RLMXLSXDataImporter alloc] initWithFile:targetFileURL.path encoding:EncodingUtf8];
NSError *error;
if (![importer importToPath:targetDirectoryURL.path withSchema:schema error:&error]) {
[NSApp presentError:error];
}
}
[securelyScopedURL stopAccessingSecurityScopedResource];
}];
}
- (IBAction)importFileFromCSV:(id)sender
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.canChooseDirectories = NO;
openPanel.canChooseFiles = YES;
openPanel.canCreateDirectories = YES;
openPanel.allowsMultipleSelection = YES;
openPanel.message = @"Please choose the CSV files you wish to import.";
openPanel.allowedFileTypes = @[@"csv"];
NSInteger result = [openPanel runModal];
if (result != NSFileHandlingPanelOKButton) {
return;
}
NSArray *fileURLs = openPanel.URLs;
NSMutableArray *filePaths = [NSMutableArray array];
for (NSURL *url in fileURLs) {
[filePaths addObject:url.path];
}
NSOpenPanel *savePanel = [NSOpenPanel openPanel];
savePanel.canChooseDirectories = YES;
savePanel.canChooseFiles = NO;
savePanel.canCreateDirectories = YES;
savePanel.allowsMultipleSelection = NO;
savePanel.message = @"Please choose the destination folder for the new Realm file.";
result = [savePanel runModal];
if (result != NSFileHandlingPanelOKButton) {
return;
}
NSURL *targetDirectoryURL = savePanel.URL;
NSString *realmFilePath = [targetDirectoryURL.path stringByAppendingPathComponent:@"default.realm"];
if ([[NSFileManager defaultManager] fileExistsAtPath:realmFilePath]) {
NSAlert *alert = [NSAlert alertWithMessageText:@"A Realm file named \"default.realm\" already exists in that location. Do you wish to proceed?"
defaultButton:@"Cancel"
alternateButton:@"OK"
otherButton:nil
informativeTextWithFormat:@"The existing file will be deleted and replaced with a new one. This operation cannot be undone."];
NSInteger result = [alert runModal];
if (result > 0) {
return;
}
[[NSFileManager defaultManager] removeItemAtPath:realmFilePath error:nil];
}
AppSandboxFileAccess *fileAccess = [AppSandboxFileAccess fileAccess];
[fileAccess requestAccessPermissionsForFileURL:targetDirectoryURL persistPermission:YES withBlock:^(NSURL *securelyScopedURL, NSData *bookmarkData) {
[securelyScopedURL startAccessingSecurityScopedResource];
@autoreleasepool {
RLMImportSchemaGenerator *schemaGenerator = [[RLMImportSchemaGenerator alloc] initWithFiles:filePaths encoding:EncodingUtf8];
RLMImportSchema *schema = [schemaGenerator generatedSchemaWithError:nil];
if (schema == nil) {
NSAlert *alert = [NSAlert alertWithMessageText:@"Unable to Generate Schema" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"Please check the files are in the correct format and try again."];
[alert runModal];
return;
}
RLMCSVDataImporter *importer = [[RLMCSVDataImporter alloc] initWithFiles:filePaths encoding:EncodingUtf8];
[importer importToPath:targetDirectoryURL.path withSchema:schema error:nil];
}
[securelyScopedURL stopAccessingSecurityScopedResource];
}];
}
#pragma mark - Private methods
- (void)openFileWithMenuItem:(NSMenuItem *)menuItem
{
[self openFileAtURL:menuItem.representedObject];
}
- (void)openFileAtURL:(NSURL *)url
{
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfURL:url display:YES completionHandler:^(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error) {
if (error != nil) {
[NSApp presentError:error];
}
}];
}
- (void)openSyncURL:(NSURL *)syncURL credentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL {
[(RLMDocumentController *)[NSDocumentController sharedDocumentController] openDocumentWithContentsOfSyncURL:syncURL credentials:credentials authServerURL:authServerURL display:YES completionHandler:^(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error) {
if (error != nil) {
[NSApp presentError:error];
}
}];
}
- (void)showSavePanelStringFromDirectory:(NSURL *)directoryUrl completionHandler:(void(^)(BOOL userSelectesFile, NSURL *selectedFile))completion
{
NSSavePanel *savePanel = [NSSavePanel savePanel];
// Restrict the file type to whatever you like
savePanel.allowedFileTypes = @[kRealmFileExtension];
// Set the starting directory
savePanel.directoryURL = directoryUrl;
// And show another dialog headline than "Save"
savePanel.title = @"Generate";
savePanel.prompt = @"Generate";
// Perform other setup
// Use a completion handler -- this is a block which takes one argument
// which corresponds to the button that was clicked
[savePanel beginWithCompletionHandler:^(NSInteger result){
if (result == NSFileHandlingPanelOKButton) {
// Close panel before handling errors
[savePanel orderOut:self];
// Notify caller about the file selected
completion(YES, savePanel.URL);
}
else {
completion(NO, nil);
}
}];
}
#pragma mark - Auxiliary Windows Management
- (void)addAuxiliaryWindowController:(NSWindowController *)windowController {
if (self.auxiliaryWindowControllers == nil) {
self.auxiliaryWindowControllers = [NSMutableArray new];
}
[self.auxiliaryWindowControllers addObject:windowController];
}
- (void)removeAuxiliaryWindowController:(NSWindowController *)windowController {
[self.auxiliaryWindowControllers removeObject:windowController];
}
- (__kindof NSWindowController *)auxiliaryWindowControllerOfClass:(Class)windowControllerClass {
for (NSWindowController *windowController in self.auxiliaryWindowControllers) {
if ([windowController isKindOfClass:windowControllerClass]) {
return windowController;
}
}
return nil;
}
#pragma mark - Sync
- (IBAction)openSyncURL:(id)sender {
RLMOpenSyncURLWindowController *openSyncURLWindowController = [self auxiliaryWindowControllerOfClass:[RLMOpenSyncURLWindowController class]];
if (openSyncURLWindowController != nil) {
[openSyncURLWindowController.window makeKeyAndOrderFront:sender];
return;
}
openSyncURLWindowController = [[RLMOpenSyncURLWindowController alloc] init];
[openSyncURLWindowController showWindow:sender completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseOK) {
[self openSyncURL:openSyncURLWindowController.url credentials:openSyncURLWindowController.credentials authServerURL:nil];
}
[self removeAuxiliaryWindowController:openSyncURLWindowController];
}];
[self addAuxiliaryWindowController:openSyncURLWindowController];
}
- (IBAction)connectToSyncServer:(id)sender {
RLMConnectToServerWindowController *connectToServerWindowController = [self auxiliaryWindowControllerOfClass:[RLMConnectToServerWindowController class]];
if (connectToServerWindowController != nil) {
[connectToServerWindowController.window makeKeyAndOrderFront:sender];
return;
}
connectToServerWindowController = [[RLMConnectToServerWindowController alloc] init];
[connectToServerWindowController showWindow:sender completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseOK) {
NSURL *serverURL = connectToServerWindowController.serverURL;
RLMSyncCredentials *credentials = connectToServerWindowController.credentials;
[self connectToServerAtURL:serverURL withAdminCredentials:credentials];
}
[self removeAuxiliaryWindowController:connectToServerWindowController];
}];
[self addAuxiliaryWindowController:connectToServerWindowController];
}
- (void)connectToServerAtURL:(NSURL *)serverURL withAdminCredentials:(RLMSyncCredentials *)credentials {
NSURL *authServerURL = authServerURLForSyncURL(serverURL);
[RLMSyncUser logInWithCredentials:credentials authServerURL:authServerURL onCompletion:^(RLMSyncUser *user, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (user == nil) {
[NSApp presentError:error];
[self connectToSyncServer:nil];
} else {
RLMSyncServerBrowserWindowController *browserWindowController = [[RLMSyncServerBrowserWindowController alloc] initWithServerURL:serverURL user:user];
browserWindowController.onSelectURL = ^(NSURL *url) {
[self openSyncURL:url credentials:credentials authServerURL:authServerURL];
};
[browserWindowController showWindow:nil completionHandler:^(NSModalResponse returnCode) {
[self removeAuxiliaryWindowController:browserWindowController];
}];
[self addAuxiliaryWindowController:browserWindowController];
}
});
}];
}
- (void)logOutSyncUsers {
// Log out all the logged in users to cleanup chached realms
[[RLMSyncUser allUsers] enumerateKeysAndObjectsUsingBlock:^(NSString *key, RLMSyncUser *user, BOOL *stop) {
[user logOut];
}];
}
#pragma mark - Other
- (IBAction)visitRealmStudioSite:(NSMenuItem *)sender {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://realm.io/products/realm-studio/"]];
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMDocumentController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Cocoa;
@import Realm;
NS_ASSUME_NONNULL_BEGIN
@interface RLMDocumentController : NSDocumentController
- (void)openDocumentWithContentsOfSyncURL:(NSURL *)url credentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL * __nullable)authServerURL display:(BOOL)displayDocument completionHandler:(void (^)(NSDocument * __nullable document, BOOL documentWasAlreadyOpen, NSError * __nullable error))completionHandler;
@end
NS_ASSUME_NONNULL_END
================================================
FILE: RealmBrowser/Controllers/RLMDocumentController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Realm;
#import "RLMDocumentController.h"
#import "RLMDocument.h"
#import "RLMBrowserConstants.h"
@implementation RLMDocumentController
- (void)openDocumentWithContentsOfSyncURL:(NSURL *)url credentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL display:(BOOL)displayDocument completionHandler:(void (^)(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error))completionHandler; {
RLMDocument *document = [self documentForURL:url];
if (document != nil) {
completionHandler(document, YES, nil);
return;
}
NSError *error;
document = [[RLMDocument alloc] initWithContentsOfSyncURL:url credentials:credentials authServerURL:authServerURL error:&error];
if (document != nil) {
[self addDocument:document];
if (displayDocument) {
[document makeWindowControllers];
[document showWindows];
}
}
// NSDocumentController calls completion handler asynchronously for new documents
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(document, NO, error);
});
}
- (NSString *)typeForContentsOfURL:(NSURL *)url error:(NSError * _Nullable __autoreleasing *)outError {
if ([url.scheme isEqualToString:kRealmURLScheme] || [url.scheme isEqualToString:kSecureRealmURLScheme]) {
return kRealmUTIIdentifier;
} else {
return [super typeForContentsOfURL:url error:outError];
}
}
- (void)openDocument:(id)sender {
@try {
[super openDocument:sender];
} @catch (NSException *exception) {
// NSOpenPanel in sandboxed environment doesn't handle changes in path and crashes if one of the parent directories has been renamed.
// This case usually happens when users try to open realm file from simulator's directory while Xcode launches an app and changes
// simulator root path.
// See https://rink.hockeyapp.net/manage/apps/405793/app_versions/27/crash_reasons/176155071.
NSAlert *alert = [NSAlert alertWithMessageText:@"Failed to open realm file" defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@"File has been moved to a different location, please try to open it again."];
[alert runModal];
}
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMEncryptionKeyWindowController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMWindowController.h"
@interface RLMEncryptionKeyWindowController : RLMWindowController
@property (readonly) NSData *encryptionKey;
@end
================================================
FILE: RealmBrowser/Controllers/RLMEncryptionKeyWindowController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMEncryptionKeyWindowController.h"
@interface RLMEncryptionKeyWindowController () <NSTextFieldDelegate>
@property (nonatomic, weak) IBOutlet NSTextField *keyTextField;
@property (nonatomic, weak) IBOutlet NSButton *okayButton;
@property (nonatomic, strong) NSData *encryptionKey;
@end
@implementation RLMEncryptionKeyWindowController
- (void)controlTextDidChange:(NSNotification *)notification {
NSString *stringValue = self.keyTextField.stringValue;
//Ensure only hex-compatible characters have been entered
NSCharacterSet *chars = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"] invertedSet];
BOOL isValid = (NSNotFound == [stringValue rangeOfCharacterFromSet:chars].location) && stringValue.length == 128;
self.okayButton.enabled = isValid;
}
- (IBAction)okayButtonClicked:(id)sender {
NSData *encryptionKey = [self dataFromHexadecimalString:self.keyTextField.stringValue];
self.encryptionKey = encryptionKey;
[self closeWithReturnCode:NSModalResponseOK];
}
// http://stackoverflow.com/a/13627835/599344
- (NSData *)dataFromHexadecimalString:(NSString *)string
{
string = [string lowercaseString];
NSMutableData *data= [NSMutableData new];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i = 0;
NSInteger length = string.length;
while (i < length-1) {
char c = [string characterAtIndex:i++];
if (c < '0' || (c > '9' && c < 'a') || c > 'f')
continue;
byte_chars[0] = c;
byte_chars[1] = [string characterAtIndex:i++];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
return data;
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMExportIndicatorWindowController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import <Cocoa/Cocoa.h>
@interface RLMExportIndicatorWindowController : NSWindowController
@end
================================================
FILE: RealmBrowser/Controllers/RLMExportIndicatorWindowController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMExportIndicatorWindowController.h"
@interface RLMExportIndicatorWindowController ()
@property (nonatomic, weak) IBOutlet NSProgressIndicator *progressIndicator;
@end
@implementation RLMExportIndicatorWindowController
- (instancetype)init
{
if (self = [super initWithWindowNibName:@"RLMExportIndicatorWindowController"]) {
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
[self.progressIndicator startAnimation:self];
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMInstanceTableViewController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Cocoa;
#import "RLMViewController.h"
#import "RLMTableView.h"
@class RLMRealmBrowserWindowController;
@class RLMArrayNode;
@interface RLMInstanceTableViewController : RLMViewController <RLMTableViewDelegate, RLMTableViewDataSource>
@property (nonatomic, readonly) RLMTableView *realmTableView;
@property (nonatomic) BOOL realmIsLocked;
@property (nonatomic) BOOL displaysArray;
@property (nonatomic, copy) void(^didSelectedBlock)(RLMObject *rowObject);
- (void)reloadData;
@end
================================================
FILE: RealmBrowser/Controllers/RLMInstanceTableViewController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMInstanceTableViewController.h"
@import Foundation;
@import Realm.Dynamic;
#import "RLMRealmBrowserWindowController.h"
#import "RLMObjectLinkSelectionViewController.h"
#import "RLMArrayNavigationState.h"
#import "RLMQueryNavigationState.h"
#import "RLMArrayNode.h"
#import "RLMResultsNode.h"
#import "RLMRealmNode.h"
#import "RLMBadgeTableCellView.h"
#import "RLMBasicTableCellView.h"
#import "RLMBoolTableCellView.h"
#import "RLMOptionalBoolTableCellView.h"
#import "RLMNumberTableCellView.h"
#import "RLMImageTableCellView.h"
#import "RLMTableColumn.h"
#import "NSColor+ByteSizeFactory.h"
#import "objc/objc-class.h"
#import "RLMDescriptions.h"
NSString * const kRLMObjectType = @"RLMObjectType";
static const NSInteger NOT_A_COLUMN = -1;
static const NSInteger NOT_A_ROW = -1;
static const NSInteger ARRAY_GUTTER_INDEX = -1;
typedef NS_ENUM(int32_t, RLMUpdateType) {
RLMUpdateTypeRealm,
RLMUpdateTypeTableView
};
@implementation RLMInstanceTableViewController {
BOOL awake;
BOOL linkCursorDisplaying;
NSDateFormatter *dateFormatter;
NSNumberFormatter *numberFormatter;
NSMutableDictionary *autofittedColumns;
RLMDescriptions *realmDescriptions;
}
#pragma mark - NSObject Overrides
- (void)awakeFromNib
{
[super awakeFromNib];
if (awake) {
return;
}
[self.tableView setTarget:self];
[self.tableView setAction:@selector(userClicked:)];
[self.tableView setDoubleAction:@selector(userDoubleClicked:)];
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateStyle = NSDateFormatterMediumStyle;
dateFormatter.timeStyle = NSDateFormatterShortStyle;
numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
linkCursorDisplaying = NO;
autofittedColumns = [NSMutableDictionary dictionary];
realmDescriptions = [[RLMDescriptions alloc] init];
[self.tableView registerForDraggedTypes:@[kRLMObjectType]];
[self.tableView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
awake = YES;
}
- (void)reloadData {
[self.tableView reloadData];
}
#pragma mark - Public methods - Accessors
- (RLMTableView *)realmTableView
{
return (RLMTableView *)self.tableView;
}
#pragma mark - RLMViewController Overrides
- (void)performUpdateUsingState:(RLMNavigationState *)newState oldState:(RLMNavigationState *)oldState
{
[super performUpdateUsingState:newState oldState:oldState];
[self.tableView setAutosaveTableColumns:NO];
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
if ([newState isMemberOfClass:[RLMNavigationState class]]) {
self.displayedType = newState.selectedType;
[self.realmTableView setupColumnsWithType:newState.selectedType];
if (newState.selectedInstanceIndex != NSNotFound) {
[self setSelectionIndex:newState.selectedInstanceIndex];
}
}
else if ([newState isMemberOfClass:[RLMArrayNavigationState class]]) {
RLMArrayNavigationState *arrayState = (RLMArrayNavigationState *)newState;
RLMClassNode *referringType = (RLMClassNode *)arrayState.selectedType;
RLMObject *referingInstance = [referringType instanceAtIndex:arrayState.selectedInstanceIndex];
RLMArrayNode *arrayNode = [[RLMArrayNode alloc] initWithReferringProperty:arrayState.property
onObject:referingInstance
realm:realm];
self.displayedType = arrayNode;
[self.realmTableView setupColumnsWithType:arrayNode];
[self setSelectionIndex:arrayState.arrayIndex];
}
else if ([newState isMemberOfClass:[RLMQueryNavigationState class]]) {
RLMQueryNavigationState *queryState = (RLMQueryNavigationState *)newState;
RLMResultsNode *resultsNode = [[RLMResultsNode alloc] initWithQuery:queryState.searchText
result:queryState.results
andParent:queryState.selectedType];
self.displayedType = resultsNode;
[self.realmTableView setupColumnsWithType:resultsNode];
[self setSelectionIndex:0];
}
self.tableView.autosaveName = [NSString stringWithFormat:@"%lu:%@", realm.hash, self.displayedType.name];
[self.tableView setAutosaveTableColumns:YES];
if (![autofittedColumns[self.tableView.autosaveName] isEqual:@YES]) {
[self.realmTableView makeColumnsFitContents];
autofittedColumns[self.tableView.autosaveName] = @YES;
}
}
#pragma mark - NSTableView Data Source
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
if (tableView != self.tableView) {
return 0;
}
return self.displayedType.instanceCount;
}
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
{
if (self.realmIsLocked || !self.displaysArray) {
return NO;
}
NSData *indexSetData = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pboard declareTypes:@[kRLMObjectType] owner:self];
[pboard setData:indexSetData forType:kRLMObjectType];
return YES;
}
- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
{
if (operation == NSTableViewDropAbove) {
return NSDragOperationMove;
}
return NSDragOperationNone;
}
-(void)tableView:(NSTableView *)tableView draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint forRowIndexes:(NSIndexSet *)rowIndexes {
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)destination dropOperation:(NSTableViewDropOperation)operation
{
if (self.realmIsLocked || !self.displaysArray) {
return NO;
}
// Check that the dragged item is of correct type
NSArray *supportedTypes = @[kRLMObjectType];
NSPasteboard *draggingPasteboard = [info draggingPasteboard];
NSString *availableType = [draggingPasteboard availableTypeFromArray:supportedTypes];
if ([availableType compare:kRLMObjectType] == NSOrderedSame) {
NSData *rowIndexData = [draggingPasteboard dataForType:kRLMObjectType];
NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowIndexData];
// Performs the move in the realm
[self moveRowsInRealmFrom:rowIndexes to:destination];
return YES;
}
return NO;
}
#pragma mark - RLMTableView Data Source
-(NSString *)headerToolTipForColumn:(RLMClassProperty *)propertyColumn
{
numberFormatter.maximumFractionDigits = 3;
if (propertyColumn.property.array) {
return nil;
}
// For certain types we want to add some statistics
RLMPropertyType type = propertyColumn.property.type;
NSString *propertyName = propertyColumn.property.name;
if (![self.displayedType isKindOfClass:[RLMClassNode class]]) {
return nil;
}
RLMResults *results = ((RLMClassNode *)self.displayedType).allObjects;
switch (type) {
case RLMPropertyTypeInt:
case RLMPropertyTypeFloat:
case RLMPropertyTypeDouble: {
numberFormatter.minimumFractionDigits = (type == RLMPropertyTypeInt) ? 0 : 3;
NSString *min = [numberFormatter stringFromNumber:[results minOfProperty:propertyName]];
NSString *avg = [numberFormatter stringFromNumber:[results averageOfProperty:propertyName]];
NSString *max = [numberFormatter stringFromNumber:[results maxOfProperty:propertyName]];
NSString *sum = [numberFormatter stringFromNumber:[results sumOfProperty:propertyName]];
return [NSString stringWithFormat:@"Minimum: %@\nAverage: %@\nMaximum: %@\nSum: %@", min, avg, max, sum];
}
case RLMPropertyTypeDate: {
NSString *min = [dateFormatter stringFromDate:[results minOfProperty:propertyName]];
NSString *max = [dateFormatter stringFromDate:[results maxOfProperty:propertyName]];
return [NSString stringWithFormat:@"Earliest: %@\nLatest: %@", min, max];
}
case RLMPropertyTypeBool: {
NSUInteger count = results.count;
if (count == 0) return nil;
// we have to query for both, as there might also be NULL values.
NSUInteger trueCount = [results objectsWhere:@"%K == YES", propertyName].count;
NSUInteger falseCount = [results objectsWhere:@"%K == NO", propertyName].count;
float percentTrue = trueCount * 100.0 / count;
float percentFalse = falseCount * 100.0 / count;
return [NSString stringWithFormat:@"True: %lu (%.1f%%)\nFalse: %lu (%.1f%%)",
(unsigned long)trueCount, percentTrue, (unsigned long)falseCount, percentFalse];
}
default:
return nil;
}
}
#pragma mark - NSTableView Delegate
-(CGFloat)tableView:(NSTableView *)tableView sizeToFitWidthOfColumn:(NSInteger)column
{
RLMTableColumn *tableColumn = (RLMTableColumn *)self.realmTableView.tableColumns[column];
return [tableColumn sizeThatFitsWithLimit:NO];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification
{
if (self.tableView == notification.object) {
NSInteger selectedIndex = self.tableView.selectedRow;
[self.parentWindowController.currentState updateSelectionToIndex:selectedIndex];
if (self.didSelectedBlock != nil) {
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:selectedIndex];
self.didSelectedBlock(selectedInstance);
}
}
}
-(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
if (tableView != self.tableView) {
return nil;
}
NSUInteger column = [tableView.tableColumns indexOfObject:tableColumn];
NSInteger propertyIndex = [self propertyIndexForColumn:column];
// Array gutter
if (propertyIndex == ARRAY_GUTTER_INDEX) {
RLMBasicTableCellView *gutterCellView = [tableView makeViewWithIdentifier:@"GutterCell" owner:self];
if (gutterCellView == nil) {
gutterCellView = [RLMBasicTableCellView viewWithIdentifier:@"GutterCell"];
}
gutterCellView.textField.stringValue = [@(rowIndex) stringValue];
gutterCellView.textField.editable = NO;
return gutterCellView;
}
RLMClassProperty *classProperty = self.displayedType.propertyColumns[propertyIndex];
RLMProperty *property = classProperty.property;
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:rowIndex];
id propertyValue = selectedInstance[classProperty.name];
if (propertyValue == NSNull.null) {
propertyValue = nil;
}
NSString *reuseIdentifier = [NSString stringWithFormat:@"Property.%@.Optional.%d",
[RLMDescriptions typeNameOfProperty:property],
property.optional];
if (property.array) {
RLMBadgeTableCellView *badgeCellView = [tableView makeViewWithIdentifier:reuseIdentifier owner:self];
if (!badgeCellView) {
badgeCellView = [RLMBadgeTableCellView viewWithIdentifier:reuseIdentifier];
badgeCellView.optional = YES;
}
NSString *string = [realmDescriptions printablePropertyValue:propertyValue ofType:property];
NSDictionary *attr = @{NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle)};
badgeCellView.textField.attributedStringValue = [[NSAttributedString alloc] initWithString:string attributes:attr];
badgeCellView.textField.editable = NO;
badgeCellView.badge.hidden = NO;
badgeCellView.badge.title = [NSString stringWithFormat:@"%lu", [(RLMArray *)propertyValue count]];
[badgeCellView.badge.cell setHighlightsBy:0];
badgeCellView.toolTip = [realmDescriptions tooltipForPropertyValue:propertyValue ofType:classProperty.property];
return badgeCellView;
}
NSTableCellView *cellView;
switch (classProperty.type) {
case RLMPropertyTypeBool: {
if (property.optional) {
RLMOptionalBoolTableCellView *boolCellView = [tableView makeViewWithIdentifier:reuseIdentifier owner:self];
if (!boolCellView) {
boolCellView = [RLMOptionalBoolTableCellView viewWithIdentifier:reuseIdentifier];
boolCellView.popupControl.target = self;
boolCellView.popupControl.action = @selector(optionalBoolPopupChanged:);
}
// 0 = nil, 1 = False, 2 = True
if (propertyValue == nil) {
[boolCellView.popupControl selectItemAtIndex:0];
}
else {
if ([propertyValue boolValue]) {
[boolCellView.popupControl selectItemAtIndex:2];
}
else {
[boolCellView.popupControl selectItemAtIndex:1];
}
}
cellView = boolCellView;
}
else {
RLMBoolTableCellView *boolCellView = [tableView makeViewWithIdentifier:reuseIdentifier owner:self];
if (!boolCellView) {
boolCellView = [RLMBoolTableCellView viewWithIdentifier:reuseIdentifier];
boolCellView.checkBox.target = self;
boolCellView.checkBox.action = @selector(editedCheckBox:);
}
boolCellView.checkBox.state = [(NSNumber *)propertyValue boolValue] ? NSOnState : NSOffState;
[boolCellView.checkBox setEnabled:!self.realmIsLocked];
cellView = boolCellView;
}
break;
}
// Intentional fallthrough
case RLMPropertyTypeInt:
case RLMPropertyTypeFloat:
case RLMPropertyTypeDouble: {
RLMNumberTableCellView *numberCellView = [tableView makeViewWithIdentifier:reuseIdentifier owner:self];
if (!numberCellView) {
numberCellView = [RLMNumberTableCellView viewWithIdentifier:reuseIdentifier];
numberCellView.textField.target = self;
numberCellView.textField.action = @selector(editedTextField:);
}
numberCellView.textField.objectValue = propertyValue;
numberCellView.textField.editable = !self.realmIsLocked && !classProperty.isPrimaryKey;
cellView = numberCellView;
break;
}
case RLMPropertyTypeObject: {
RLMLinkTableCellView *linkCellView = [tableView makeViewWithIdentifier:reuseIdentifier owner:self];
if (!linkCellView) {
linkCellView = [RLMLinkTableCellView viewWithIdentifier:reuseIdentifier];
linkCellView.textField.target = self;
linkCellView.textField.action = @selector(editedTextField:);
}
NSString *string = [realmDescriptions printablePropertyValue:propertyValue ofType:property];
NSDictionary *attr = @{NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle)};
linkCellView.textField.attributedStringValue = [[NSAttributedString alloc] initWithString:string attributes:attr];
linkCellView.textField.editable = NO;
cellView = linkCellView;
break;
}
// Intentional fallthrough
case RLMPropertyTypeLinkingObjects:
case RLMPropertyTypeData:
case RLMPropertyTypeAny:
case RLMPropertyTypeDate:
case RLMPropertyTypeString: {
RLMBasicTableCellView *basicCellView = [tableView makeViewWithIdentifier:reuseIdentifier owner:self];
if (!basicCellView) {
basicCellView = [RLMBasicTableCellView viewWithIdentifier:reuseIdentifier];
basicCellView.textField.target = self;
basicCellView.textField.action = @selector(editedTextField:);
}
basicCellView.textField.stringValue = [realmDescriptions printablePropertyValue:propertyValue ofType:property];
basicCellView.textField.editable = !self.realmIsLocked
&& property.type == RLMPropertyTypeString
&& !classProperty.isPrimaryKey;
cellView = basicCellView;
break;
}
}
if ([cellView respondsToSelector:@selector(setOptional:)]) {
[(id)cellView setOptional:property.optional];
}
cellView.toolTip = [realmDescriptions tooltipForPropertyValue:propertyValue ofType:classProperty.property];
return cellView;
}
#pragma mark - RLMTableView Delegate
// Asking the delegate about the state
- (BOOL)displaysArray
{
return ([self.displayedType isMemberOfClass:[RLMArrayNode class]]);
}
- (BOOL)isColumnObjectType:(NSInteger)column;
{
NSAssert(column != NOT_A_COLUMN, @"This method can only be used with an actual column index");
RLMProperty *prop = [self propertyForColumn:column];
return prop.type == RLMPropertyTypeObject && !prop.array;
}
// Asking the delegate about the contents
- (BOOL)containsObjectInRows:(NSIndexSet *)rowIndexes column:(NSInteger)column;
{
if (![self isColumnObjectType:column]) {
return NO;
}
NSInteger propertyIndex = [self propertyIndexForColumn:column];
return [self cellsAreNonEmptyInRows:rowIndexes propertyColumn:propertyIndex];
}
- (BOOL)containsArrayInRows:(NSIndexSet *)rowIndexes column:(NSInteger)column;
{
NSAssert(column != NOT_A_COLUMN, @"This method can only be used with an actual column index");
NSInteger propertyIndex = [self propertyIndexForColumn:column];
if (![self propertyForColumn:column].array) {
return NO;
}
return [self cellsAreNonEmptyInRows:rowIndexes propertyColumn:propertyIndex];
}
// RLMObject operations (when showing class table)
- (void)deleteObjects:(NSIndexSet *)rowIndexes
{
[self deleteObjectsInRealmAtIndexes:rowIndexes];
[self.parentWindowController reloadAllWindows];
}
- (void)copyValueFromRow:(NSInteger)row column:(NSInteger)column {
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMClassProperty *classProperty = self.displayedType.propertyColumns[propertyIndex];
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:row];
id propertyValue = selectedInstance[classProperty.name];
NSString *string = [realmDescriptions printablePropertyValue:propertyValue ofType:classProperty.property];
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
[pasteboard writeObjects:@[ string ]];
}
- (void)addNewObjects:(NSIndexSet *)rowIndexes
{
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
NSUInteger objectCount = MAX(rowIndexes.count, 1);
RLMObject *newObject;
[realm beginWriteTransaction];
for (NSUInteger i = 0; i < objectCount; i++) {
newObject = [self.class createObjectInRealm:realm withSchema:self.displayedType.schema];
}
[realm commitWriteTransaction];
[self.parentWindowController reloadAllWindows];
if (newObject && [self.displayedType isKindOfClass:RLMClassNode.class]) {
RLMClassNode *classNode = (RLMClassNode *)self.displayedType;
NSUInteger row = [classNode indexOfInstance:newObject];
[self.realmTableView scrollToRow:row];
}
}
// RLMArray operations
- (void)removeRows:(NSIndexSet *)rowIndexes
{
[self removeRowsInRealmAt:rowIndexes];
}
- (void)deleteRows:(NSIndexSet *)rowIndexes
{
[self deleteObjectsInRealmAtIndexes:rowIndexes];
}
- (void)addNewRows:(NSIndexSet *)rowIndexes
{
[self insertNewRowsInRealmAt:rowIndexes];
}
- (void)presentListPopoverIn:(CGRect)rect nodeType:(RLMTypeNode *)node transaction:(void (^)(RLMObject *))block
{
RLMObjectLinkSelectionViewController *popoverContent = [RLMObjectLinkSelectionViewController loadInstance];
NSPopover *popover = [[NSPopover alloc] init];
popover.contentViewController = popoverContent;
popover.behavior = NSPopoverBehaviorTransient;
popoverContent.displayedType = node;
__weak typeof(self) weakSelf = self;
__weak typeof(popover) weakPopover = popover;
popoverContent.didSelectedBlock = ^(RLMObject *object) {
RLMRealm *realm = weakSelf.parentWindowController.document.presentedRealm.realm;
[realm beginWriteTransaction];
block(object);
[realm commitWriteTransaction];
[weakPopover close];
};
[popover showRelativeToRect:rect ofView:self.tableView preferredEdge:NSMaxYEdge];
}
- (void)insertLinks:(NSIndexSet *)rowIndexes column:(NSInteger)columnIndex
{
NSArray *topLevelClasses = self.parentWindowController.document.presentedRealm.topLevelClasses;
NSString *containedClassName = [(RLMArrayNode *)self.displayedType objectClassName];
RLMTypeNode *node = nil;
for (RLMClassNode *classNode in topLevelClasses) {
if ([classNode.name isEqualToString:containedClassName]) {
node = classNode;
}
}
if (node == nil) return;
NSRect cellRect = [self.tableView frameOfCellAtColumn:columnIndex row:rowIndexes.firstIndex];
__weak typeof(self) weakSelf = self;
[self presentListPopoverIn:cellRect nodeType:node transaction: ^(RLMObject *object) {
[(RLMArrayNode *)weakSelf.displayedType insertInstance:object atIndex:rowIndexes.firstIndex];
}];
}
// Operations on links in cells
- (void)setObjectLinkAtRows:(NSIndexSet *)rowIndexes column:(NSInteger)columnIndex {
NSArray *topLevelClasses = self.parentWindowController.document.presentedRealm.topLevelClasses;
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:rowIndexes.firstIndex];
NSInteger propertyIndex = [self propertyIndexForColumn:columnIndex];
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
RLMObjectSchema *objectSchema = [realm.schema schemaForClassName:self.displayedType.name];
RLMProperty *property = objectSchema.properties[propertyIndex];
RLMTypeNode *node = nil;
for (RLMClassNode *classNode in topLevelClasses) {
if ([classNode.name isEqualToString:property.objectClassName]) {
node = classNode;
}
}
if (node == nil) return;
NSRect cellRect = [self.tableView frameOfCellAtColumn:columnIndex row:rowIndexes.firstIndex];
__weak typeof(self) weakSelf = self;
[self presentListPopoverIn:cellRect nodeType:node transaction:^(RLMObject *object) {
if ([weakSelf propertyForColumn: columnIndex].array) {
[(RLMArray*)selectedInstance[property.name] addObject:object];
} else {
selectedInstance[property.name] = object;
}
}];
}
- (void)removeObjectLinksAtRows:(NSIndexSet *)rowIndexes column:(NSInteger)columnIndex
{
[self removeContentsAtRows:rowIndexes column:columnIndex];
}
- (void)removeArrayLinksAtRows:(NSIndexSet *)rowIndexes column:(NSInteger)columnIndex
{
[self removeContentsAtRows:rowIndexes column:columnIndex];
}
// Opening an array in a new window
- (void)openArrayInNewWindowAtRow:(NSInteger)row column:(NSInteger)column
{
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMClassProperty *propertyNode = self.displayedType.propertyColumns[propertyIndex];
RLMArrayNavigationState *state = [[RLMArrayNavigationState alloc] initWithSelectedType:self.displayedType
typeIndex:row
property:propertyNode.property
arrayIndex:0];
[self.parentWindowController newWindowWithNavigationState:state];
}
#pragma mark - Private Methods - RLMTableView Delegate Helpers
+ (RLMObject *)createObjectInRealm:(RLMRealm *)realm withSchema:(RLMObjectSchema *)schema
{
NSMutableDictionary *objectBlueprint = [self defaultValuesForSchema:schema];
RLMProperty *primaryKey = schema.primaryKeyProperty;
if (primaryKey) {
id uniqueValue = [self uniqueValueForProperty:primaryKey className:schema.className inRealm:realm];
if (!uniqueValue) {
return nil;
}
objectBlueprint[primaryKey.name] = uniqueValue;
}
return [realm createObject:schema.className withValue:objectBlueprint];
}
+ (id)uniqueValueForProperty:(RLMProperty *)primaryKey className:(NSString *)className inRealm:(RLMRealm *)realm
{
NSUInteger remainingAttempts = 100;
NSUInteger maxBitsUsed = 8;
while (remainingAttempts > 0) {
id uniqueValue;
if (primaryKey.type == RLMPropertyTypeInt) {
u_int32_t maxInt = MIN(1 << maxBitsUsed++, UINT32_MAX);
uniqueValue = @(arc4random_uniform(maxInt));
} else if (primaryKey.type == RLMPropertyTypeString) {
uniqueValue = [[NSUUID UUID] UUIDString];
}
if ([[realm objects:className where:@"%K == %@", primaryKey.name, uniqueValue] count] == 0) {
return uniqueValue;
}
remainingAttempts--;
}
return nil;
}
+ (NSMutableDictionary *)defaultValuesForSchema:(RLMObjectSchema *)schema
{
NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
for (RLMProperty *property in schema.properties) {
defaultValues[property.name] = property.array ? @[] : [self defaultValueForPropertyType:property.type];
}
return defaultValues;
}
+ (id)defaultValueForPropertyType:(RLMPropertyType)propertyType
{
switch (propertyType) {
case RLMPropertyTypeInt:
return @0;
case RLMPropertyTypeFloat:
return @0.0f;
case RLMPropertyTypeDouble:
return @0.0;
case RLMPropertyTypeString:
return @"";
case RLMPropertyTypeBool:
return @NO;
case RLMPropertyTypeDate:
return [NSDate date];
case RLMPropertyTypeData:
return [@"<Data>" dataUsingEncoding:NSUTF8StringEncoding];
case RLMPropertyTypeAny:
return @"<Any>";
case RLMPropertyTypeObject:
return [NSNull null];
case RLMPropertyTypeLinkingObjects:
return [NSNull null];
}
}
- (RLMProperty *)propertyForColumn:(NSInteger)column
{
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
RLMObjectSchema *objectSchema = [realm.schema schemaForClassName:self.displayedType.name];
return objectSchema.properties[propertyIndex];
}
- (BOOL)cellsAreNonEmptyInRows:(NSIndexSet *)rowIndexes propertyColumn:(NSInteger)propertyColumn
{
RLMClassProperty *classProperty = self.displayedType.propertyColumns[propertyColumn];
__block BOOL returnValue = NO;
[rowIndexes enumerateIndexesUsingBlock:^(NSUInteger rowIndex, BOOL *stop) {
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:rowIndex];
id propertyValue = selectedInstance[classProperty.name];
if (propertyValue) {
returnValue = YES;
*stop = YES;
}
}];
return returnValue;
}
- (void)removeContentsAtRows:(NSIndexSet *)rowIndexes column:(NSInteger)column
{
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
RLMClassProperty *classProperty = self.displayedType.propertyColumns[propertyIndex];
id newValue = classProperty.property.array ? @[] : [NSNull null];
[realm beginWriteTransaction];
[rowIndexes enumerateIndexesUsingBlock:^(NSUInteger rowIndex, BOOL *stop) {
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:rowIndex];
selectedInstance[classProperty.name] = newValue;
}];
[realm commitWriteTransaction];
[self.parentWindowController reloadAllWindows];
}
#pragma mark - Rearranging objects in arrays - Private methods
- (void)removeRowsInRealmAt:(NSIndexSet *)rowIndexes
{
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
[realm beginWriteTransaction];
[rowIndexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger index, BOOL *stop) {
[(RLMArrayNode *)self.displayedType removeInstanceAtIndex:index];
}];
[realm commitWriteTransaction];
}
- (void)insertNewRowsInRealmAt:(NSIndexSet *)rowIndexes
{
if (rowIndexes.count == 0) {
rowIndexes = [NSIndexSet indexSetWithIndex:0];
}
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
[realm beginWriteTransaction];
RLMArrayNode *arrayNode = (RLMArrayNode *)self.displayedType;
if (arrayNode.isObject) {
[rowIndexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger i, BOOL *stop) {
RLMObject *object = [self.class createObjectInRealm:realm withSchema:self.displayedType.schema];
[arrayNode insertInstance:object atIndex:i];
}];
}
else {
[rowIndexes enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger i, BOOL *stop) {
[arrayNode insertInstance:[self.class defaultValueForPropertyType:arrayNode.referringProperty.type] atIndex:i];
}];
}
[realm commitWriteTransaction];
}
- (void)moveRowsInRealmFrom:(NSIndexSet *)sourceIndexes to:(NSUInteger)destination
{
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
NSMutableArray *sources = [self arrayWithIndexSet:sourceIndexes];
[realm beginWriteTransaction];
// Iterate through the array, representing source row indices
for (NSUInteger i = 0; i < sources.count; i++) {
NSUInteger source = [sources[i] unsignedIntegerValue];
[(RLMArrayNode *)self.displayedType moveInstanceFromIndex:source toIndex:destination];
[self updateSourceIndices:sources afterIndex:i withSource:source destination:&destination];
}
[realm commitWriteTransaction];
}
- (void)deleteObjectsInRealmAtIndexes:(NSIndexSet *)rowIndexes
{
if (!self.displayedType.isObject) {
[self removeRowsInRealmAt:rowIndexes];
return;
}
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
NSMutableArray *objectsToDelete = [NSMutableArray array];
[rowIndexes enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
[objectsToDelete addObject:[self.displayedType instanceAtIndex:index]];
}];
[realm beginWriteTransaction];
[realm deleteObjects:objectsToDelete];
[realm commitWriteTransaction];
}
-(NSMutableArray *)arrayWithIndexSet:(NSIndexSet *)indexSet
{
NSMutableArray *sources = [NSMutableArray array];
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
[sources addObject:@(idx)];
}];
return sources;
}
-(void)updateSourceIndices:(NSMutableArray *)sources
afterIndex:(NSUInteger)i
withSource:(NSUInteger)source
destination:(NSUInteger *)destination
{
for (NSUInteger j = i + 1; j < sources.count; j++) {
NSUInteger sourceIndexToModify = [sources[j] unsignedIntegerValue];
// Everything right of the destination is shifted right
if (sourceIndexToModify > *destination) {
sourceIndexToModify++;
}
// Everything right of the current source is shifted left
if (sourceIndexToModify > source) {
sourceIndexToModify--;
}
sources[j] = @(sourceIndexToModify);
}
// If the move was from higher index to lower, shift destination right
if (source > *destination) {
(*destination)++;
}
}
#pragma mark - Mouse Handling
- (void)mouseDidEnterCellAtLocation:(RLMTableLocation)location
{
NSInteger propertyIndex = [self propertyIndexForColumn:location.column];
if (propertyIndex >= self.displayedType.propertyColumns.count || location.row >= self.displayedType.instanceCount) {
[self disableLinkCursor];
return;
}
RLMClassProperty *propertyNode = self.displayedType.propertyColumns[propertyIndex];
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:location.row];
id propertyValue = selectedInstance[propertyNode.name];
if (!propertyValue) {
[self disableLinkCursor];
return;
}
if (propertyNode.type == RLMPropertyTypeObject || propertyNode.property.array) {
[self enableLinkCursor];
}
}
- (void)mouseDidExitCellAtLocation:(RLMTableLocation)location
{
[self disableLinkCursor];
}
- (void)mouseDidExitView:(RLMTableView *)view
{
[self disableLinkCursor];
}
#pragma mark - Public Methods - NSTableView Event Handling
- (IBAction)editedTextField:(NSTextField *)sender {
NSInteger row = [self.tableView rowForView:sender];
NSInteger column = [self.tableView columnForView:sender];
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
if (row < 0 || column < 0 || realm.inWriteTransaction) {
// Table view was reloaded during editing
return;
}
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMTypeNode *displayedType = self.displayedType;
RLMClassProperty *propertyNode = displayedType.propertyColumns[propertyIndex];
RLMObject *selectedInstance = [displayedType instanceAtIndex:row];
BOOL optionalValue = propertyNode.property.optional;
id result = nil;
switch (propertyNode.type) {
case RLMPropertyTypeInt:
numberFormatter.allowsFloats = NO;
result = [numberFormatter numberFromString:sender.stringValue];
break;
case RLMPropertyTypeFloat:
case RLMPropertyTypeDouble:
numberFormatter.allowsFloats = YES;
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
result = [numberFormatter numberFromString:sender.stringValue];
break;
case RLMPropertyTypeString:
result = sender.stringValue;
break;
case RLMPropertyTypeDate:
result = [dateFormatter dateFromString:sender.stringValue];
break;
case RLMPropertyTypeLinkingObjects:
case RLMPropertyTypeAny:
case RLMPropertyTypeBool:
case RLMPropertyTypeData:
case RLMPropertyTypeObject:
break;
}
if (result || optionalValue) {
NSError *error;
[realm beginWriteTransaction];
selectedInstance[propertyNode.name] = result;
if (![realm commitWriteTransaction:&error]) {
[NSApp presentError:error];
}
}
[self.parentWindowController reloadAllWindows];
}
- (IBAction)editedCheckBox:(NSButton *)sender
{
NSInteger row = [self.tableView rowForView:sender];
NSInteger column = [self.tableView columnForView:sender];
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMTypeNode *displayedType = self.displayedType;
RLMClassProperty *propertyNode = displayedType.propertyColumns[propertyIndex];
RLMObject *selectedInstance = [displayedType instanceAtIndex:row];
NSNumber *result = @((BOOL)(sender.state == NSOnState));
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
[realm beginWriteTransaction];
selectedInstance[propertyNode.name] = result;
[realm commitWriteTransaction];
[self.parentWindowController reloadAllWindows];
}
- (void)optionalBoolPopupChanged:(NSPopUpButton *)sender
{
NSInteger row = [self.tableView rowForView:sender];
NSInteger column = [self.tableView columnForView:sender];
NSInteger propertyIndex = [self propertyIndexForColumn:column];
RLMTypeNode *displayedType = self.displayedType;
RLMClassProperty *propertyNode = displayedType.propertyColumns[propertyIndex];
RLMObject *selectedInstance = [displayedType instanceAtIndex:row];
NSNumber *result = nil;
if (sender.indexOfSelectedItem > 0) {
result = @((BOOL)(sender.indexOfSelectedItem == 2));
}
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
[realm beginWriteTransaction];
selectedInstance[propertyNode.name] = result;
[realm commitWriteTransaction];
[self.parentWindowController reloadAllWindows];
}
- (void)rightClickedLocation:(RLMTableLocation)location
{
NSUInteger row = location.row;
if (row >= self.displayedType.instanceCount || RLMTableLocationRowIsUndefined(location)) {
[self clearSelection];
return;
}
if ([self.tableView.selectedRowIndexes containsIndex:row]) {
return;
}
[self setSelectionIndex:row];
}
- (void)userClicked:(NSTableView *)sender
{
if (self.tableView.selectedRowIndexes.count > 1) {
return;
}
NSInteger row = self.tableView.clickedRow;
NSInteger column = self.tableView.clickedColumn;
NSInteger propertyIndex = [self propertyIndexForColumn:column];
if (row == NOT_A_ROW || propertyIndex < 0) {
return;
}
RLMClassProperty *propertyNode = self.displayedType.propertyColumns[propertyIndex];
if (propertyNode.type == RLMPropertyTypeObject || propertyNode.property.array) {
RLMObject *selectedInstance = [self.displayedType instanceAtIndex:row];
id propertyValue = selectedInstance[propertyNode.name];
if ([propertyValue isKindOfClass:[RLMObject class]]) {
RLMObject *linkedObject = (RLMObject *)propertyValue;
RLMObjectSchema *linkedObjectSchema = linkedObject.objectSchema;
for (RLMClassNode *classNode in self.parentWindowController.document.presentedRealm.topLevelClasses) {
if ([classNode.name isEqualToString:linkedObjectSchema.className]) {
RLMResults *allInstances = [linkedObject.realm allObjects:linkedObjectSchema.className];
NSUInteger objectIndex = [allInstances indexOfObject:linkedObject];
RLMNavigationState *state = [[RLMNavigationState alloc] initWithSelectedType:classNode index:objectIndex];
[self.parentWindowController addNavigationState:state fromViewController:self];
break;
}
}
}
else if ([propertyValue isKindOfClass:[RLMArray class]]) {
RLMArrayNavigationState *state = [[RLMArrayNavigationState alloc] initWithSelectedType:self.displayedType
typeIndex:row
property:propertyNode.property
arrayIndex:0];
[self.parentWindowController addNavigationState:state fromViewController:self];
}
}
else {
[self setSelectionIndex:row];
}
}
- (void)userDoubleClicked:(NSTableView *)sender {
NSInteger row = self.tableView.clickedRow;
NSInteger column = self.tableView.clickedColumn;
NSInteger propertyIndex = [self propertyIndexForColumn:column];
if (row == NOT_A_ROW || propertyIndex < 0 || self.realmIsLocked) {
return;
}
RLMTypeNode *displayedType = self.displayedType;
RLMClassProperty *propertyNode = displayedType.propertyColumns[propertyIndex];
RLMObject *selectedObject = [displayedType instanceAtIndex:row];
id propertyValue = selectedObject[propertyNode.name];
switch (propertyNode.type) {
case RLMPropertyTypeDate: {
// Create a menu with a single menu item, and later populate it with the propertyValue
NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""];
NSSize intercellSpacing = [self.tableView intercellSpacing];
NSRect frame = [self.tableView frameOfCellAtColumn:column row:row];
frame.origin.x -= 0.5*intercellSpacing.width;
frame.origin.y -= 0.5*intercellSpacing.height;
frame.size.width += intercellSpacing.width;
frame.size.height += intercellSpacing.height;
frame.size.height = MAX(23.0, frame.size.height);
// Set up a date picker with no border or background
NSDatePicker *datepicker = [[NSDatePicker alloc] initWithFrame:frame];
datepicker.bordered = NO;
datepicker.drawsBackground = NO;
datepicker.datePickerStyle = NSTextFieldAndStepperDatePickerStyle;
datepicker.datePickerElements = NSHourMinuteSecondDatePickerElementFlag
| NSYearMonthDayDatePickerElementFlag | NSTimeZoneDatePickerElementFlag;
datepicker.dateValue = propertyValue;
item.view = datepicker;
[menu addItem:item];
if ([menu popUpMenuPositioningItem:nil atLocation:frame.origin inView:self.tableView]) {
RLMRealm *realm = self.parentWindowController.document.presentedRealm.realm;
[realm beginWriteTransaction];
selectedObject[propertyNode.name] = datepicker.dateValue;
[realm commitWriteTransaction];
[self.tableView reloadData];
}
break;
}
case RLMPropertyTypeInt:
case RLMPropertyTypeFloat:
case RLMPropertyTypeDouble:
case RLMPropertyTypeString: {
// Start editing the textfield
NSTableCellView *cellView = [self.tableView viewAtColumn:column row:row makeIfNecessary:NO];
[[cellView.textField window] makeFirstResponder:cellView.textField];
break;
}
case RLMPropertyTypeAny:
case RLMPropertyTypeBool:
case RLMPropertyTypeData:
case RLMPropertyTypeObject:
case RLMPropertyTypeLinkingObjects:
// Do nothing
break;
}
}
#pragma mark - Public Methods - Table View Construction
- (void)enableLinkCursor
{
if (linkCursorDisplaying) {
return;
}
NSCursor *currentCursor = [NSCursor currentCursor];
[currentCursor push];
NSCursor *newCursor = [NSCursor pointingHandCursor];
[newCursor set];
linkCursorDisplaying = YES;
}
- (void)disableLinkCursor
{
if (!linkCursorDisplaying) {
return;
}
[NSCursor pop];
linkCursorDisplaying = NO;
}
#pragma mark - Private Methods - Convenience
-(NSInteger)propertyIndexForColumn:(NSInteger)column
{
return self.displaysArray ? column - 1 : column;
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMObjectLinkSelectionViewController.h
================================================
//
// RLMObjectLinkSelectionViewController.h
// RealmBrowser
//
// Created by sbuglakov on 24/08/15.
// Copyright (c) 2015 Realm inc. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class RLMTypeNode;
@class RLMObject;
@interface RLMObjectLinkSelectionViewController : NSViewController
@property (nonatomic, copy) void(^didSelectedBlock)(RLMObject *rowObject);
@property (nonatomic, strong) RLMTypeNode *displayedType;
+ (instancetype)loadInstance;
@end
================================================
FILE: RealmBrowser/Controllers/RLMObjectLinkSelectionViewController.m
================================================
//
// RLMObjectLinkSelectionViewController.m
// RealmBrowser
//
// Created by sbuglakov on 24/08/15.
// Copyright (c) 2015 Realm inc. All rights reserved.
//
#import "RLMObjectLinkSelectionViewController.h"
#import "RLMInstanceTableViewController.h"
@interface RLMObjectLinkSelectionViewController ()
@property (nonatomic, strong) IBOutlet RLMInstanceTableViewController *tableController;
@end
@implementation RLMObjectLinkSelectionViewController
+ (instancetype)loadInstance {
return [[RLMObjectLinkSelectionViewController alloc] initWithNibName:@"RLMObjectLinkSelectionViewController" bundle:[NSBundle mainBundle]];
}
- (void)viewDidLoad {
[super viewDidLoad];
if (self.displayedType != nil) {
self.displayedType = self.displayedType;
}
if (self.didSelectedBlock != nil) {
self.didSelectedBlock = self.didSelectedBlock;
}
}
- (void)setDisplayedType:(RLMTypeNode *)newNode {
_displayedType = newNode;
if (self.isViewLoaded) {
RLMNavigationState *state = [[RLMNavigationState alloc] initWithSelectedType:_displayedType index:NSNotFound];
[self.tableController performUpdateUsingState:state oldState:nil];
}
}
- (void)setDidSelectedBlock:(void (^)(RLMObject *))didSelectedBlock {
_didSelectedBlock = didSelectedBlock;
if (self.isViewLoaded) {
self.tableController.didSelectedBlock = didSelectedBlock;
}
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMRealmBrowserWindowController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Cocoa;
#import "RLMDocument.h"
#import "RLMTypeOutlineViewController.h"
#import "RLMInstanceTableViewController.h"
extern const NSUInteger kMaxNumberOfArrayEntriesInToolTip;
@interface RLMRealmBrowserWindowController : NSWindowController
@property (nonatomic, readonly) RLMNavigationState *currentState;
@property (atomic, assign) RLMDocument *document;
@property (nonatomic, strong) IBOutlet RLMTypeOutlineViewController *outlineViewController;
@property (nonatomic, strong) IBOutlet RLMInstanceTableViewController *tableViewController;
- (void)addNavigationState:(RLMNavigationState *)state fromViewController:(RLMViewController *)controller;
- (void)newWindowWithNavigationState:(RLMNavigationState *)state;
- (void)reloadAllWindows;
@end
================================================
FILE: RealmBrowser/Controllers/RLMRealmBrowserWindowController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Realm;
@import Realm.Private;
@import Realm.Dynamic;
@import RealmConverter;
#import "RLMRealmBrowserWindowController.h"
#import "RLMNavigationStack.h"
#import "RLMModelExporter.h"
#import "RLMExportIndicatorWindowController.h"
#import "RLMEncryptionKeyWindowController.h"
#import "RLMLoginWindowController.h"
#import "RLMConnectionIndicatorWindowController.h"
#import "RLMBrowserConstants.h"
NSString * const kRealmLockedImage = @"RealmLocked";
NSString * const kRealmUnlockedImage = @"RealmUnlocked";
NSString * const kRealmLockedTooltip = @"Unlock to enable editing";
NSString * const kRealmUnlockedTooltip = @"Lock to prevent editing";
NSString * const kRealmKeyIsLockedForRealm = @"LockedRealm:%@";
NSString * const kRealmKeyWindowFrameForRealm = @"WindowFrameForRealm:%@";
NSString * const kRealmKeyOutlineWidthForRealm = @"OutlineWidthForRealm:%@";
static void const *kWaitForDocumentSchemaLoadObservationContext;
@interface RLMRealmBrowserWindowController()<NSWindowDelegate>
@property (atomic, weak) IBOutlet NSSplitView *splitView;
@property (nonatomic, strong) IBOutlet NSSegmentedControl *navigationButtons;
@property (atomic, weak) IBOutlet NSToolbarItem *lockRealmButton;
@property (nonatomic, strong) IBOutlet NSSearchField *searchField;
@property (nonatomic, strong) RLMExportIndicatorWindowController *exportWindowController;
@property (nonatomic, strong) RLMEncryptionKeyWindowController *encryptionController;
@property (nonatomic, strong) RLMLoginWindowController *loginWindowController;
@property (nonatomic, strong) RLMConnectionIndicatorWindowController *connectionIndicatorWindowController;
@property (nonatomic, strong) RLMNotificationToken *documentNotificationToken;
@end
@implementation RLMRealmBrowserWindowController {
RLMNavigationStack *navigationStack;
}
@dynamic document;
- (void)setDocument:(RLMDocument *)document {
if (document == self.document) {
return;
}
[self stopObservingDocument];
[super setDocument:document];
if (self.windowLoaded && self.window.isVisible) {
[self handleDocumentState];
}
}
#pragma mark - NSWindowController Overrides
- (void)windowDidLoad
{
navigationStack = [[RLMNavigationStack alloc] init];
NSString *realmPath = self.document.fileURL.path;
[self setWindowFrameAutosaveName:[NSString stringWithFormat:kRealmKeyWindowFrameForRealm, realmPath]];
[self.splitView setAutosaveName:[NSString stringWithFormat:kRealmKeyOutlineWidthForRealm, realmPath]];
// Modify responder chain to handle shortcuts for table view (workaround for https://github.com/realm/realm-browser-osx/issues/241)
self.outlineViewController.tableView.enclosingScrollView.nextResponder = self.tableViewController.tableView;
}
- (IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[self handleDocumentState];
}
#pragma mark - Document observation
- (void)handleDocumentState {
switch (self.document.state) {
case RLMDocumentStateRequiresFormatUpgrade:
[self handleFormatUpgrade];
break;
case RLMDocumentStateNeedsEncryptionKey:
[self handleEncryption];
break;
case RLMDocumentStateLoading:
[self waitForDocumentLoaded];
break;
case RLMDocumentStateNeedsValidCredentials:
[self handleSyncCredentials];
break;
case RLMDocumentStateLoaded:
[self realmDidLoad];
break;
case RLMDocumentStateUnrecoverableError:
[self handleUnrecoverableError];
break;
}
}
- (void)startObservingDocument {
__weak typeof(self) weakSelf = self;
[self.documentNotificationToken invalidate];
self.documentNotificationToken = [self.document.presentedRealm.realm addNotificationBlock:^(RLMNotification notification, RLMRealm *realm) {
// Send notifications to all document's window controllers
[weakSelf.document.windowControllers makeObjectsPerformSelector:@selector(handleDocumentChange)];
}];
}
- (void)handleDocumentChange {
[self reloadAfterEdit];
}
- (void)stopObservingDocument {
[self.documentNotificationToken invalidate];
}
- (void)realmDidLoad {
[self.outlineViewController realmDidLoad];
[self.tableViewController realmDidLoad];
[self updateNavigationButtons];
id firstItem = self.document.presentedRealm.topLevelClasses.firstObject;
if (firstItem != nil && navigationStack.currentState == nil) {
RLMNavigationState *initState = [[RLMNavigationState alloc] initWithSelectedType:firstItem index:NSNotFound];
[self addNavigationState:initState fromViewController:nil];
}
[self startObservingDocument];
}
- (void)handleFormatUpgrade {
NSAlert *alert = [[NSAlert alloc] init];
alert.messageText = [NSString stringWithFormat:@"\"%@\" is at an older file format version and must be upgraded before it can be opened. Would you like to proceed?", self.document.fileURL.lastPathComponent];
alert.informativeText = @"If the file is upgraded, it will no longer be compatible with older versions of Realm. File format upgrades are permanent and cannot be undone.";
[alert addButtonWithTitle:@"Cancel"];
[alert addButtonWithTitle:@"Proceed with Upgrade"];
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSAlertSecondButtonReturn) {
[self.document loadByPerformingFormatUpgradeWithError:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDocumentState];
});
} else {
[self.document close];
}
}];
}
- (void)handleEncryption {
self.encryptionController = [[RLMEncryptionKeyWindowController alloc] init];
[self.encryptionController showSheetForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseOK) {
[self.document loadWithEncryptionKey:self.encryptionController.encryptionKey error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDocumentState];
});
} else {
[self.document close];
}
self.encryptionController = nil;
}];
}
- (void)waitForDocumentLoaded {
[self showLoadingIndicator];
[self.document addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:&kWaitForDocumentSchemaLoadObservationContext];
}
- (void)cancelDocumentLoading {
[self.document removeObserver:self forKeyPath:@"state"];
}
- (void)documentLoaded {
[self.document removeObserver:self forKeyPath:@"state"];
[self hideLoadingIndicator];
[self handleDocumentState];
}
- (void)handleSyncCredentials {
self.loginWindowController = [[RLMLoginWindowController alloc] init];
self.loginWindowController.credentials = self.document.credentials;
[self.loginWindowController showSheetForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseOK) {
[self showLoadingIndicator];
[self.document loadWithCredentials:self.loginWindowController.credentials completionHandler:^(NSError *error) {
[self hideLoadingIndicator];
// TODO: handle error code properly
if (error != nil) {
[[NSAlert alertWithError:error] beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[self handleSyncCredentials];
}];
} else {
[self handleDocumentState];
}
}];
} else {
[self.document close];
}
self.loginWindowController = nil;
}];
}
- (void)showLoadingIndicator {
if (self.connectionIndicatorWindowController.isWindowVisible) {
return;
}
self.connectionIndicatorWindowController = [[RLMConnectionIndicatorWindowController alloc] init];
[self.connectionIndicatorWindowController showSheetForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseCancel) {
[self cancelDocumentLoading];
[self.document close];
}
self.connectionIndicatorWindowController = nil;
}];
}
- (void)hideLoadingIndicator {
[self.connectionIndicatorWindowController closeWithReturnCode:NSModalResponseOK];
}
- (void)handleUnrecoverableError {
NSAlert *alert;
if (self.document.error != nil) {
alert = [NSAlert alertWithError:self.document.error];
} else {
alert = [[NSAlert alloc] init];
alert.messageText = @"Realm couldn't be opened";
alert.alertStyle = NSCriticalAlertStyle;
}
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[self.document close];
}];
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if (context == &kWaitForDocumentSchemaLoadObservationContext) {
if (self.document.state != RLMDocumentStateLoading) {
[self documentLoaded];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark - Public methods - Accessors
- (RLMNavigationState *)currentState
{
return navigationStack.currentState;
}
#pragma mark - Public methods - Menu items
- (void)saveModelsForLanguage:(RLMModelExporterLanguage)language
{
NSArray *objectSchemas = self.document.presentedRealm.realm.schema.objectSchema;
[RLMModelExporter saveModelsForSchemas:objectSchemas inLanguage:language window:self.window];
}
- (IBAction)saveJavaModels:(id)sender
{
[self saveModelsForLanguage:RLMModelExporterLanguageJava];
}
- (IBAction)saveObjcModels:(id)sender
{
[self saveModelsForLanguage:RLMModelExporterLanguageObjectiveC];
}
- (IBAction)saveSwiftModels:(id)sender
{
[self saveModelsForLanguage:RLMModelExporterLanguageSwift];
}
- (IBAction)saveJavaScriptModels:(id)sender
{
[self saveModelsForLanguage:RLMModelExporterLanguageJavaScript];
}
- (IBAction)saveCSharpModels:(id)sender
{
[self saveModelsForLanguage:RLMModelExporterLanguageCSharp];
}
- (IBAction)exportToCompactedRealm:(id)sender
{
NSString *fileName = self.document.fileURL.lastPathComponent ?: self.document.syncURL.lastPathComponent ?: @"Compacted";
if (![fileName.pathExtension isEqualToString:kRealmFileExtension]) {
fileName = [fileName.stringByDeletingPathExtension stringByAppendingPathExtension:kRealmFileExtension];
}
NSSavePanel *panel = [NSSavePanel savePanel];
panel.canCreateDirectories = YES;
panel.nameFieldStringValue = fileName;
panel.prompt = @"Export";
[panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result){
if (result != NSFileHandlingPanelOKButton) {
return;
}
[panel orderOut:nil];
[self exportAndCompactCopyOfRealmFileAtURL:panel.URL];
}];
}
- (IBAction)exportToCSV:(id)sender
{
NSOpenPanel *panel = [NSOpenPanel openPanel];
panel.canCreateDirectories = YES;
panel.canChooseDirectories = YES;
panel.canChooseFiles = NO;
panel.message = @"Choose the directory in which to save the CSV files generated from this Realm file.";
panel.prompt = @"Export";
[panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
if (result != NSFileHandlingPanelOKButton) {
return;
}
[panel orderOut:nil];
RLMCSVDataExporter *exporter = [[RLMCSVDataExporter alloc] initWithRealm:self.document.presentedRealm.realm];
NSError *error = nil;
if (![exporter exportToFolderAtPath:panel.URL.path withError:&error]) {
[NSApp presentError:error];
} else {
[[NSWorkspace sharedWorkspace] openURL:panel.URL];
}
}];
}
- (void)exportAndCompactCopyOfRealmFileAtURL:(NSURL *)realmFileURL
{
//Check that this won't end up overwriting the original file
if ([realmFileURL.path.lowercaseString isEqualToString:self.document.fileURL.path.lowercaseString]) {
NSAlert *alert = [[NSAlert alloc] init];
alert.messageText = @"You cannot overwrite the original Realm file.";
alert.informativeText = @"Please choose a different location in which to save this Realm file.";
[alert runModal];
return;
}
//Ensure a file with the same name doesn't already exist
BOOL directory = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:realmFileURL.path isDirectory:&directory] && !directory) {
NSError *error = nil;
if (![[NSFileManager defaultManager] removeItemAtURL:realmFileURL error:&error]) {
[NSApp presentError:error];
return;
}
}
void (^closeExportWindowOnMainThreadAndShowError)(NSError *) = ^void(NSError *error) {
dispatch_sync(dispatch_get_main_queue(), ^{
[self.window endSheet:self.exportWindowController.window];
if (error != nil) {
[NSApp presentError:error];
} else {
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[realmFileURL]];
}
});
};
//Display an 'exporting' progress indicator
self.exportWindowController = [[RLMExportIndicatorWindowController alloc] init];
[self.window beginSheet:self.exportWindowController.window completionHandler:nil];
//Perform the export/compact operations on a background thread as they can potentially be time-consuming
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSError *error = nil;
RLMRealm *currentThreadRealm = [RLMRealm realmWithConfiguration:self.document.presentedRealm.realm.configuration error:&error];
if (currentThreadRealm == nil) {
closeExportWindowOnMainThreadAndShowError(error);
return;
}
if (![currentThreadRealm writeCopyToURL:realmFileURL encryptionKey:nil error:&error]) {
closeExportWindowOnMainThreadAndShowError(error);
return;
}
closeExportWindowOnMainThreadAndShowError(nil);
});
}
#pragma mark - Public methods - User Actions
- (void)reloadAllWindows
{
NSArray *windowControllers = [self.document windowControllers];
for (RLMRealmBrowserWindowController *wc in windowControllers) {
[wc reloadAfterEdit];
}
}
- (void)reloadAfterEdit
{
[self.outlineViewController reloadData];
NSString *realmPath = self.document.fileURL.path;
NSString *key = [NSString stringWithFormat:kRealmKeyIsLockedForRealm, realmPath];
BOOL realmIsLocked = [[NSUserDefaults standardUserDefaults] boolForKey:key];
self.tableViewController.realmIsLocked = realmIsLocked;
self.lockRealmButton.image = [NSImage imageNamed:realmIsLocked ? kRealmLockedImage : kRealmUnlockedImage];
self.lockRealmButton.toolTip = realmIsLocked ? kRealmLockedTooltip : kRealmUnlockedTooltip;
if (self.tableViewController.displayedType.isInvalidated) {
[navigationStack reset];
[self realmDidLoad];
} else {
[self.tableViewController reloadData];
}
}
#pragma mark - Public methods - Navigation
- (void)addNavigationState:(RLMNavigationState *)state fromViewController:(RLMViewController *)controller
{
if (!controller.navigationFromHistory) {
RLMNavigationState *oldState = navigationStack.currentState;
[navigationStack pushState:state];
[self updateNavigationButtons];
if (controller == self.tableViewController || controller == nil) {
[self.outlineViewController updateUsingState:state oldState:oldState];
}
[self.tableViewController updateUsingState:state oldState:oldState];
}
// Searching is not implemented for link arrays yet
BOOL isArray = [state isMemberOfClass:[RLMArrayNavigationState class]];
[self.searchField setEnabled:!isArray];
}
- (void)newWindowWithNavigationState:(RLMNavigationState *)state
{
RLMRealmBrowserWindowController *wc = [[RLMRealmBrowserWindowController alloc] initWithWindowNibName:self.windowNibName];
[self.document addWindowController:wc];
[self.document showWindows];
[wc addNavigationState:state fromViewController:wc.tableViewController];
}
- (IBAction)userClicksOnNavigationButtons:(NSSegmentedControl *)buttons
{
RLMNavigationState *oldState = navigationStack.currentState;
switch (buttons.selectedSegment) {
case 0: { // Navigate backwards
RLMNavigationState *state = [navigationStack navigateBackward];
if (state != nil) {
[self.outlineViewController updateUsingState:state oldState:oldState];
[self.tableViewController updateUsingState:state oldState:oldState];
}
break;
}
case 1: { // Navigate forwards
RLMNavigationState *state = [navigationStack navigateForward];
if (state != nil) {
[self.outlineViewController updateUsingState:state oldState:oldState];
[self.tableViewController updateUsingState:state oldState:oldState];
}
break;
}
default:
break;
}
[self updateNavigationButtons];
}
- (IBAction)userClickedLockRealm:(id)sender
{
NSString *realmPath = self.document.fileURL.path;
NSString *key = [NSString stringWithFormat:kRealmKeyIsLockedForRealm, realmPath];
BOOL currentlyLocked = [[NSUserDefaults standardUserDefaults] boolForKey:key];
[self setRealmLocked:!currentlyLocked];
}
-(void)setRealmLocked:(BOOL)locked
{
NSString *realmPath = self.document.fileURL.path;
NSString *key = [NSString stringWithFormat:kRealmKeyIsLockedForRealm, realmPath];
[[NSUserDefaults standardUserDefaults] setBool:locked forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
[self reloadAllWindows];
}
- (IBAction)searchAction:(NSSearchFieldCell *)searchCell
{
NSString *searchText = searchCell.stringValue;
RLMTypeNode *typeNode = navigationStack.currentState.selectedType;
// Return to parent class (showing all objects) when the user clears the search text
if (searchText.length == 0) {
if ([navigationStack.currentState isMemberOfClass:[RLMQueryNavigationState class]]) {
RLMNavigationState *state = [[RLMNavigationState alloc] initWithSelectedType:typeNode index:0];
[self addNavigationState:state fromViewController:self.tableViewController];
}
return;
}
NSArray *columns = typeNode.propertyColumns;
NSUInteger columnCount = columns.count;
RLMRealm *realm = self.document.presentedRealm.realm;
NSMutableArray *predicates = [NSMutableArray array];
NSNumberFormatter *floatFormatter = [[NSNumberFormatter alloc] init];
NSNumberFormatter *integerFormatter = [[NSNumberFormatter alloc] init];
integerFormatter.allowsFloats = NO;
for (NSUInteger index = 0; index < columnCount; index++) {
RLMClassProperty *property = columns[index];
NSExpression *propertyExpression = [NSExpression expressionForKeyPath:property.name];
NSExpression *valueExpression;
NSPredicateOperatorType comparisonOperator = NSEqualToPredicateOperatorType;
NSComparisonPredicateOptions comparisonOptions = 0;
switch (property.type) {
case RLMPropertyTypeBool: {
if ([searchText caseInsensitiveCompare:@"true"] == NSOrderedSame ||
[searchText caseInsensitiveCompare:@"YES"] == NSOrderedSame) {
valueExpression = [NSExpression expressionForConstantValue:@YES];
}
else if ([searchText caseInsensitiveCompare:@"false"] == NSOrderedSame ||
[searchText caseInsensitiveCompare:@"NO"] == NSOrderedSame) {
valueExpression = [NSExpression expressionForConstantValue:@NO];
}
break;
}
case RLMPropertyTypeInt: {
NSNumber *value = [integerFormatter numberFromString:searchText];
if (value) {
valueExpression = [NSExpression expressionForConstantValue:value];
}
break;
}
case RLMPropertyTypeString: {
valueExpression = [NSExpression expressionForConstantValue:searchText];
comparisonOperator = NSContainsPredicateOperatorType;
comparisonOptions = NSCaseInsensitivePredicateOption;
break;
}
case RLMPropertyTypeFloat:
case RLMPropertyTypeDouble: {
NSNumber *value = [floatFormatter numberFromString:searchText];
if (value) {
valueExpression = [NSExpression expressionForConstantValue:value];
}
break;
}
default:
break;
}
if (!valueExpression) {
// We were unable to convert the search text into a predicate for this property type.
continue;
}
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:propertyExpression
rightExpression:valueExpression
modifier:NSDirectPredicateModifier
type:comparisonOperator
options:comparisonOptions];
[predicates addObject:predicate];
}
NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
RLMResults *result = [realm objects:typeNode.name withPredicate:predicate];
RLMQueryNavigationState *state = [[RLMQueryNavigationState alloc] initWithQuery:searchText type:typeNode results:result];
[self addNavigationState:state fromViewController:self.tableViewController];
}
#pragma mark - Private methods
- (void)updateNavigationButtons
{
[self.navigationButtons setEnabled:[navigationStack canNavigateBackward] forSegment:0];
[self.navigationButtons setEnabled:[navigationStack canNavigateForward] forSegment:1];
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMTypeOutlineViewController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Cocoa;
#import "RLMViewController.h"
#import "RLMClassNode.h"
@interface RLMTypeOutlineViewController : RLMViewController <NSOutlineViewDataSource, NSOutlineViewDelegate>
- (void)reloadData;
@end
================================================
FILE: RealmBrowser/Controllers/RLMTypeOutlineViewController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMTypeOutlineViewController.h"
#import "RLMRealmBrowserWindowController.h"
#import "RLMRealmOutlineNode.h"
#import "RLMArrayNavigationState.h"
#import "RLMQueryNavigationState.h"
#import "RLMObjectNode.h"
#import "RLMResultsNode.h"
#import "RLMRealmOutlineNode.h"
@interface RLMTypeOutlineViewController ()
@property (nonatomic, strong) IBOutlet NSOutlineView *classesOutlineView;
@property (nonatomic) BOOL skipSelectionChangeHandling;
@end
@implementation RLMTypeOutlineViewController
#pragma mark - RLMViewController overrides
-(void)realmDidLoad
{
[self.classesOutlineView reloadData];
// Expand the root item representing the selected realm.
RLMRealmNode *firstItem = self.parentWindowController.document.presentedRealm;
if (firstItem != nil) {
// We want the class outline to be expanded as default
[self.classesOutlineView expandItem:firstItem expandChildren:YES];
}
}
- (void)reloadData {
NSInteger row = self.tableView.selectedRow;
[self.tableView reloadData];
self.skipSelectionChangeHandling = YES;
// selectRowIndexes:byExtendingSelection: notifies delegate but this notification should be skipped.
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
self.skipSelectionChangeHandling = NO;
}
#pragma mark - NSOutlineViewDataSource implementation
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
if (item == nil) {
return self.parentWindowController.document.presentedRealm;
}
// ... and second level nodes are all classes.
else if ([item conformsToProtocol:@protocol(RLMRealmOutlineNode)]) {
id<RLMRealmOutlineNode> outlineItem = item;
return [outlineItem childNodeAtIndex:index];
}
return nil;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
// The root node is expandable if we are presenting a realm
if (item == nil) {
return self.parentWindowController.document.presentedRealm == nil;
}
// ... otherwise the exandability check is re-delegated to the node in question.
else if ([item conformsToProtocol:@protocol(RLMRealmOutlineNode)]) {
id<RLMRealmOutlineNode> outlineItem = item;
return outlineItem.isExpandable;
}
return NO;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
// There is never more than one root node
if (item == nil) {
return 1;
}
// ... otherwise the number of child nodes are defined by the node in question.
else if ([item conformsToProtocol:@protocol(RLMRealmOutlineNode)]) {
id<RLMRealmOutlineNode> outlineItem = item;
return outlineItem.numberOfChildNodes;
}
return 0;
}
#pragma mark - NSOutlineViewDelegate implementation
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
// Group headers should not be selectable
return ![item isKindOfClass:[RLMRealmNode class]];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item
{
return NO;
}
- (NSString *)outlineView:(NSOutlineView *)outlineView
toolTipForCell:(NSCell *)cell
rect:(NSRectPointer)rect
tableColumn:(NSTableColumn *)tc
item:(id)item
mouseLocation:(NSPoint)mouseLocation
{
if ([item respondsToSelector:@selector(hasToolTip)]) {
if ([item respondsToSelector:@selector(toolTipString)]) {
return [item toolTipString];
}
}
return nil;
}
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
if (self.skipSelectionChangeHandling) {
return;
}
NSOutlineView *outlineView = notification.object;
if (outlineView == self.classesOutlineView) {
NSInteger row = [outlineView selectedRow];
// The arrays we get from link views are ephemeral, so we
// remove them when any class node is selected
if (row != -1) {
[self selectedItem:[outlineView itemAtRow:row]];
}
}
}
-(void)selectedItem:(id<RLMRealmOutlineNode>)item
{
if ([item isKindOfClass:[RLMResultsNode class]]) {
return;
}
id<RLMRealmOutlineNode> theItem = item;
// If we didn't select an array, we should flatten the outline view
if (![item isKindOfClass:[RLMArrayNode class]]) {
[self removeAllChildArrays];
}
// If we clicked an object, collapse and then select its parent
if ([item isKindOfClass:[RLMObjectNode class]]) {
theItem = ((RLMObjectNode *)item).parentNode;
}
RLMNavigationState *state = [[RLMNavigationState alloc] initWithSelectedType:theItem index:NSNotFound];
[self.parentWindowController addNavigationState:state fromViewController:self];
NSInteger typeIndex = [self.classesOutlineView rowForItem:theItem];
[self setSelectionIndex:typeIndex];
}
- (void)performUpdateUsingState:(RLMNavigationState *)newState oldState:(RLMNavigationState *)oldState
{
if ([newState isMemberOfClass:[RLMArrayNavigationState class]]) {
// Deselect everything in sidebar in case of swithing to array
[self setSelectionIndex:-1];
} else {
[self setSelectionIndex:[self.classesOutlineView rowForItem:newState.selectedType]];
}
}
- (void)outlineView:(NSOutlineView *)outlineView didAddRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
{
// No Action
}
- (void)outlineView:(NSOutlineView *)outlineView didRemoveRowView:(NSTableRowView *)rowView forRow:(NSInteger)row
{
// No Action
}
- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
{
return nil;
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
if (outlineView == self.classesOutlineView) {
if ([item conformsToProtocol:@protocol(RLMRealmOutlineNode)]) {
id<RLMRealmOutlineNode> outlineNode = item;
return [outlineNode cellViewForTableView:self.tableView];
}
}
return nil;
}
#pragma mark - Public methods - Accessors
- (NSOutlineView *)outlineView
{
if ([self.view isKindOfClass:[NSOutlineView class]]) {
return (NSOutlineView *)self.view;
}
return nil;
}
#pragma mark - Private methods
- (void)removeAllChildArrays
{
for (RLMClassNode *node in self.parentWindowController.document.presentedRealm.topLevelClasses) {
[node removeAllChildNodes];
[self.classesOutlineView reloadItem:node];
}
}
@end
================================================
FILE: RealmBrowser/Controllers/RLMViewController.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Cocoa;
#import "RLMNavigationState.h"
@class RLMRealmBrowserWindowController;
@interface RLMViewController : NSViewController
@property (nonatomic, readonly) NSTableView *tableView;
@property (nonatomic, readonly) BOOL navigationFromHistory;
@property (nonatomic, strong) RLMTypeNode *displayedType;
@property (nonatomic, weak) IBOutlet RLMRealmBrowserWindowController *parentWindowController;
- (void)updateUsingState:(RLMNavigationState *)newState oldState:(RLMNavigationState *)oldState;
- (void)performUpdateUsingState:(RLMNavigationState *)newState oldState:(RLMNavigationState *)oldState;
- (void)clearSelection;
- (void)setSelectionIndex:(NSInteger)newIndex;
-(void)realmDidLoad;
@end
================================================
FILE: RealmBrowser/Controllers/RLMViewController.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMViewController.h"
#import "RLMRealmBrowserWindowController.h"
@implementation RLMViewController
#pragma mark - NSObject overrides
- (void)awakeFromNib
{
[super awakeFromNib];
_navigationFromHistory = NO;
}
#pragma mark - Public methods - Accessors
-(void)realmDidLoad
{
// No action - should be implemented by subclasses.
}
- (NSTableView *)tableView
{
if ([self.view isKindOfClass:[NSTableView class]]) {
return (NSTableView *)self.view;
}
return nil;
}
#pragma mark - Public methods
- (void)updateUsingState:(RLMNavigationState *)newState oldState:(RLMNavigationState *)oldState
{
_navigationFromHistory = YES;
[self performUpdateUsingState:newState oldState:oldState];
_navigationFromHistory = NO;
}
- (void)performUpdateUsingState:(RLMNavigationState *)newState oldState:(RLMNavigationState *)oldState
{
// No action - should be implemented by subclasses.
}
- (void)clearSelection
{
[self.tableView deselectAll:self];
}
- (void)setSelectionIndex:(NSInteger)newIndex
{
NSInteger oldIndex = self.tableView.selectedRow;
if (oldIndex != newIndex) {
if (newIndex == -1) {
[self.tableView deselectAll:self];
} else {
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:newIndex] byExtendingSelection:NO];
}
[self.tableView scrollRowToVisible:newIndex];
}
}
@end
================================================
FILE: RealmBrowser/Models/RLMArrayNavigationState.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMNavigationState.h"
#import "RLMArrayNode.h"
@interface RLMArrayNavigationState : RLMNavigationState
@property (nonatomic, readonly) RLMProperty *property;
@property (nonatomic, readonly) NSInteger arrayIndex;
- (instancetype)initWithSelectedType:(RLMTypeNode *)type typeIndex:(NSInteger)typeIndex property:(RLMProperty *)property arrayIndex:(NSInteger)arrayIndex;
@end
================================================
FILE: RealmBrowser/Models/RLMArrayNavigationState.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMArrayNavigationState.h"
@implementation RLMArrayNavigationState
- (instancetype)initWithSelectedType:(RLMTypeNode *)type typeIndex:(NSInteger)typeIndex property:(RLMProperty *)property arrayIndex:(NSInteger)arrayIndex;
{
if (self = [super initWithSelectedType:type index:typeIndex]) {
_property = property;
_arrayIndex = arrayIndex;
}
return self;
}
- (void)updateSelectionToIndex:(NSInteger)index
{
_arrayIndex = index;
}
- (BOOL)isEqualTo:(id)object
{
if ([object isKindOfClass:[self class]]) {
BOOL result = [super isEqualTo:object];
RLMArrayNavigationState *comparedState = (RLMArrayNavigationState *)object;
return result && self.property == comparedState.property;
}
return NO;
}
@end
================================================
FILE: RealmBrowser/Models/RLMArrayNode.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMTypeNode.h"
@interface RLMArrayNode : RLMTypeNode
@property (readonly) RLMProperty *referringProperty;
- (instancetype)initWithReferringProperty:(RLMProperty *)property onObject:(RLMObject *)object realm:(RLMRealm *)realm;
- (BOOL)insertInstance:(RLMObject *)object atIndex:(NSUInteger)index;
- (BOOL)removeInstanceAtIndex:(NSUInteger)index;
- (BOOL)moveInstanceFromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex;
//- (BOOL)isEqualTo:(id)object;
- (NSString *)objectClassName;
@end
================================================
FILE: RealmBrowser/Models/RLMArrayNode.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMArrayNode.h"
#import "RLMSidebarTableCellView.h"
@interface RLMProperty (Dynamic)
- (instancetype)initWithName:(NSString *)name
type:(RLMPropertyType)type
objectClassName:(nullable NSString *)objectClassName
linkOriginPropertyName:(nullable NSString *)linkOriginPropertyName
indexed:(BOOL)indexed
optional:(BOOL)optional;
@end
@interface RLMObjectSchema (Dynamic)
- (instancetype)initWithClassName:(NSString *)objectClassName objectClass:(Class)objectClass properties:(NSArray *)properties;
@end
// A value which pretends to be an RLMObject representing a row in a non-object array
@interface RLMRowProxy : NSObject
@end
@implementation RLMRowProxy {
RLMArray *_array;
NSUInteger _index;
}
+ (instancetype)proxyForArray:(RLMArray *)array row:(NSUInteger)index {
RLMRowProxy *proxy = [[self alloc] init];
proxy->_array = array;
proxy->_index = index;
return proxy;
}
- (id)objectForKeyedSubscript:(__unused id)subscript {
return _array[_index];
}
- (void)setObject:(id)value forKeyedSubscript:(__unused id)subscript {
_array[_index] = value;
}
@end
@implementation RLMArrayNode {
RLMProperty *_referringProperty;
RLMObject *_referringObject;
RLMArray *_displayedArray;
bool _isObject;
}
#pragma mark - Public Methods
- (instancetype)initWithReferringProperty:(RLMProperty *)property onObject:(RLMObject *)object realm:(RLMRealm *)realm
{
RLMArray *array = object[property.name];
RLMObjectSchema *elementSchema;
if (array.objectClassName) {
elementSchema = [realm.schema schemaForClassName:array.objectClassName];
}
else {
// Create a fake object schema representing the values of a primitive array
RLMProperty *prop = [[RLMProperty alloc] initWithName:@"Value"
type:property.type
objectClassName:nil
linkOriginPropertyName:nil
indexed:NO
optional:property.optional];
elementSchema = [[RLMObjectSchema alloc] initWithClassName:property.name objectClass:RLMObject.class properties:@[prop]];
}
if (self = [super initWithSchema:elementSchema inRealm:realm]) {
_referringProperty = property;
_referringObject = object;
_displayedArray = array;
_isObject = array.objectClassName != nil;
}
return self;
}
-(BOOL)insertInstance:(RLMObject *)object atIndex:(NSUInteger)index
{
if (index > _displayedArray.count || object == nil) {
return NO;
}
[_displayedArray insertObject:object atIndex:index];
return YES;
}
-(BOOL)removeInstanceAtIndex:(NSUInteger)index
{
if (index >= [_displayedArray count]) {
return NO;
}
[_displayedArray removeObjectAtIndex:index];
return YES;
}
-(BOOL)moveInstanceFromIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex
{
if (fromIndex >= [_displayedArray count] || toIndex > [_displayedArray count]) {
return NO;
}
[_displayedArray moveObjectAtIndex:fromIndex toIndex:toIndex];
return YES;
}
-(BOOL)isEqualTo:(id)object
{
if ([object class] != [self class]) {
return NO;
}
if (self == object) {
return YES;
}
RLMArrayNode *otherArrayNode = object;
if (self.instanceCount != otherArrayNode.instanceCount) {
return NO;
}
for (int i = 0; i < self.instanceCount; i++) {
if (![_displayedArray[i] isEqualToObject:[otherArrayNode instanceAtIndex:i]]) {
return NO;
}
}
return YES;
}
- (NSString *)objectClassName
{
return _displayedArray.objectClassName;
}
#pragma mark - RLMTypeNode Overrides
- (NSString *)name
{
return @"Array";
}
- (NSUInteger)instanceCount
{
return _displayedArray.count;
}
- (BOOL)isInvalidated
{
return _displayedArray.isInvalidated;
}
- (BOOL)isObject
{
return _isObject;
}
- (RLMObject *)instanceAtIndex:(NSUInteger)index
{
return _isObject ? _displayedArray[index] : [RLMRowProxy proxyForArray:_displayedArray row:index];
}
- (id)nodeElementForColumnWithIndex:(NSInteger)index
{
switch (index) {
case 0:
return [NSString stringWithFormat:@"%@<%@>", _referringProperty.name, _referringProperty.objectClassName];
default:
return nil;
}
}
- (NSView *)cellViewForTableView:(NSTableView *)tableView
{
RLMSidebarTableCellView *cellView = [tableView makeViewWithIdentifier:@"MainCell" owner:self];
cellView.textField.stringValue = [NSString stringWithFormat:@"%@: <%@>",
_referringProperty.name, _referringProperty.objectClassName];
cellView.button.title = [NSString stringWithFormat:@"%lu", [self instanceCount]];
[[cellView.button cell] setHighlightsBy:0];
cellView.button.hidden = NO;
return cellView;
}
#pragma mark - RLMRealmOutlineNode Implementation
- (BOOL)hasToolTip
{
return YES;
}
- (NSString *)toolTipString
{
return _referringObject.description;
}
@end
================================================
FILE: RealmBrowser/Models/RLMClassNode.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMTypeNode.h"
@class RLMObjectNode;
@class RLMResultsNode;
@class RLMResults;
@interface RLMClassNode : RLMTypeNode
@property (nonatomic) RLMResults *allObjects;
- (RLMObjectNode *)displayChildObject:(RLMObject *)object;
- (RLMResultsNode *)displayChildResultsFromQuery:(NSString *)searchText result:(RLMResults *)result;
- (void)removeAllChildNodes;
- (void)removeDisplayingOfArrayAtIndex:(NSUInteger)index;
- (void)removeDisplayingOfArrayFromObjectAtIndex:(NSUInteger)index;
@end
================================================
FILE: RealmBrowser/Models/RLMClassNode.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMClassNode.h"
@import Realm.Dynamic;
#import "RLMObjectNode.h"
#import "RLMArrayNode.h"
#import "RLMResultsNode.h"
#import "RLMSidebarTableCellView.h"
@interface RLMClassNode ()
@property (nonatomic, readonly) NSMutableArray *displayedItems;
@end
@implementation RLMClassNode {
NSMutableArray *displayedObjects;
NSMutableArray *displayedArrays;
BOOL displaysQuery;
}
- (instancetype)initWithSchema:(RLMObjectSchema *)schema inRealm:(RLMRealm *)realm
{
self = [super initWithSchema:schema inRealm:realm];
if (self) {
displayedObjects = [[NSMutableArray alloc] initWithCapacity:10];
displayedArrays = [[NSMutableArray alloc] initWithCapacity:10];
displaysQuery = NO;
}
return self;
}
#pragma mark - RLMObjectNode overrides
- (NSString *)name
{
return [self.schema.className copy];
}
- (NSUInteger)instanceCount
{
return self.allObjects.count;
}
#pragma mark - RLMRealmOutlineNode implementation
- (BOOL)isExpandable
{
return self.displayedItems.count > 0;
}
- (NSUInteger)numberOfChildNodes
{
return self.displayedItems.count;
}
- (id<RLMRealmOutlineNode>)childNodeAtIndex:(NSUInteger)index
{
return self.displayedItems[index];
}
#pragma mark - RLMTypeNode overrides
- (RLMObject *)instanceAtIndex:(NSUInteger)index
{
return self.allObjects[index];
}
- (NSUInteger)indexOfInstance:(RLMObject *)instance
{
return [self.allObjects indexOfObject:instance];
}
- (NSView *)cellViewForTableView:(NSTableView *)tableView
{
RLMSidebarTableCellView *result = [tableView makeViewWithIdentifier:@"MainCell" owner:self];
result.textField.stringValue = self.name;
result.button.title = [NSString stringWithFormat:@"%lu", (unsigned long)[self instanceCount]];
[[result.button cell] setHighlightsBy:0];
result.button.hidden = NO;
result.imageView.image = nil;
return result;
}
- (RLMResults *)allObjects
{
if (!_allObjects) {
_allObjects = [self.realm allObjects:self.schema.className];
}
return _allObjects;
}
#pragma mark - Public methods
- (RLMObjectNode *)displayChildObject:(RLMObject *)object
{
displaysQuery = NO;
RLMObjectNode *objectNode = [[RLMObjectNode alloc] initWithObject:object realm:self.realm];
objectNode.parentNode = self;
if (displayedObjects.count == 0) {
[displayedObjects addObject:objectNode];
}
else {
[displayedObjects replaceObjectAtIndex:0 withObject:objectNode];
}
return objectNode;
}
- (RLMResultsNode *)displayChildResultsFromQuery:(NSString *)searchText result:(RLMResults *)result
{
displaysQuery = YES;
RLMResultsNode *resultsNode = [[RLMResultsNode alloc] initWithQuery:searchText result:result andParent:self];
if (displayedArrays.count == 0) {
[displayedArrays addObject:resultsNode];
}
else {
[displayedArrays replaceObjectAtIndex:0 withObject:resultsNode];
}
return resultsNode;
}
- (void)removeAllChildNodes
{
[displayedArrays removeAllObjects];
[displayedObjects removeAllObjects];
}
- (void)removeDisplayingOfArrayAtIndex:(NSUInteger)index
{
}
- (void)removeDisplayingOfArrayFromObjectAtIndex:(NSUInteger)index
{
}
#pragma mark - Private methods
- (NSMutableArray *)displayedItems
{
return displaysQuery ? displayedArrays : displayedObjects;
}
@end
================================================
FILE: RealmBrowser/Models/RLMClassProperty.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Foundation;
@import Realm;
@interface RLMClassProperty : NSObject
@property (nonatomic, readonly) RLMProperty *property;
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) RLMPropertyType type;
@property (nonatomic, readonly) BOOL isPrimaryKey;
@property (nonatomic, readonly) Class class;
- (instancetype)initWithProperty:(RLMProperty *)property isPrimaryKey:(BOOL)flag;
@end
================================================
FILE: RealmBrowser/Models/RLMClassProperty.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMClassNode.h"
#import "RLMClassProperty.h"
@implementation RLMClassProperty
- (instancetype)initWithProperty:(RLMProperty *)property isPrimaryKey:(BOOL)flag;
{
if (self = [super init]) {
_property = property;
_isPrimaryKey = flag;
}
return self;
}
- (NSString *)name
{
return _property.name;
}
- (RLMPropertyType)type
{
return _property.type;
}
- (Class)class
{
switch (self.type) {
case RLMPropertyTypeBool:
case RLMPropertyTypeInt:
case RLMPropertyTypeFloat:
case RLMPropertyTypeDouble:
return [NSNumber class];
case RLMPropertyTypeString:
return [NSString class];
case RLMPropertyTypeDate:
return [NSDate class];
case RLMPropertyTypeData:
case RLMPropertyTypeObject:
case RLMPropertyTypeAny:
return [RLMClassNode class];
default:
return nil;
}
}
@end
================================================
FILE: RealmBrowser/Models/RLMDocument.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Cocoa;
@import Realm;
#import "RLMRealmNode.h"
typedef NS_ENUM(NSInteger, RLMDocumentState) {
RLMDocumentStateRequiresFormatUpgrade,
RLMDocumentStateNeedsEncryptionKey,
RLMDocumentStateNeedsValidCredentials,
RLMDocumentStateLoading,
RLMDocumentStateLoaded,
RLMDocumentStateUnrecoverableError
};
@interface RLMDocument : NSDocument
@property (nonatomic, assign) RLMDocumentState state;
@property (nonatomic, strong) IBOutlet RLMRealmNode *presentedRealm;
@property (nonatomic, copy, readonly) NSURL *syncURL;
@property (nonatomic, copy, readonly) NSURL *authServerURL;
@property (nonatomic, strong, readonly) RLMSyncCredentials *credentials;
@property (nonatomic, strong, readonly) NSError *error;
- (instancetype)initWithContentsOfFileURL:(NSURL *)fileURL error:(NSError **)outError;
- (instancetype)initWithContentsOfSyncURL:(NSURL *)syncURL credentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL error:(NSError **)outError;
- (BOOL)loadByPerformingFormatUpgradeWithError:(NSError * __autoreleasing *)error;
- (BOOL)loadWithEncryptionKey:(NSData *)key error:(NSError * __autoreleasing *)error;
- (void)loadWithCredentials:(RLMSyncCredentials *)credentials completionHandler:(void (^)(NSError *error))completionHandler;
@end
================================================
FILE: RealmBrowser/Models/RLMDocument.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import AppSandboxFileAccess;
@import Realm.Private;
#import "RLMDocument.h"
#import "RLMBrowserConstants.h"
#import "RLMRealmBrowserWindowController.h"
#import "RLMSyncUtils.h"
@interface RLMDocument ()
@property (nonatomic, strong) NSURL *securityScopedURL;
@property (nonatomic, copy) NSURL *syncURL;
@property (nonatomic, copy) NSURL *authServerURL;
@property (nonatomic, strong) RLMSyncCredentials *credentials;
@property (nonatomic, strong) NSError *error;
@property (nonatomic, strong) RLMSyncUser *user;
@end
@implementation RLMDocument
- (instancetype)initWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError
{
if (![typeName.lowercaseString isEqualToString:kRealmUTIIdentifier]) {
return nil;
}
if (absoluteURL.isFileURL) {
return [self initWithContentsOfFileURL:absoluteURL error:outError];
} else {
return [self initWithContentsOfSyncURL:absoluteURL credentials:nil authServerURL:nil error:outError];
}
}
- (instancetype)initWithContentsOfFileURL:(NSURL *)fileURL error:(NSError **)outError {
if (![fileURL.pathExtension.lowercaseString isEqualToString:kRealmFileExtension]) {
return nil;
}
BOOL isDir = NO;
if (!([[NSFileManager defaultManager] fileExistsAtPath:fileURL.path isDirectory:&isDir] && isDir == NO)) {
return nil;
}
NSURL *folderURL = fileURL.URLByDeletingLastPathComponent;
self = [super init];
if (self != nil) {
self.fileURL = fileURL;
// In case we're trying to open Realm file located in app's container directory there is no reason to ask access permissions
if (![[NSFileManager defaultManager] isWritableFileAtPath:folderURL.path]) {
[[AppSandboxFileAccess fileAccess] requestAccessPermissionsForFileURL:folderURL persistPermission:YES withBlock:^(NSURL *securityScopedFileURL, NSData *bookmarkData) {
self.securityScopedURL = securityScopedFileURL;
}];
if (self.securityScopedURL == nil) {
return nil;
}
}
[self.securityScopedURL startAccessingSecurityScopedResource];
self.presentedRealm = [[RLMRealmNode alloc] initWithFileURL:self.fileURL];
if (![self loadWithError:outError] && self.state == RLMDocumentStateUnrecoverableError) {
return nil;
}
}
return self;
}
- (instancetype)initWithContentsOfSyncURL:(NSURL *)syncURL credentials:(RLMSyncCredentials *)credentials authServerURL:(NSURL *)authServerURL error:(NSError **)outError {
self = [super init];
if (self != nil) {
self.syncURL = syncURL;
self.authServerURL = authServerURL ?: authServerURLForSyncURL(syncURL);
self.state = RLMDocumentStateNeedsValidCredentials;
if (credentials != nil) {
[self loadWithCredentials:credentials completionHandler:nil];
}
}
return self;
}
- (void)dealloc {
if (self.securityScopedURL != nil) {
//In certain instances, RLMRealm's C++ destructor method will attempt to clean up
//specific auxiliary files belonging to this realm file.
//If the destructor call occurs after the access to the sandbox resource has been released here,
//and it attempts to delete any files, RLMRealm will throw an exception.
//Mac OS X apps only have a finite number of open sandbox resources at any given time, so while it's not necessary
//to release them straight away, it is still good practice to do so eventually.
//As such, this will release the handle a minute, after closing the document.
NSURL *scopedURL = self.securityScopedURL;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(60 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[scopedURL stopAccessingSecurityScopedResource];
});
}
}
- (BOOL)loadByPerformingFormatUpgradeWithError:(NSError **)error {
NSAssert(self.state == RLMDocumentStateRequiresFormatUpgrade, @"Invalid document state");
self.presentedRealm.disableFormatUpgrade = NO;
return [self loadWithError:error];
}
- (BOOL)loadWithEncryptionKey:(NSData *)key error:(NSError **)error {
NSAssert(self.state == RLMDocumentStateNeedsEncryptionKey, @"Invalid document state");
self.presentedRealm.encryptionKey = key;
return [self loadWithError:error];
}
- (void)loadWithCredentials:(RLMSyncCredentials *)credentials completionHandler:(void (^)(NSError *error))completionHandler {
// Workaround for access token auth, state will be set to RLMDocumentStateUnrecoverableError in case of invalid token
NSAssert(self.state == RLMDocumentStateNeedsValidCredentials || self.state == RLMDocumentStateUnrecoverableError, @"Invalid document state");
completionHandler = completionHandler ?: ^(NSError *error) {};
self.credentials = credentials;
self.state = RLMDocumentStateLoading;
__weak typeof(self) weakSelf = self;
[RLMSyncUser logInWithCredentials:self.credentials authServerURL:self.authServerURL onCompletion:^(RLMSyncUser *user, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (!weakSelf) {
return;
} else if (user == nil) {
weakSelf.state = RLMDocumentStateNeedsValidCredentials;
// FIXME: workaround for https://github.com/realm/realm-cocoa-private/issues/204
if (error.code == RLMSyncAuthErrorHTTPStatusCodeError && [[error.userInfo valueForKey:@"statusCode"] integerValue] == 400) {
NSMutableDictionary *userInfo = [error.userInfo mutableCopy];
[userInfo setValue:@"Invalid credentials." forKey:NSLocalizedDescriptionKey];
[userInfo setValue:@"Please check your authentication credentials and that you have an access to the specified URL." forKey:NSLocalizedRecoverySuggestionErrorKey];
NSError *authenticationError = [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:userInfo];
completionHandler(authenticationError);
} else {
completionHandler(error);
}
} else {
weakSelf.user = user;
RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
configuration.dynamic = YES;
configuration.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:weakSelf.user realmURL:weakSelf.syncURL];
configuration.syncConfiguration.enableSSLValidation = NO;
[RLMRealm asyncOpenWithConfiguration:configuration callbackQueue:dispatch_get_main_queue() callback:^(RLMRealm *realm, NSError *error) {
if (!weakSelf) {
return;
} else if (error) {
weakSelf.state = RLMDocumentStateUnrecoverableError;
} else {
weakSelf.presentedRealm = [[RLMRealmNode alloc] initWithConfiguration:configuration];
[weakSelf loadWithError:&error];
}
completionHandler(error);
}];
}
});
}];
}
- (BOOL)loadWithError:(NSError **)outError {
NSAssert(self.presentedRealm != nil, @"Presented Realm must be created before loading");
NSError *error;
if ([self.presentedRealm connect:&error]) {
self.state = RLMDocumentStateLoaded;
self.error = nil;
return YES;
} else {
switch (error.code) {
case RLMErrorFileAccess:
self.state = RLMDocumentStateNeedsEncryptionKey;
break;
case RLMErrorFileFormatUpgradeRequired:
self.state = RLMDocumentStateRequiresFormatUpgrade;
break;
default:
self.state = RLMDocumentStateUnrecoverableError;
break;
}
self.error = error;
if (outError != nil) {
*outError = error;
}
return NO;
}
}
#pragma mark NSDocument overrides
- (void)makeWindowControllers
{
RLMRealmBrowserWindowController *windowController = [[RLMRealmBrowserWindowController alloc] initWithWindowNibName:self.windowNibName];
[self addWindowController:windowController];
}
- (NSString *)windowNibName
{
return @"RLMDocument";
}
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
// As we do not use the usual file handling mechanism we just returns nil (but it is necessary
// to override this method as the default implementation throws an exception.
return nil;
}
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
// As we do not use the usual file handling mechanism we just returns YES (but it is necessary
// to override this method as the default implementation throws an exception.
return YES;
}
- (NSString *)displayName
{
return self.syncURL ? self.syncURL.absoluteString : self.fileURL.lastPathComponent.stringByDeletingPathExtension;
}
@end
================================================
FILE: RealmBrowser/Models/RLMNavigationStack.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Foundation;
#import "RLMNavigationState.h"
#import "RLMArrayNavigationState.h"
#import "RLMQueryNavigationState.h"
@interface RLMNavigationStack : NSObject
@property (nonatomic, readonly) RLMNavigationState *currentState;
- (void)reset;
- (RLMNavigationState *)pushStateWithTypeNode:(RLMTypeNode *)typeNode index:(NSInteger)selectionIndex;
- (RLMArrayNavigationState *)pushStateWithTypeNode:(RLMTypeNode *)typeNode index:(NSInteger)selectionIndex property:(RLMProperty *)property;
- (void)pushState:(RLMNavigationState *)state;
- (RLMNavigationState *)navigateBackward;
- (RLMNavigationState *)navigateForward;
- (BOOL)canNavigateBackward;
- (BOOL)canNavigateForward;
@end
================================================
FILE: RealmBrowser/Models/RLMNavigationStack.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMNavigationStack.h"
@implementation RLMNavigationStack {
NSMutableArray *stack;
NSInteger index;
}
- (instancetype)init
{
if (self = [super init]) {
stack = [[NSMutableArray alloc] initWithCapacity:200];
index = -1;
}
return self;
}
- (void)reset
{
[stack removeAllObjects];
index = -1;
}
- (RLMNavigationState *)currentState
{
if (0 <= index && index < stack.count ) {
return stack[index];
}
return nil;
}
- (RLMNavigationState *)pushStateWithTypeNode:(RLMTypeNode *)typeNode index:(NSInteger)selectionIndex
{
RLMNavigationState *state = [[RLMNavigationState alloc] initWithSelectedType:typeNode
index:selectionIndex];
[self pushState:state];
return state;
}
- (RLMArrayNavigationState *)pushStateWithTypeNode:(RLMTypeNode *)typeNode index:(NSInteger)selectionIndex property:(RLMProperty *)property
{
RLMArrayNavigationState *state = [[RLMArrayNavigationState alloc] initWithSelectedType:typeNode
typeIndex:selectionIndex
property:property
arrayIndex:0];
[self pushState:state];
return state;
}
- (void)pushState:(RLMNavigationState *)state
{
NSInteger lastIndex = (NSInteger)stack.count - 1;
if (index < lastIndex) {
[stack removeObjectsInRange:NSMakeRange(index, stack.count - index - 1)];
}
[stack addObject:state];
index++;
}
- (RLMNavigationState *)navigateBackward
{
if (index > 0) {
index--;
return stack[index];
}
return nil;
}
- (RLMNavigationState *)navigateForward
{
if (index < stack.count - 1) {
index++;
return stack[index];
}
return nil;
}
- (BOOL)canNavigateBackward
{
return stack.count > 1 && index > 0;
}
- (BOOL)canNavigateForward
{
return stack.count > 1 && index < stack.count - 1;
}
@end
================================================
FILE: RealmBrowser/Models/RLMNavigationState.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Foundation;
#import "RLMTypeNode.h"
@interface RLMNavigationState : NSObject
@property (nonatomic, readonly) RLMTypeNode *selectedType;
@property (nonatomic, readonly) NSInteger selectedInstanceIndex;
- (instancetype)initWithSelectedType:(RLMTypeNode *)type index:(NSInteger)index;
- (void)updateSelectionToIndex:(NSInteger)index;
@end
================================================
FILE: RealmBrowser/Models/RLMNavigationState.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMNavigationState.h"
@implementation RLMNavigationState
- (instancetype)initWithSelectedType:(RLMTypeNode *)type index:(NSInteger)index
{
if (self = [super init]) {
_selectedType = type;
_selectedInstanceIndex = index;
}
return self;
}
- (void)updateSelectionToIndex:(NSInteger)index
{
_selectedInstanceIndex = index;
}
- (BOOL)isEqualTo:(id)object
{
if ([object isKindOfClass:[self class]]) {
RLMNavigationState *comparedState = (RLMNavigationState *)object;
BOOL result = self.selectedType == comparedState.selectedType &&
self.selectedInstanceIndex == comparedState.selectedInstanceIndex;
return result;
}
return NO;
}
@end
================================================
FILE: RealmBrowser/Models/RLMObjectNode.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMTypeNode.h"
@class RLMArrayNode;
@interface RLMObjectNode : RLMTypeNode
@property (nonatomic) id<RLMRealmOutlineNode> childNode;
@property (nonatomic) id<RLMRealmOutlineNode> parentNode;
- (instancetype)initWithObject:(RLMObject *)object realm:(RLMRealm *)realm;
- (RLMArrayNode *)displayChildArrayFromProperty:(RLMProperty *)property object:(RLMObject *)object;
@end
================================================
FILE: RealmBrowser/Models/RLMObjectNode.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMObjectNode.h"
#import "RLMSidebarTableCellView.h"
#import "RLMArrayNode.h"
#import "RLMDescriptions.h"
@interface RLMObjectNode ()
@property (nonatomic) RLMObject *object;
@property (nonatomic) RLMDescriptions *realmDescriptions;
@end
@implementation RLMObjectNode {
NSMutableArray *displayedArrays;
}
- (instancetype)initWithObject:(RLMObject *)object realm:(RLMRealm *)realm
{
NSString *elementTypeName = object.className;
RLMSchema *realmSchema = realm.schema;
RLMObjectSchema *elementSchema = [realmSchema schemaForClassName:elementTypeName];
if (self = [super initWithSchema:elementSchema inRealm:realm]) {
displayedArrays = [[NSMutableArray alloc] initWithCapacity:10];
self.realmDescriptions = [[RLMDescriptions alloc] init];
self.object = object;
}
return self;
}
- (BOOL)isExpandable
{
return YES;
}
- (NSUInteger)numberOfChildNodes
{
return 1;
}
- (id<RLMRealmOutlineNode>)childNodeAtIndex:(NSUInteger)index
{
return self.childNode;
}
- (RLMArrayNode *)displayChildArrayFromProperty:(RLMProperty *)property object:(RLMObject *)object
{
RLMArrayNode *arrayNode = [[RLMArrayNode alloc] initWithReferringProperty:property onObject:object realm:self.realm];
if (displayedArrays.count == 0) {
[displayedArrays addObject:arrayNode];
}
else {
[displayedArrays replaceObjectAtIndex:0 withObject:arrayNode];
}
return arrayNode;
}
- (NSView *)cellViewForTableView:(NSTableView *)tableView
{
RLMSidebarTableCellView *result = [tableView makeViewWithIdentifier:@"MainCell" owner:self];
result.textField.stringValue = [self.realmDescriptions descriptionOfObject:self.object];
result.button.hidden = YES;
return result;
}
@end
================================================
FILE: RealmBrowser/Models/RLMQueryNavigationState.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMNavigationState.h"
#import "RLMArrayNode.h"
@interface RLMQueryNavigationState : RLMNavigationState
@property (nonatomic, readonly) RLMResults *results;
@property (nonatomic, readonly) NSString *searchText;
- (instancetype)initWithQuery:(NSString*)searchText type:(RLMTypeNode *)type results:(RLMResults *)results;
@end
================================================
FILE: RealmBrowser/Models/RLMQueryNavigationState.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
#import "RLMQueryNavigationState.h"
@implementation RLMQueryNavigationState
- (instancetype)initWithQuery:(NSString*)searchText type:(RLMTypeNode *)type results:(RLMResults *)results;
{
if (self = [super initWithSelectedType:type
index:0]) {
_results = results;
_searchText = searchText;
}
return self;
}
@end
================================================
FILE: RealmBrowser/Models/RLMRealmNode.h
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Foundation;
@import Realm;
#import "RLMClassNode.h"
#import "RLMRealmOutlineNode.h"
@interface RLMRealmNode : NSObject <RLMRealmOutlineNode>
@property (nonatomic, readonly) RLMRealm *realm;
@property (nonatomic, readonly) NSArray *topLevelClasses;
@property (nonatomic, strong) NSData *encryptionKey;
@property (nonatomic, assign) BOOL disableFormatUpgrade;
- (instancetype)initWithConfiguration:(RLMRealmConfiguration *)configuration;
- (instancetype)initWithFileURL:(NSURL *)fileURL;
- (instancetype)initWithSyncURL:(NSURL *)syncURL user:(RLMSyncUser *)user;
- (BOOL)connect:(NSError **)error;
@end
================================================
FILE: RealmBrowser/Models/RLMRealmNode.m
================================================
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2014-2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
@import Realm.Private;
@import Realm.Dynamic;
#import "RLMRealmNode.h"
@interface RLMRealmNode ()
@property (nonatomic, strong) RLMRealmConfiguration *configuration;
@end
@implementation RLMRealmNode
- (instancetype)initWithConfiguration:(RLMRealmConfiguration *)configuration {
self = [super init];
if (self) {
self.configuration = configuration;
}
return self;
}
- (instancetype)initWithFileURL:(NSURL *)fileURL {
RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
configuration.dynamic = YES;
configuration.fileURL = fileURL;
self = [self initWithConfiguration:configuration];
if (self) {
self.disableFormatUpgrade = YES;
}
return self;
}
- (instancetype)initWithSyncURL:(NSURL *)syncURL user:(RLMSyncUser *)user {
RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
configuration.dynamic = YES;
configuration.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:user realmURL:syncURL];
configuration.syncConfiguration.enableSSLValidation = NO;
return [self initWithConfiguration:configuration];
}
- (BOOL)connect:(NSError **)error {
self.configuration.encryptionKey = self.encryptionKey;
self.configuration.disableFormatUpgrade = self.disableFormatUpgrade;
NSError *localError;
_realm = [RLMRealm realmWithConfiguration:self.configuration error:&localError];
if (localError) {
NSLog(@"Realm was opened with error: %@", localError);
} else {
_topLevelClasses = [self constructTopLevelClasses];
}
if (error) {
*error = localError;
}
return !localError;
}
#pragma mark - RLMRealmOutlineNode implementation
- (BOOL)isRootNode
{
return YES;
}
- (BOOL)isExpandable
{
return self.topLevelClasses.count != 0;
}
- (NSUInteger)numberOfChildNodes
{
return self.topLevelClasses.count;
}
- (id<RLMRealmOutlineNode>)childNodeAtIndex:(NSUInteger)index
{
return self.topLevelClasses[index];
}
- (BOOL)hasToolTip
{
return YES;
}
- (NSString *)toolTipString
{
return self.configuration.fileURL.path;
}
- (NSView *)cellViewForTableView:(NSTableView *)tableView
{
NSTableCellView *headerView = [tableView makeViewWithIdentifier:@"HeaderCell" owner:self];
headerView.textField.stringValue = @"Models";
return headerView;
}
#pragma mark - Private methods
- (NSArray *)constructTopLevelClasses
{
RLMSchema *realmSchema = _realm.schema;
NSArray *objectSchemas = realmSchema.objectSchema;
NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:objectSchemas.count];
for (RLMObjectSchema *objectSchema in objectSchemas) {
if (objectSchema.properties.count > 0) {
RLMClassNode *tableNode = [[RLMClassNode alloc] initWithSchema:objectSchema inRealm:_realm];
[result addObject:tableNode];
}
}
[result sortUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]];
return result;
}
@end
================================================
FILE: RealmBrowser/Models/RLMRealmOutlineNode.h
================================================
////////////////////////////////////////////////////
gitextract_izcrtqob/
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── Jenkinsfile
├── Podfile
├── README.md
├── RealmBrowser/
│ ├── Application/
│ │ ├── RLMApplicationDelegate+CrashReporting.h
│ │ ├── RLMApplicationDelegate+CrashReporting.m
│ │ ├── RLMApplicationDelegate.h
│ │ └── RLMApplicationDelegate.m
│ ├── Controllers/
│ │ ├── RLMDocumentController.h
│ │ ├── RLMDocumentController.m
│ │ ├── RLMEncryptionKeyWindowController.h
│ │ ├── RLMEncryptionKeyWindowController.m
│ │ ├── RLMExportIndicatorWindowController.h
│ │ ├── RLMExportIndicatorWindowController.m
│ │ ├── RLMInstanceTableViewController.h
│ │ ├── RLMInstanceTableViewController.m
│ │ ├── RLMObjectLinkSelectionViewController.h
│ │ ├── RLMObjectLinkSelectionViewController.m
│ │ ├── RLMRealmBrowserWindowController.h
│ │ ├── RLMRealmBrowserWindowController.m
│ │ ├── RLMTypeOutlineViewController.h
│ │ ├── RLMTypeOutlineViewController.m
│ │ ├── RLMViewController.h
│ │ └── RLMViewController.m
│ ├── Models/
│ │ ├── RLMArrayNavigationState.h
│ │ ├── RLMArrayNavigationState.m
│ │ ├── RLMArrayNode.h
│ │ ├── RLMArrayNode.m
│ │ ├── RLMClassNode.h
│ │ ├── RLMClassNode.m
│ │ ├── RLMClassProperty.h
│ │ ├── RLMClassProperty.m
│ │ ├── RLMDocument.h
│ │ ├── RLMDocument.m
│ │ ├── RLMNavigationStack.h
│ │ ├── RLMNavigationStack.m
│ │ ├── RLMNavigationState.h
│ │ ├── RLMNavigationState.m
│ │ ├── RLMObjectNode.h
│ │ ├── RLMObjectNode.m
│ │ ├── RLMQueryNavigationState.h
│ │ ├── RLMQueryNavigationState.m
│ │ ├── RLMRealmNode.h
│ │ ├── RLMRealmNode.m
│ │ ├── RLMRealmOutlineNode.h
│ │ ├── RLMResultsNode.h
│ │ ├── RLMResultsNode.m
│ │ ├── RLMTableColumn.h
│ │ ├── RLMTableColumn.m
│ │ ├── RLMTypeNode.h
│ │ └── RLMTypeNode.m
│ ├── Resources/
│ │ ├── Documents/
│ │ │ └── en.lproj/
│ │ │ └── Credits.rtf
│ │ ├── Icons/
│ │ │ └── RealmFileIcon.icns
│ │ ├── Images.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── Contents.json
│ │ │ ├── Mavericks10.9.imageset/
│ │ │ │ └── Contents.json
│ │ │ ├── RealmLocked.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── RealmUnlocked.imageset/
│ │ │ └── Contents.json
│ │ └── UI/
│ │ ├── Base.lproj/
│ │ │ ├── EncryptionKeyWindow.xib
│ │ │ ├── MainMenu.xib
│ │ │ └── RLMDocument.xib
│ │ ├── RLMExportIndicatorWindowController.xib
│ │ └── RLMObjectLinkSelectionViewController.xib
│ ├── Support/
│ │ ├── NSColor+ByteSizeFactory.h
│ │ ├── NSColor+ByteSizeFactory.m
│ │ ├── RLMBrowserConstants.h
│ │ ├── RLMBrowserConstants.m
│ │ ├── RLMDescriptions.h
│ │ ├── RLMDescriptions.m
│ │ ├── RLMModelExporter.h
│ │ ├── RLMModelExporter.m
│ │ ├── RLMTestDataGenerator.h
│ │ ├── RLMTestDataGenerator.m
│ │ ├── RLMUtils.h
│ │ ├── RLMUtils.m
│ │ ├── TestClasses.h
│ │ └── TestClasses.m
│ ├── Supporting Files/
│ │ ├── Realm Browser.entitlements
│ │ ├── RealmBrowser-Info.plist
│ │ ├── RealmBrowser-Prefix.pch
│ │ ├── en.lproj/
│ │ │ └── InfoPlist.strings
│ │ └── main.m
│ └── Views/
│ ├── RLMBadgeTableCellView.h
│ ├── RLMBadgeTableCellView.m
│ ├── RLMBasicTableCellView.h
│ ├── RLMBasicTableCellView.m
│ ├── RLMBoolTableCellView.h
│ ├── RLMBoolTableCellView.m
│ ├── RLMImageTableCellView.h
│ ├── RLMImageTableCellView.m
│ ├── RLMLinkTableCellView.h
│ ├── RLMLinkTableCellView.m
│ ├── RLMNumberTableCellView.h
│ ├── RLMNumberTableCellView.m
│ ├── RLMOptionalBoolTableCellView.h
│ ├── RLMOptionalBoolTableCellView.m
│ ├── RLMSidebarTableCellView.h
│ ├── RLMSidebarTableCellView.m
│ ├── RLMTableCellView.h
│ ├── RLMTableCellView.m
│ ├── RLMTableHeaderCell.h
│ ├── RLMTableHeaderCell.m
│ ├── RLMTableView.h
│ └── RLMTableView.m
├── RealmBrowser.xcodeproj/
│ ├── project.pbxproj
│ └── xcshareddata/
│ └── xcschemes/
│ └── Realm Browser.xcscheme
├── RealmBrowserSync/
│ ├── Controllers/
│ │ ├── RLMAccessTokenCredentialViewController.h
│ │ ├── RLMAccessTokenCredentialViewController.m
│ │ ├── RLMCloudKitCredentialViewController.h
│ │ ├── RLMCloudKitCredentialViewController.m
│ │ ├── RLMConnectToServerWindowController.h
│ │ ├── RLMConnectToServerWindowController.m
│ │ ├── RLMConnectionIndicatorWindowController.h
│ │ ├── RLMConnectionIndicatorWindowController.m
│ │ ├── RLMCredentialViewController+Private.h
│ │ ├── RLMCredentialViewController.h
│ │ ├── RLMCredentialViewController.m
│ │ ├── RLMCredentialsViewController.h
│ │ ├── RLMCredentialsViewController.m
│ │ ├── RLMFacebookCredentialViewController.h
│ │ ├── RLMFacebookCredentialViewController.m
│ │ ├── RLMGoogleCredentialViewController.h
│ │ ├── RLMGoogleCredentialViewController.m
│ │ ├── RLMLoginWindowController.h
│ │ ├── RLMLoginWindowController.m
│ │ ├── RLMOpenSyncURLWindowController.h
│ │ ├── RLMOpenSyncURLWindowController.m
│ │ ├── RLMSyncServerBrowserWindowController.h
│ │ ├── RLMSyncServerBrowserWindowController.m
│ │ ├── RLMUsernameCredentialViewController.h
│ │ ├── RLMUsernameCredentialViewController.m
│ │ ├── RLMWelcomeWindowController.h
│ │ └── RLMWelcomeWindowController.m
│ ├── Library/
│ │ ├── NSView+RLMExtensions.h
│ │ ├── NSView+RLMExtensions.m
│ │ ├── RLMKeychainInfo+RLMSyncCredentials.h
│ │ ├── RLMKeychainInfo+RLMSyncCredentials.m
│ │ ├── RLMKeychainInfo.h
│ │ ├── RLMKeychainInfo.m
│ │ ├── RLMKeychainStore.h
│ │ ├── RLMKeychainStore.m
│ │ ├── RLMSyncURLValueTransformer.h
│ │ ├── RLMSyncURLValueTransformer.m
│ │ ├── RLMSyncUtils.h
│ │ ├── RLMSyncUtils.m
│ │ ├── RLMWindowController.h
│ │ └── RLMWindowController.m
│ └── Resources/
│ ├── AccessTokenCredentialView.xib
│ ├── CloudKitCredentialView.xib
│ ├── ConnectToServerWindow.xib
│ ├── ConnectionIndicatorWindow.xib
│ ├── CredentialsView.xib
│ ├── FacebookCredentialView.xib
│ ├── GoogleCredentialView.xib
│ ├── LoginWindow.xib
│ ├── OpenSyncURLWindow.xib
│ ├── SyncServerBrowserWindow.xib
│ ├── UsernameCredentialView.xib
│ └── WelcomeWindow.xib
├── RealmBrowserTests/
│ ├── RLMTestObjects.h
│ ├── RLMTestObjects.m
│ ├── RealmBrowserTests.m
│ └── Supporting Files/
│ ├── RealmBrowserTests-Info.plist
│ └── en.lproj/
│ └── InfoPlist.strings
└── fastlane/
├── Appfile
├── Fastfile
└── README.md
SYMBOL INDEX (4 symbols across 4 files)
FILE: RealmBrowser/Models/RLMDocument.h
type RLMDocumentStateRequiresFormatUpgrade (line 24) | typedef NS_ENUM(NSInteger, RLMDocumentState) {
FILE: RealmBrowser/Support/RLMModelExporter.h
type RLMModelExporterLanguageJava (line 21) | typedef NS_ENUM(NSInteger, RLMModelExporterLanguage) {
FILE: RealmBrowser/Views/RLMTableView.h
type RLMTableLocation (line 23) | typedef struct {
FILE: RealmBrowserSync/Library/RLMWindowController.h
type RLMWindowDisplayModeNone (line 21) | typedef NS_ENUM(NSInteger, RLMWindowDisplayMode) {
Condensed preview — 171 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (717K chars).
[
{
"path": ".gitignore",
"chars": 291,
"preview": "# Xcode\n.DS_Store\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev"
},
{
"path": ".travis.yml",
"chars": 100,
"preview": "language: objective-c\nosx_image: xcode8.3\ninstall: bundle install\nscript: bundle exec fastlane test\n"
},
{
"path": "CHANGELOG.md",
"chars": 7540,
"preview": "3.0.1 Release notes (2017-11-03)\n=============================================================\n* Fix crash when deleting"
},
{
"path": "CONTRIBUTING.md",
"chars": 1795,
"preview": "# Contributing\n\n## Filing Issues\n\nWhether you find a bug, typo or an API call that could be clarified, please [file an i"
},
{
"path": "Gemfile",
"chars": 62,
"preview": "source 'https://rubygems.org'\n\ngem 'cocoapods'\ngem 'fastlane'\n"
},
{
"path": "Jenkinsfile",
"chars": 1410,
"preview": "#!groovy\n\nnode('osx_vegas') {\n dir('realm-browser') {\n wrap([$class: 'AnsiColorBuildWrapper']) {\n stage('SCM') "
},
{
"path": "Podfile",
"chars": 654,
"preview": "source 'https://github.com/CocoaPods/Specs.git'\n\nplatform :osx, '10.9'\nuse_frameworks!\n\ntarget 'RealmBrowser' do\n pod"
},
{
"path": "README.md",
"chars": 2006,
"preview": "# NOTE - This tool is now deprecated!\nOur future development efforts will go into the new cross platform Realm Studio, n"
},
{
"path": "RealmBrowser/Application/RLMApplicationDelegate+CrashReporting.h",
"chars": 872,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2016 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Application/RLMApplicationDelegate+CrashReporting.m",
"chars": 1165,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2016 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Application/RLMApplicationDelegate.h",
"chars": 926,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Application/RLMApplicationDelegate.m",
"chars": 33038,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMDocumentController.h",
"chars": 1203,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowser/Controllers/RLMDocumentController.m",
"chars": 3025,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowser/Controllers/RLMEncryptionKeyWindowController.h",
"chars": 899,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMEncryptionKeyWindowController.m",
"chars": 2475,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMExportIndicatorWindowController.h",
"chars": 847,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMExportIndicatorWindowController.m",
"chars": 1241,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMInstanceTableViewController.h",
"chars": 1243,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMInstanceTableViewController.m",
"chars": 44799,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMObjectLinkSelectionViewController.h",
"chars": 465,
"preview": "//\n// RLMObjectLinkSelectionViewController.h\n// RealmBrowser\n//\n// Created by sbuglakov on 24/08/15.\n// Copyright (c"
},
{
"path": "RealmBrowser/Controllers/RLMObjectLinkSelectionViewController.m",
"chars": 1414,
"preview": "//\n// RLMObjectLinkSelectionViewController.m\n// RealmBrowser\n//\n// Created by sbuglakov on 24/08/15.\n// Copyright (c"
},
{
"path": "RealmBrowser/Controllers/RLMRealmBrowserWindowController.h",
"chars": 1509,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMRealmBrowserWindowController.m",
"chars": 23481,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMTypeOutlineViewController.h",
"chars": 957,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMTypeOutlineViewController.m",
"chars": 7413,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMViewController.h",
"chars": 1459,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Controllers/RLMViewController.m",
"chars": 2190,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMArrayNavigationState.h",
"chars": 1135,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMArrayNavigationState.m",
"chars": 1540,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMArrayNode.h",
"chars": 1257,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMArrayNode.m",
"chars": 6025,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMClassNode.h",
"chars": 1250,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMClassNode.m",
"chars": 4155,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMClassProperty.h",
"chars": 1167,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMClassProperty.m",
"chars": 1704,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMDocument.h",
"chars": 2042,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMDocument.m",
"chars": 9941,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMNavigationStack.h",
"chars": 1442,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMNavigationStack.m",
"chars": 2905,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMNavigationState.h",
"chars": 1099,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMNavigationState.m",
"chars": 1500,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMObjectNode.h",
"chars": 1133,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMObjectNode.m",
"chars": 2555,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMQueryNavigationState.h",
"chars": 1086,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMQueryNavigationState.m",
"chars": 1128,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMRealmNode.h",
"chars": 1365,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMRealmNode.m",
"chars": 3804,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMRealmOutlineNode.h",
"chars": 1097,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMResultsNode.h",
"chars": 938,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMResultsNode.m",
"chars": 1865,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMTableColumn.h",
"chars": 939,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMTableColumn.m",
"chars": 2447,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMTypeNode.h",
"chars": 1470,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Models/RLMTypeNode.m",
"chars": 3243,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Resources/Documents/en.lproj/Credits.rtf",
"chars": 526,
"preview": "{\\rtf1\\ansi\\ansicpg1252\\cocoartf1347\\cocoasubrtf570\n{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;}\n{\\colortbl;\\red255\\green25"
},
{
"path": "RealmBrowser/Resources/Images.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1440,
"preview": "{\n \"images\" : [\n {\n \"size\" : \"16x16\",\n \"idiom\" : \"mac\",\n \"filename\" : \"RealmBrowserAppIcon16x16.png\","
},
{
"path": "RealmBrowser/Resources/Images.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "RealmBrowser/Resources/Images.xcassets/Mavericks10.9.imageset/Contents.json",
"chars": 4857,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"apple-17.png\",\n \"language-direction\" : \"rig"
},
{
"path": "RealmBrowser/Resources/Images.xcassets/RealmLocked.imageset/Contents.json",
"chars": 274,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"mac\",\n \"filename\" : \"RealmLocked.png\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "RealmBrowser/Resources/Images.xcassets/RealmUnlocked.imageset/Contents.json",
"chars": 278,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"mac\",\n \"filename\" : \"RealmUnlocked.png\",\n \"scale\" : \"1x\"\n },\n {\n"
},
{
"path": "RealmBrowser/Resources/UI/Base.lproj/EncryptionKeyWindow.xib",
"chars": 9279,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "RealmBrowser/Resources/UI/Base.lproj/MainMenu.xib",
"chars": 22897,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "RealmBrowser/Resources/UI/Base.lproj/RLMDocument.xib",
"chars": 27658,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "RealmBrowser/Resources/UI/RLMExportIndicatorWindowController.xib",
"chars": 4385,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowser/Resources/UI/RLMObjectLinkSelectionViewController.xib",
"chars": 5462,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowser/Support/NSColor+ByteSizeFactory.h",
"chars": 1082,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/NSColor+ByteSizeFactory.m",
"chars": 1677,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMBrowserConstants.h",
"chars": 1377,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMBrowserConstants.m",
"chars": 1568,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMDescriptions.h",
"chars": 1115,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMDescriptions.m",
"chars": 11815,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMModelExporter.h",
"chars": 1186,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMModelExporter.m",
"chars": 28543,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMTestDataGenerator.h",
"chars": 1096,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMTestDataGenerator.m",
"chars": 7859,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMUtils.h",
"chars": 818,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2016 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/RLMUtils.m",
"chars": 895,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2016 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/TestClasses.h",
"chars": 1391,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Support/TestClasses.m",
"chars": 890,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Supporting Files/Realm Browser.entitlements",
"chars": 366,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "RealmBrowser/Supporting Files/RealmBrowser-Info.plist",
"chars": 2920,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "RealmBrowser/Supporting Files/RealmBrowser-Prefix.pch",
"chars": 163,
"preview": "//\n// Prefix header\n//\n// The contents of this file are implicitly included at the beginning of every source file.\n//\n"
},
{
"path": "RealmBrowser/Supporting Files/en.lproj/InfoPlist.strings",
"chars": 45,
"preview": "/* Localized versions of Info.plist keys */\n\n"
},
{
"path": "RealmBrowser/Supporting Files/main.m",
"chars": 851,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMBadgeTableCellView.h",
"chars": 906,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMBadgeTableCellView.m",
"chars": 4930,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMBasicTableCellView.h",
"chars": 837,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMBasicTableCellView.m",
"chars": 3864,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMBoolTableCellView.h",
"chars": 885,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMBoolTableCellView.m",
"chars": 2351,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMImageTableCellView.h",
"chars": 852,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMImageTableCellView.m",
"chars": 828,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMLinkTableCellView.h",
"chars": 846,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMLinkTableCellView.m",
"chars": 1098,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMNumberTableCellView.h",
"chars": 853,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMNumberTableCellView.m",
"chars": 5114,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMOptionalBoolTableCellView.h",
"chars": 906,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMOptionalBoolTableCellView.m",
"chars": 2650,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMSidebarTableCellView.h",
"chars": 870,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMSidebarTableCellView.m",
"chars": 832,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMTableCellView.h",
"chars": 923,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMTableCellView.m",
"chars": 3884,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMTableHeaderCell.h",
"chars": 909,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMTableHeaderCell.m",
"chars": 2229,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMTableView.h",
"chars": 3380,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser/Views/RLMTableView.m",
"chars": 21257,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowser.xcodeproj/project.pbxproj",
"chars": 91112,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "RealmBrowser.xcodeproj/xcshareddata/xcschemes/Realm Browser.xcscheme",
"chars": 3978,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"0830\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "RealmBrowserSync/Controllers/RLMAccessTokenCredentialViewController.h",
"chars": 871,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMAccessTokenCredentialViewController.m",
"chars": 1661,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCloudKitCredentialViewController.h",
"chars": 868,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCloudKitCredentialViewController.m",
"chars": 1538,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMConnectToServerWindowController.h",
"chars": 986,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMConnectToServerWindowController.m",
"chars": 6011,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMConnectionIndicatorWindowController.h",
"chars": 903,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMConnectionIndicatorWindowController.m",
"chars": 1333,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCredentialViewController+Private.h",
"chars": 884,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCredentialViewController.h",
"chars": 903,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCredentialViewController.m",
"chars": 2055,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCredentialsViewController.h",
"chars": 1582,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMCredentialsViewController.m",
"chars": 5317,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMFacebookCredentialViewController.h",
"chars": 868,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMFacebookCredentialViewController.m",
"chars": 1489,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMGoogleCredentialViewController.h",
"chars": 866,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMGoogleCredentialViewController.m",
"chars": 1528,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMLoginWindowController.h",
"chars": 968,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMLoginWindowController.m",
"chars": 2543,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMOpenSyncURLWindowController.h",
"chars": 977,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMOpenSyncURLWindowController.m",
"chars": 3696,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMSyncServerBrowserWindowController.h",
"chars": 1060,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMSyncServerBrowserWindowController.m",
"chars": 6403,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMUsernameCredentialViewController.h",
"chars": 868,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMUsernameCredentialViewController.m",
"chars": 1909,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMWelcomeWindowController.h",
"chars": 843,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Controllers/RLMWelcomeWindowController.m",
"chars": 2100,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/NSView+RLMExtensions.h",
"chars": 862,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/NSView+RLMExtensions.m",
"chars": 1329,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/RLMKeychainInfo+RLMSyncCredentials.h",
"chars": 479,
"preview": "//\n// RLMKeychainInfo+RLMSyncCredentials.h\n// RealmBrowser\n//\n// Created by Guilherme Rambo on 12/04/17.\n// Copyrigh"
},
{
"path": "RealmBrowserSync/Library/RLMKeychainInfo+RLMSyncCredentials.m",
"chars": 2162,
"preview": "//\n// RLMKeychainInfo+RLMSyncCredentials.m\n// RealmBrowser\n//\n// Created by Guilherme Rambo on 12/04/17.\n// Copyrigh"
},
{
"path": "RealmBrowserSync/Library/RLMKeychainInfo.h",
"chars": 367,
"preview": "//\n// RLMKeychainInfo.h\n// RealmBrowser\n//\n// Created by Guilherme Rambo on 11/04/17.\n// Copyright © 2017 Realm inc."
},
{
"path": "RealmBrowserSync/Library/RLMKeychainInfo.m",
"chars": 213,
"preview": "//\n// RLMKeychainInfo.m\n// RealmBrowser\n//\n// Created by Guilherme Rambo on 11/04/17.\n// Copyright © 2017 Realm inc."
},
{
"path": "RealmBrowserSync/Library/RLMKeychainStore.h",
"chars": 428,
"preview": "//\n// RLMKeychainStore.h\n// RealmBrowser\n//\n// Created by Guilherme Rambo on 11/04/17.\n// Copyright © 2017 Realm inc"
},
{
"path": "RealmBrowserSync/Library/RLMKeychainStore.m",
"chars": 4403,
"preview": "//\n// RLMKeychainStore.m\n// RealmBrowser\n//\n// Created by Guilherme Rambo on 11/04/17.\n// Copyright © 2017 Realm inc"
},
{
"path": "RealmBrowserSync/Library/RLMSyncURLValueTransformer.h",
"chars": 844,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/RLMSyncURLValueTransformer.m",
"chars": 1343,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/RLMSyncUtils.h",
"chars": 813,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/RLMSyncUtils.m",
"chars": 1186,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/RLMWindowController.h",
"chars": 1605,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Library/RLMWindowController.m",
"chars": 4301,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2016 Realm Inc.\n//\n// Licen"
},
{
"path": "RealmBrowserSync/Resources/AccessTokenCredentialView.xib",
"chars": 4216,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/CloudKitCredentialView.xib",
"chars": 4216,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/ConnectToServerWindow.xib",
"chars": 10950,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "RealmBrowserSync/Resources/ConnectionIndicatorWindow.xib",
"chars": 5613,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/CredentialsView.xib",
"chars": 3268,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/FacebookCredentialView.xib",
"chars": 4216,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/GoogleCredentialView.xib",
"chars": 4211,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/LoginWindow.xib",
"chars": 7177,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "RealmBrowserSync/Resources/OpenSyncURLWindow.xib",
"chars": 9582,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/SyncServerBrowserWindow.xib",
"chars": 16239,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "RealmBrowserSync/Resources/UsernameCredentialView.xib",
"chars": 6624,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserSync/Resources/WelcomeWindow.xib",
"chars": 9537,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3"
},
{
"path": "RealmBrowserTests/RLMTestObjects.h",
"chars": 963,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowserTests/RLMTestObjects.m",
"chars": 934,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowserTests/RealmBrowserTests.m",
"chars": 3927,
"preview": "////////////////////////////////////////////////////////////////////////////\n//\n// Copyright 2014-2015 Realm Inc.\n//\n// "
},
{
"path": "RealmBrowserTests/Supporting Files/RealmBrowserTests-Info.plist",
"chars": 684,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "RealmBrowserTests/Supporting Files/en.lproj/InfoPlist.strings",
"chars": 45,
"preview": "/* Localized versions of Info.plist keys */\n\n"
},
{
"path": "fastlane/Appfile",
"chars": 60,
"preview": "app_identifier \"io.realm.realmbrowser\"\nteam_id \"QX5CR2FTN2\"\n"
},
{
"path": "fastlane/Fastfile",
"chars": 2528,
"preview": "# Make sure that the environment variable below are defined before\n# running :build with submit_to_XXX arguments\n#\n# HOC"
},
{
"path": "fastlane/README.md",
"chars": 1452,
"preview": "fastlane documentation\n================\n# Installation\n\nMake sure you have the latest version of the Xcode command line "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the realm/realm-browser-osx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 171 files (655.8 KB), approximately 162.7k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.