mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
replace swift logic with objc
This commit is contained in:
@@ -1,54 +1,8 @@
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
fn main() {
|
fn main() {
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
|
|
||||||
let out_dir = std::env::var("OUT_DIR").expect("env var OUT_DIR is invalid or not set");
|
// Compile Objective-C files
|
||||||
|
|
||||||
// Compile Swift files FIRST (generates Bitwarden-Swift.h for browser_access.m)
|
|
||||||
let swift_files: Vec<String> = glob("src/native/**/*.swift")
|
|
||||||
.expect("Failed to read Swift glob pattern")
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.filter_map(|p| {
|
|
||||||
println!("cargo::rerun-if-changed={}", p.display());
|
|
||||||
p.to_str().map(|s| s.to_string())
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !swift_files.is_empty() {
|
|
||||||
// Compile Swift into a static library
|
|
||||||
let status = Command::new("swiftc")
|
|
||||||
.args([
|
|
||||||
"-emit-library",
|
|
||||||
"-static",
|
|
||||||
"-module-name",
|
|
||||||
"Bitwarden",
|
|
||||||
"-import-objc-header",
|
|
||||||
"src/native/bridging-header.h",
|
|
||||||
"-emit-objc-header-path",
|
|
||||||
&format!("{}/Bitwarden-Swift.h", out_dir),
|
|
||||||
"-o",
|
|
||||||
&format!("{}/libbitwarden_swift.a", out_dir),
|
|
||||||
])
|
|
||||||
.args(&swift_files)
|
|
||||||
.status()
|
|
||||||
.expect("Failed to compile Swift code");
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
panic!("Swift compilation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell cargo to link the Swift library
|
|
||||||
println!("cargo:rustc-link-search=native={}", out_dir);
|
|
||||||
println!("cargo:rustc-link-lib=static=bitwarden_swift");
|
|
||||||
|
|
||||||
// Link required Swift/Foundation frameworks
|
|
||||||
println!("cargo:rustc-link-lib=framework=Foundation");
|
|
||||||
println!("cargo:rustc-link-lib=framework=AppKit");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile Objective-C files (Bitwarden-Swift.h exists now)
|
|
||||||
let mut builder = cc::Build::new();
|
let mut builder = cc::Build::new();
|
||||||
|
|
||||||
// Compile all .m files in the src/native directory
|
// Compile all .m files in the src/native directory
|
||||||
@@ -59,9 +13,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.include(&out_dir) // Add OUT_DIR to include path so Bitwarden-Swift.h can be found
|
|
||||||
.flag("-fobjc-arc") // Enable Auto Reference Counting (ARC)
|
.flag("-fobjc-arc") // Enable Auto Reference Counting (ARC)
|
||||||
.compile("objc_code");
|
.compile("objc_code");
|
||||||
|
|
||||||
|
// Link required frameworks
|
||||||
|
println!("cargo:rustc-link-lib=framework=Foundation");
|
||||||
|
println!("cargo:rustc-link-lib=framework=AppKit");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface BrowserAccessManager : NSObject
|
||||||
|
|
||||||
|
- (instancetype)init;
|
||||||
|
|
||||||
|
/// Request access to a specific browser's directory
|
||||||
|
/// Returns security bookmark data (used to persist permissions) as base64 string, or nil if user declined
|
||||||
|
- (nullable NSString *)requestAccessToBrowserDir:(NSString *)browserName;
|
||||||
|
|
||||||
|
/// Check if we have stored bookmark for browser (doesn't verify it's still valid)
|
||||||
|
- (BOOL)hasStoredAccess:(NSString *)browserName;
|
||||||
|
|
||||||
|
/// Start accessing a browser directory using stored bookmark
|
||||||
|
/// Returns the resolved path, or nil if bookmark is invalid/revoked
|
||||||
|
- (nullable NSString *)startAccessingBrowser:(NSString *)browserName;
|
||||||
|
|
||||||
|
/// Stop accessing a browser directory (must be called after startAccessingBrowser)
|
||||||
|
- (void)stopAccessingBrowser:(NSString *)browserName;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
#import "BrowserAccessManager.h"
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@implementation BrowserAccessManager {
|
||||||
|
NSString *_bookmarkKey;
|
||||||
|
NSDictionary<NSString *, NSString *> *_browserPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_bookmarkKey = @"com.bitwarden.chromiumImporter.bookmarks";
|
||||||
|
|
||||||
|
_browserPaths = @{
|
||||||
|
@"Chrome": @"Library/Application Support/Google/Chrome",
|
||||||
|
@"Chromium": @"Library/Application Support/Chromium",
|
||||||
|
@"Microsoft Edge": @"Library/Application Support/Microsoft Edge",
|
||||||
|
@"Brave": @"Library/Application Support/BraveSoftware/Brave-Browser",
|
||||||
|
@"Arc": @"Library/Application Support/Arc/User Data",
|
||||||
|
@"Opera": @"Library/Application Support/com.operasoftware.Opera",
|
||||||
|
@"Vivaldi": @"Library/Application Support/Vivaldi"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)requestAccessToBrowserDir:(NSString *)browserName {
|
||||||
|
// NSLog(@"[OBJC] requestAccessToBrowserDir called for: %@", browserName);
|
||||||
|
|
||||||
|
NSString *relativePath = _browserPaths[browserName];
|
||||||
|
if (!relativePath) {
|
||||||
|
// NSLog(@"[OBJC] Unknown browser: %@", browserName);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *homeDir = [[NSFileManager defaultManager] homeDirectoryForCurrentUser];
|
||||||
|
NSURL *browserPath = [homeDir URLByAppendingPathComponent:relativePath];
|
||||||
|
|
||||||
|
// NSLog(@"[OBJC] Browser path: %@", browserPath.path);
|
||||||
|
|
||||||
|
// NSOpenPanel must be run on the main thread
|
||||||
|
__block NSURL *selectedURL = nil;
|
||||||
|
__block NSModalResponse panelResult = NSModalResponseCancel;
|
||||||
|
|
||||||
|
void (^showPanel)(void) = ^{
|
||||||
|
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||||
|
openPanel.message = [NSString stringWithFormat:
|
||||||
|
@"Please select your %@ data folder\n\nExpected location:\n%@",
|
||||||
|
browserName, browserPath.path];
|
||||||
|
openPanel.prompt = @"Grant Access";
|
||||||
|
openPanel.allowsMultipleSelection = NO;
|
||||||
|
openPanel.canChooseDirectories = YES;
|
||||||
|
openPanel.canChooseFiles = NO;
|
||||||
|
openPanel.directoryURL = browserPath;
|
||||||
|
|
||||||
|
// NSLog(@"[OBJC] About to call runModal");
|
||||||
|
panelResult = [openPanel runModal];
|
||||||
|
selectedURL = openPanel.URL;
|
||||||
|
// NSLog(@"[OBJC] runModal returned: %ld", (long)panelResult);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ([NSThread isMainThread]) {
|
||||||
|
// NSLog(@"[OBJC] Already on main thread");
|
||||||
|
showPanel();
|
||||||
|
} else {
|
||||||
|
// NSLog(@"[OBJC] Dispatching to main queue...");
|
||||||
|
dispatch_sync(dispatch_get_main_queue(), showPanel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (panelResult != NSModalResponseOK || !selectedURL) {
|
||||||
|
// NSLog(@"[OBJC] User cancelled access request or panel failed");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NSLog(@"[OBJC] User selected URL: %@", selectedURL.path);
|
||||||
|
|
||||||
|
NSURL *localStatePath = [selectedURL URLByAppendingPathComponent:@"Local State"];
|
||||||
|
if (![[NSFileManager defaultManager] fileExistsAtPath:localStatePath.path]) {
|
||||||
|
// NSLog(@"[OBJC] Selected folder doesn't appear to be a valid %@ directory", browserName);
|
||||||
|
|
||||||
|
NSAlert *alert = [[NSAlert alloc] init];
|
||||||
|
alert.messageText = @"Invalid Folder";
|
||||||
|
alert.informativeText = [NSString stringWithFormat:
|
||||||
|
@"The selected folder doesn't appear to be a valid %@ data directory. Please select the correct folder.",
|
||||||
|
browserName];
|
||||||
|
alert.alertStyle = NSAlertStyleWarning;
|
||||||
|
[alert runModal];
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access is temporary right now, persist it by creating a security bookmark
|
||||||
|
NSError *error = nil;
|
||||||
|
NSData *bookmarkData = [selectedURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
|
||||||
|
includingResourceValuesForKeys:nil
|
||||||
|
relativeToURL:nil
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
if (!bookmarkData) {
|
||||||
|
// NSLog(@"[OBJC] Failed to create bookmark: %@", error);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self saveBookmark:bookmarkData forBrowser:browserName];
|
||||||
|
// NSLog(@"[OBJC] Successfully created and saved bookmark");
|
||||||
|
return [bookmarkData base64EncodedStringWithOptions:0];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasStoredAccess:(NSString *)browserName {
|
||||||
|
return [self loadBookmarkForBrowser:browserName] != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)startAccessingBrowser:(NSString *)browserName {
|
||||||
|
NSData *bookmarkData = [self loadBookmarkForBrowser:browserName];
|
||||||
|
if (!bookmarkData) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isStale = NO;
|
||||||
|
NSError *error = nil;
|
||||||
|
NSURL *url = [NSURL URLByResolvingBookmarkData:bookmarkData
|
||||||
|
options:NSURLBookmarkResolutionWithSecurityScope
|
||||||
|
relativeToURL:nil
|
||||||
|
bookmarkDataIsStale:&isStale
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
// NSLog(@"Failed to resolve bookmark: %@", error);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStale) {
|
||||||
|
// NSLog(@"Security bookmark for %@ is stale, attempting to re-create it", browserName);
|
||||||
|
NSData *newBookmarkData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
|
||||||
|
includingResourceValuesForKeys:nil
|
||||||
|
relativeToURL:nil
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
if (!newBookmarkData) {
|
||||||
|
// NSLog(@"Failed to create bookmark: %@", error);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self saveBookmark:newBookmarkData forBrowser:browserName];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![url startAccessingSecurityScopedResource]) {
|
||||||
|
// NSLog(@"Failed to start accessing security-scoped resource");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stopAccessingBrowser:(NSString *)browserName {
|
||||||
|
NSData *bookmarkData = [self loadBookmarkForBrowser:browserName];
|
||||||
|
if (!bookmarkData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isStale = NO;
|
||||||
|
NSError *error = nil;
|
||||||
|
NSURL *url = [NSURL URLByResolvingBookmarkData:bookmarkData
|
||||||
|
options:NSURLBookmarkResolutionWithSecurityScope
|
||||||
|
relativeToURL:nil
|
||||||
|
bookmarkDataIsStale:&isStale
|
||||||
|
error:&error];
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
// NSLog(@"Failed to resolve bookmark for stop: %@", error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[url stopAccessingSecurityScopedResource];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private Methods
|
||||||
|
|
||||||
|
- (NSString *)bookmarkKeyFor:(NSString *)browserName {
|
||||||
|
return [NSString stringWithFormat:@"%@.%@", _bookmarkKey, browserName];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)saveBookmark:(NSData *)data forBrowser:(NSString *)browserName {
|
||||||
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
NSString *key = [self bookmarkKeyFor:browserName];
|
||||||
|
[defaults setObject:data forKey:key];
|
||||||
|
[defaults synchronize];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSData *)loadBookmarkForBrowser:(NSString *)browserName {
|
||||||
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
NSString *key = [self bookmarkKeyFor:browserName];
|
||||||
|
return [defaults dataForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#import "browser_access.h"
|
#import "browser_access.h"
|
||||||
#import "../utils.h"
|
#import "../utils.h"
|
||||||
|
|
||||||
#import "Bitwarden-Swift.h"
|
#import "BrowserAccessManager.h"
|
||||||
|
|
||||||
static BrowserAccessManager* sharedManager = nil;
|
static BrowserAccessManager* sharedManager = nil;
|
||||||
|
|
||||||
@@ -17,8 +17,7 @@ static BrowserAccessManager* getManager() {
|
|||||||
char* requestBrowserAccess(const char* browserName) {
|
char* requestBrowserAccess(const char* browserName) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSString* name = [NSString stringWithUTF8String:browserName];
|
NSString* name = [NSString stringWithUTF8String:browserName];
|
||||||
// Note: Matches the Swift method name with typo "Broswer"
|
NSString* result = [getManager() requestAccessToBrowserDir:name];
|
||||||
NSString* result = [getManager() requestAccessToBroswerDir:name];
|
|
||||||
|
|
||||||
if (result == nil) {
|
if (result == nil) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
import Cocoa
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@objc public class BrowserAccessManager: NSObject {
|
|
||||||
private let bookmarkKey = "com.bitwarden.chromiumImporter.bookmarks"
|
|
||||||
|
|
||||||
private let browserPaths: [String: String] = [
|
|
||||||
"Chrome": "Library/Application Support/Google/Chrome",
|
|
||||||
"Chromium": "Library/Application Support/Chromium",
|
|
||||||
"Microsoft Edge": "Library/Application Support/Microsoft Edge",
|
|
||||||
"Brave": "Library/Application Support/BraveSoftware/Brave-Browser",
|
|
||||||
"Arc": "Library/Application Support/Arc/User Data",
|
|
||||||
"Opera": "Library/Application Support/com.operasoftware.Opera",
|
|
||||||
"Vivaldi": "Library/Application Support/Vivaldi",
|
|
||||||
]
|
|
||||||
|
|
||||||
/// Request access to a specific browser's directory
|
|
||||||
/// Returns security bookmark data (used to persist permissions) as base64 string, or nil if user declined
|
|
||||||
@objc public func requestAccessToBroswerDir(_ browserName: String) -> String? {
|
|
||||||
// NSLog("[SWIFT] requestAccessToBroswerDir called for: \(browserName)")
|
|
||||||
|
|
||||||
guard let relativePath = browserPaths[browserName] else {
|
|
||||||
// NSLog("[SWIFT] Unknown browser: \(browserName)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let homeDir = FileManager.default.homeDirectoryForCurrentUser
|
|
||||||
let browserPath = homeDir.appendingPathComponent(relativePath)
|
|
||||||
|
|
||||||
// NSLog("[SWIFT] Browser path: \(browserPath.path)")
|
|
||||||
|
|
||||||
// NSOpenPanel must be run on the main thread
|
|
||||||
var selectedURL: URL?
|
|
||||||
var panelResult: NSApplication.ModalResponse = .cancel
|
|
||||||
|
|
||||||
if Thread.isMainThread {
|
|
||||||
// NSLog("[SWIFT] Already on main thread")
|
|
||||||
let openPanel = NSOpenPanel()
|
|
||||||
openPanel.message =
|
|
||||||
"Please select your \(browserName) data folder\n\nExpected location:\n\(browserPath.path)"
|
|
||||||
openPanel.prompt = "Grant Access"
|
|
||||||
openPanel.allowsMultipleSelection = false
|
|
||||||
openPanel.canChooseDirectories = true
|
|
||||||
openPanel.canChooseFiles = false
|
|
||||||
openPanel.directoryURL = browserPath
|
|
||||||
|
|
||||||
// NSLog("[SWIFT] About to call openPanel.runModal()")
|
|
||||||
panelResult = openPanel.runModal()
|
|
||||||
selectedURL = openPanel.url
|
|
||||||
// NSLog("[SWIFT] runModal returned: \(panelResult.rawValue)")
|
|
||||||
} else {
|
|
||||||
// NSLog("[SWIFT] Dispatching to main queue...")
|
|
||||||
DispatchQueue.main.sync {
|
|
||||||
// NSLog("[SWIFT] Inside main queue dispatch block")
|
|
||||||
let openPanel = NSOpenPanel()
|
|
||||||
openPanel.message =
|
|
||||||
"Please select your \(browserName) data folder\n\nExpected location:\n\(browserPath.path)"
|
|
||||||
openPanel.prompt = "Grant Access"
|
|
||||||
openPanel.allowsMultipleSelection = false
|
|
||||||
openPanel.canChooseDirectories = true
|
|
||||||
openPanel.canChooseFiles = false
|
|
||||||
openPanel.directoryURL = browserPath
|
|
||||||
|
|
||||||
// NSLog("[SWIFT] About to call openPanel.runModal()")
|
|
||||||
panelResult = openPanel.runModal()
|
|
||||||
selectedURL = openPanel.url
|
|
||||||
// NSLog("[SWIFT] runModal returned: \(panelResult.rawValue)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard panelResult == .OK, let url = selectedURL else {
|
|
||||||
// NSLog("[SWIFT] User cancelled access request or panel failed")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NSLog("[SWIFT] User selected URL: \(url.path)")
|
|
||||||
|
|
||||||
let localStatePath = url.appendingPathComponent("Local State")
|
|
||||||
guard FileManager.default.fileExists(atPath: localStatePath.path) else {
|
|
||||||
// NSLog("[SWIFT] Selected folder doesn't appear to be a valid \(browserName) directory")
|
|
||||||
|
|
||||||
let alert = NSAlert()
|
|
||||||
alert.messageText = "Invalid Folder"
|
|
||||||
alert.informativeText =
|
|
||||||
"The selected folder doesn't appear to be a valid \(browserName) data directory. Please select the correct folder."
|
|
||||||
alert.alertStyle = .warning
|
|
||||||
alert.runModal()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// access is temporary right now, persist it by creating a security bookmark
|
|
||||||
do {
|
|
||||||
let bookmarkData = try url.bookmarkData(
|
|
||||||
options: .withSecurityScope,
|
|
||||||
includingResourceValuesForKeys: nil,
|
|
||||||
relativeTo: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
saveBookmark(bookmarkData, forBrowser: browserName)
|
|
||||||
// NSLog("[SWIFT] Successfully created and saved bookmark")
|
|
||||||
return bookmarkData.base64EncodedString()
|
|
||||||
} catch {
|
|
||||||
// NSLog("[SWIFT] Failed to create bookmark: \(error)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if we have stored bookmark for browser (doesn't verify it's still valid)
|
|
||||||
@objc public func hasStoredAccess(_ browserName: String) -> Bool {
|
|
||||||
return loadBookmark(forBrowser: browserName) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start accessing a browser directory using stored bookmark
|
|
||||||
/// Returns the resolved path, or nil if bookmark is invalid/revoked
|
|
||||||
@objc public func startAccessingBrowser(_ browserName: String) -> String? {
|
|
||||||
guard let bookmarkData = loadBookmark(forBrowser: browserName) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
var isStale = false
|
|
||||||
let url = try URL(
|
|
||||||
resolvingBookmarkData: bookmarkData,
|
|
||||||
options: .withSecurityScope,
|
|
||||||
relativeTo: nil,
|
|
||||||
bookmarkDataIsStale: &isStale
|
|
||||||
)
|
|
||||||
|
|
||||||
if isStale {
|
|
||||||
// NSLog("Security bookmark for \(browserName) is stale, attempting to re-create it")
|
|
||||||
do {
|
|
||||||
let newBookmarkData = try url.bookmarkData(
|
|
||||||
options: .withSecurityScope,
|
|
||||||
includingResourceValuesForKeys: nil,
|
|
||||||
relativeTo: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
saveBookmark(newBookmarkData, forBrowser: browserName)
|
|
||||||
} catch {
|
|
||||||
// NSLog("Failed to create bookmark: \(error)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard url.startAccessingSecurityScopedResource() else {
|
|
||||||
// NSLog("Failed to start accessing security-scoped resource")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return url.path
|
|
||||||
} catch {
|
|
||||||
// NSLog("Failed to resolve bookmark: \(error)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stop accessing a browser directory (must be called after startAccessingBrowser)
|
|
||||||
@objc public func stopAccessingBrowser(_ browserName: String) {
|
|
||||||
guard let bookmarkData = loadBookmark(forBrowser: browserName) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
var isStale = false
|
|
||||||
let url = try URL(
|
|
||||||
resolvingBookmarkData: bookmarkData,
|
|
||||||
options: .withSecurityScope,
|
|
||||||
relativeTo: nil,
|
|
||||||
bookmarkDataIsStale: &isStale
|
|
||||||
)
|
|
||||||
|
|
||||||
url.stopAccessingSecurityScopedResource()
|
|
||||||
} catch {
|
|
||||||
// NSLog("Failed to resolve bookmark for stop: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func bookmarkKeyFor(_ browserName: String) -> String {
|
|
||||||
return "\(bookmarkKey).\(browserName)"
|
|
||||||
}
|
|
||||||
|
|
||||||
private func saveBookmark(_ data: Data, forBrowser browserName: String) {
|
|
||||||
let defaults = UserDefaults.standard
|
|
||||||
let key = bookmarkKeyFor(browserName)
|
|
||||||
defaults.set(data, forKey: key)
|
|
||||||
defaults.synchronize()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadBookmark(forBrowser browserName: String) -> Data? {
|
|
||||||
let defaults = UserDefaults.standard
|
|
||||||
let key = bookmarkKeyFor(browserName)
|
|
||||||
return defaults.data(forKey: key)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user