RLMPRealmPlugin.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import "RLMPRealmPlugin.h"
  19. #import "RLMPSimulatorManager.h"
  20. static RLMPRealmPlugin *sharedPlugin;
  21. static NSString *const RootDeviceSimulatorPath = @"Library/Developer/CoreSimulator/Devices";
  22. static NSString *const DeviceSimulatorApplicationPath = @"data/Containers/Data/Application";
  23. static NSString *const RLMPErrorDomain = @"io.Realm.error";
  24. static NSArray * RLMPGlobFilesAtDirectoryURLWithPredicate(NSFileManager *fileManager, NSURL *directoryURL, NSPredicate *filteredPredicate, BOOL (^handler)(NSURL *URL, NSError *error))
  25. {
  26. NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtURL:directoryURL
  27. includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
  28. options:NSDirectoryEnumerationSkipsHiddenFiles
  29. errorHandler:handler];
  30. NSMutableArray *fileURLs = [NSMutableArray array];
  31. for (NSURL *fileURL in directoryEnumerator) {
  32. NSString *fileName;
  33. [fileURL getResourceValue:&fileName forKey:NSURLNameKey error:nil];
  34. NSString *isDirectory;
  35. [fileURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
  36. // Check whether it is a directory or not
  37. if (![isDirectory boolValue]) {
  38. [fileURLs addObject:fileURL];
  39. }
  40. }
  41. return [fileURLs filteredArrayUsingPredicate:filteredPredicate];
  42. }
  43. @interface RLMPRealmPlugin()
  44. @property (nonatomic, strong) NSBundle *bundle;
  45. @property (nonatomic, strong) NSURL *browserUrl;
  46. @end
  47. @implementation RLMPRealmPlugin
  48. + (void)pluginDidLoad:(NSBundle *)plugin
  49. {
  50. static dispatch_once_t onceToken;
  51. NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"];
  52. if ([currentApplicationName isEqual:@"Xcode"]) {
  53. dispatch_once(&onceToken, ^{
  54. sharedPlugin = [[self alloc] initWithBundle:plugin];
  55. });
  56. }
  57. }
  58. - (id)initWithBundle:(NSBundle *)plugin
  59. {
  60. if (self = [super init]) {
  61. // Save reference to plugin's bundle, for resource acccess
  62. self.bundle = plugin;
  63. [[NSNotificationCenter defaultCenter] addObserver:self
  64. selector:@selector(didApplicationFinishLaunchingNotification:)
  65. name:NSApplicationDidFinishLaunchingNotification
  66. object:nil];
  67. }
  68. return self;
  69. }
  70. - (void)didApplicationFinishLaunchingNotification:(NSNotification *)notification
  71. {
  72. // Look for the Realm Browser
  73. NSString *urlString = [[NSWorkspace sharedWorkspace] fullPathForApplication:@"Realm Browser"];
  74. if (urlString) {
  75. self.browserUrl = [NSURL fileURLWithPath:urlString];
  76. // Create menu item to open Browser under File:
  77. NSMenuItem *menuItem = [[NSApp mainMenu] itemWithTitle:@"File"];
  78. if (menuItem) {
  79. [[menuItem submenu] addItem:[NSMenuItem separatorItem]];
  80. NSMenuItem *actionMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open Realm..."
  81. action:@selector(openBrowser)
  82. keyEquivalent:@""];
  83. [actionMenuItem setTarget:self];
  84. [[menuItem submenu] addItem:actionMenuItem];
  85. }
  86. }
  87. else {
  88. NSLog(@"Realm Plugin: Couldn't find Realm Browser. Will not show 'Open Realm...' menu item.");
  89. }
  90. }
  91. - (void)openBrowser
  92. {
  93. // This shouldn't be possible to call without having the Browser installed
  94. if (!self.browserUrl) {
  95. NSString *title = @"Please install the Realm Browser";
  96. NSString *message = @"You need to install the Realm Browser in order to use it from this plugin. Please visit realm.io for more information.";
  97. NSError *error = [NSError errorWithDomain:RLMPErrorDomain
  98. code:-1
  99. userInfo:@{ NSLocalizedDescriptionKey : title,
  100. NSLocalizedRecoverySuggestionErrorKey : message }];
  101. [self showError:error];
  102. return;
  103. }
  104. // Find Device UUID
  105. NSString *bootedSimulatorUUID = [RLMPSimulatorManager bootedSimulatorUUID];
  106. // Find Realm File URL
  107. NSArray *realmFileURLs = [self realmFilesURLWithDeviceUUID:bootedSimulatorUUID];
  108. if (realmFileURLs.count == 0) {
  109. NSString *title = @"Unable to find Realm file";
  110. NSString *message = @"You must launch iOS Simulator with app that uses Realm";
  111. NSError *error = [NSError errorWithDomain:RLMPErrorDomain
  112. code:-1
  113. userInfo:@{ NSLocalizedDescriptionKey : title,
  114. NSLocalizedRecoverySuggestionErrorKey : message }];
  115. [self showError:error];
  116. return;
  117. }
  118. NSMutableArray *arguments = [NSMutableArray array];
  119. for (NSURL *realmFileURL in realmFileURLs) {
  120. [arguments addObject:realmFileURL.path];
  121. }
  122. NSDictionary *configuration = @{ NSWorkspaceLaunchConfigurationArguments : arguments };
  123. // Show confirmation alert if 2 or more files are detected
  124. if (arguments.count > 1) {
  125. NSAlert *alert = [[NSAlert alloc] init];
  126. [alert setMessageText:@"Realm Browser"];
  127. [alert setInformativeText:[NSString stringWithFormat:@"%ld Realm files are detected. Are you sure to open them at once?", arguments.count]];
  128. [alert setAlertStyle:NSInformationalAlertStyle];
  129. [alert addButtonWithTitle:@"OK"];
  130. [alert addButtonWithTitle:@"Cancel"];
  131. [alert beginSheetModalForWindow:[NSApp mainWindow] completionHandler:^(NSModalResponse returnCode) {
  132. if (returnCode == NSAlertFirstButtonReturn) {
  133. [self openRealmBrowserWithConfiguration:configuration];
  134. }
  135. }];
  136. } else {
  137. [self openRealmBrowserWithConfiguration:configuration];
  138. }
  139. }
  140. - (void)openRealmBrowserWithConfiguration:(NSDictionary *)configuration
  141. {
  142. NSError *error;
  143. if (![[NSWorkspace sharedWorkspace] launchApplicationAtURL:self.browserUrl options:NSWorkspaceLaunchNewInstance configuration:configuration error:&error]) {
  144. // This will happen if the Browser was present at Xcode launch and then was deleted
  145. [self showError:error];
  146. }
  147. }
  148. - (NSArray *)realmFilesURLWithDeviceUUID:(NSString *)deviceUUID
  149. {
  150. NSFileManager *fileManager = [NSFileManager defaultManager];
  151. NSURL *homeURL = [NSURL URLWithString:NSHomeDirectory()];
  152. NSMutableString *fullPath = [NSMutableString string];
  153. [fullPath appendFormat:@"%@/%@/%@", RootDeviceSimulatorPath, deviceUUID, DeviceSimulatorApplicationPath];
  154. NSURL *bootedDeviceDirectoryURL = [homeURL URLByAppendingPathComponent:fullPath];
  155. NSArray* fileURLs = RLMPGlobFilesAtDirectoryURLWithPredicate(fileManager, bootedDeviceDirectoryURL, [NSPredicate predicateWithFormat:@"pathExtension == 'realm'"], ^BOOL(NSURL *URL, NSError *error) {
  156. if (error) {
  157. NSLog(@"%@", error);
  158. return NO;
  159. }
  160. return YES;
  161. });
  162. return fileURLs;
  163. }
  164. - (void)showError:(NSError *)error
  165. {
  166. NSAlert *alert = [NSAlert alertWithError:error];
  167. [alert runModal];
  168. }
  169. - (void)dealloc
  170. {
  171. [[NSNotificationCenter defaultCenter] removeObserver:self];
  172. }
  173. @end