UNPKG

@nativescript/core

Version:

A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.

635 lines • 22.4 kB
import { encoding as textEncoding } from '../text'; import { iOSNativeHelper } from '../utils'; // TODO: Implement all the APIs receiving callback using async blocks // TODO: Check whether we need try/catch blocks for the iOS implementation export class FileSystemAccess { constructor() { this.copy = this.copySync.bind(this); this.readText = this.readTextSync.bind(this); this.readBuffer = this.readBufferSync.bind(this); this.read = this.readSync.bind(this); this.writeText = this.writeTextSync.bind(this); this.appendBuffer = this.appendBufferSync.bind(this); this.append = this.appendSync.bind(this); this.appendText = this.appendTextSync.bind(this); this.writeBuffer = this.writeBufferSync.bind(this); this.write = this.writeSync.bind(this); } getLastModified(path) { const fileManager = NSFileManager.defaultManager; const attributes = fileManager.attributesOfItemAtPathError(path); if (attributes) { return attributes.objectForKey('NSFileModificationDate'); } else { return new Date(); } } getFileSize(path) { const fileManager = NSFileManager.defaultManager; const attributes = fileManager.attributesOfItemAtPathError(path); if (attributes) { return attributes.objectForKey('NSFileSize'); } else { return 0; } } getParent(path, onError) { try { const fileManager = NSFileManager.defaultManager; const nsString = NSString.stringWithString(path); const parentPath = nsString.stringByDeletingLastPathComponent; const name = fileManager.displayNameAtPath(parentPath); return { path: parentPath.toString(), name: name, }; } catch (exception) { if (onError) { onError(exception); } return undefined; } } getFile(path, onError) { try { const fileManager = NSFileManager.defaultManager; const exists = fileManager.fileExistsAtPath(path); if (!exists) { const parentPath = this.getParent(path, onError).path; if (!fileManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(parentPath, true, null) || !fileManager.createFileAtPathContentsAttributes(path, null, null)) { if (onError) { onError(new Error("Failed to create file at path '" + path + "'")); } return undefined; } } const fileName = fileManager.displayNameAtPath(path); return { path: path, name: fileName, extension: this.getFileExtension(path), }; } catch (exception) { if (onError) { onError(exception); } return undefined; } } getFolder(path, onError) { try { const fileManager = NSFileManager.defaultManager; const exists = this.folderExists(path); if (!exists) { try { fileManager.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(path, true, null); } catch (ex) { if (onError) { onError(new Error("Failed to create folder at path '" + path + "': " + ex)); } return undefined; } } const dirName = fileManager.displayNameAtPath(path); return { path: path, name: dirName, }; } catch (ex) { if (onError) { onError(new Error("Failed to create folder at path '" + path + "'")); } return undefined; } } getExistingFolder(path, onError) { try { const fileManager = NSFileManager.defaultManager; const exists = this.folderExists(path); if (exists) { const dirName = fileManager.displayNameAtPath(path); return { path: path, name: dirName, }; } return undefined; } catch (ex) { if (onError) { onError(new Error("Failed to get folder at path '" + path + "'")); } return undefined; } } eachEntity(path, onEntity, onError) { if (!onEntity) { return; } this.enumEntities(path, onEntity, onError); } getEntities(path, onError) { const fileInfos = new Array(); const onEntity = function (entity) { fileInfos.push(entity); return true; }; let errorOccurred; const localError = function (error) { if (onError) { onError(error); } errorOccurred = true; }; this.enumEntities(path, onEntity, localError); if (!errorOccurred) { return fileInfos; } return null; } fileExists(path) { const result = this.exists(path); return result.exists; } folderExists(path) { const result = this.exists(path); return result.exists && result.isDirectory; } exists(path) { const fileManager = NSFileManager.defaultManager; const isDirectory = new interop.Reference(interop.types.bool, false); const exists = fileManager.fileExistsAtPathIsDirectory(path, isDirectory); return { exists: exists, isDirectory: isDirectory.value }; } concatPath(left, right) { return NSString.pathWithComponents([left, right]).toString(); } deleteFile(path, onError) { this.deleteEntity(path, onError); } deleteFolder(path, onError) { this.deleteEntity(path, onError); } emptyFolder(path, onError) { const fileManager = NSFileManager.defaultManager; const entities = this.getEntities(path, onError); if (!entities) { return; } for (let i = 0; i < entities.length; i++) { try { fileManager.removeItemAtPathError(entities[i].path); } catch (ex) { if (onError) { onError(new Error("Failed to empty folder '" + path + "': " + ex)); } return; } } } rename(path, newPath, onError) { const fileManager = NSFileManager.defaultManager; try { fileManager.moveItemAtPathToPathError(path, newPath); } catch (ex) { if (onError) { onError(new Error("Failed to rename '" + path + "' to '" + newPath + "': " + ex)); } } } getLogicalRootPath() { const mainBundlePath = NSBundle.mainBundle.bundlePath; const resolvedPath = NSString.stringWithString(mainBundlePath).stringByResolvingSymlinksInPath; return resolvedPath; } getDocumentsFolderPath() { return this.getKnownPath(9 /* NSSearchPathDirectory.DocumentDirectory */); } getExternalDocumentsFolderPath() { return this.getDocumentsFolderPath(); } getTempFolderPath() { return this.getKnownPath(13 /* NSSearchPathDirectory.CachesDirectory */); } getCurrentAppPath() { return iOSNativeHelper.getCurrentAppPath(); } copySync(src, dest, onError) { const fileManager = NSFileManager.defaultManager; try { return fileManager.copyItemAtPathToPathError(src, dest); } catch (error) { if (error.message.indexOf('exists') > -1) { // check the size of file if empty remove then try copying again // this could be zero due to using File.fromPath passing in a new file let didRemove = false; try { didRemove = fileManager.removeItemAtPathError(dest); return fileManager.copyItemAtPathToPathError(src, dest); } catch (exception) { if (onError) { if (didRemove) { onError(error); } else { onError(exception); } } } } if (onError) { onError(error); } } return false; } copyAsync(src, dest) { return new Promise((resolve, reject) => { try { NSData.dataWithContentsOfFileCompletion(src, (data) => { if (!data) { reject(new Error("Failed to read file at path '" + src)); } else { data.writeToFileAtomicallyCompletion(dest, true, () => { if (this.fileExists(dest)) { const size = this.getFileSize(dest); if (size === data.length) { resolve(true); } else { reject(new Error("Failed to write file at path '" + dest)); } } else { reject(new Error("Failed to write file at path '" + dest)); } }); } }); } catch (ex) { reject(ex); } }); } readTextAsync(path, encoding) { const actualEncoding = encoding || textEncoding.UTF_8; return new Promise((resolve, reject) => { try { NSString.stringWithContentsOfFileEncodingCompletion(path, actualEncoding, (result, error) => { if (error) { reject(error); } else { resolve(result.toString()); } }); } catch (ex) { reject(new Error("Failed to read file at path '" + path + "': " + ex)); } }); } readTextSync(path, onError, encoding) { const actualEncoding = encoding || textEncoding.UTF_8; try { const nsString = NSString.stringWithContentsOfFileEncodingError(path, actualEncoding); return nsString.toString(); } catch (ex) { if (onError) { onError(new Error("Failed to read file at path '" + path + "': " + ex)); } } } readBufferAsync(path) { return new Promise((resolve, reject) => { try { NSData.dataWithContentsOfFileCompletion(path, (data) => { resolve(interop.bufferFromData(data)); }); } catch (ex) { reject(new Error("Failed to read file at path '" + path + "': " + ex)); } }); } readBufferSync(path, onError) { try { return interop.bufferFromData(NSData.dataWithContentsOfFile(path)); } catch (ex) { if (onError) { onError(new Error("Failed to read file at path '" + path + "': " + ex)); } } } readAsync(path) { return new Promise((resolve, reject) => { try { NSData.dataWithContentsOfFileCompletion(path, resolve); } catch (ex) { reject(new Error("Failed to read file at path '" + path + "': " + ex)); } }); } readSync(path, onError) { try { return NSData.dataWithContentsOfFile(path); } catch (ex) { if (onError) { onError(new Error("Failed to read file at path '" + path + "': " + ex)); } } } writeTextAsync(path, content, encoding) { const nsString = NSString.stringWithString(content); const actualEncoding = encoding || textEncoding.UTF_8; return new Promise((resolve, reject) => { try { nsString.writeToFileAtomicallyEncodingCompletion(path, true, actualEncoding, (error) => { if (error) { reject(error); } else { resolve(); } }); } catch (ex) { reject(new Error("Failed to write file at path '" + path + "': " + ex)); } }); } writeTextSync(path, content, onError, encoding) { const nsString = NSString.stringWithString(content); const actualEncoding = encoding || textEncoding.UTF_8; // TODO: verify the useAuxiliaryFile parameter should be false try { nsString.writeToFileAtomicallyEncodingError(path, false, actualEncoding); } catch (ex) { if (onError) { onError(new Error("Failed to write to file '" + path + "': " + ex)); } } } static getBuffer(buffer) { if (buffer instanceof ArrayBuffer) { return NSData.dataWithData(buffer); } else { const buf = NSData.dataWithData(buffer?.buffer); const len = buffer.byteLength; return NSData.dataWithBytesNoCopyLength(buf.bytes.add(buffer?.byteOffset ?? 0), len); } } appendBufferAsync(path, content) { return new Promise((resolve, reject) => { try { const handle = NSFileHandle.fileHandleForWritingAtPath(path); handle.appendDataCompletion(FileSystemAccess.getBuffer(content), (error) => { if (error) { reject(error); } else { resolve(); } handle.closeFile(); }); } catch (ex) { reject(new Error("Failed to write file at path '" + path + "': " + ex)); } }); } appendBufferSync(path, content, onError) { try { const handle = NSFileHandle.fileHandleForWritingAtPath(path); handle.seekToEndOfFile(); handle.writeData(FileSystemAccess.getBuffer(content)); handle.closeFile(); } catch (ex) { if (onError) { onError(new Error("Failed to write to file '" + path + "': " + ex)); } } } appendAsync(path, content) { return new Promise((resolve, reject) => { try { const handle = NSFileHandle.fileHandleForWritingAtPath(path); handle.appendDataCompletion(content, (error) => { if (error) { reject(error); } else { resolve(); } handle.closeFile(); }); } catch (ex) { reject(new Error("Failed to write file at path '" + path + "': " + ex)); } }); } appendSync(path, content, onError) { try { const handle = NSFileHandle.fileHandleForWritingAtPath(path); handle.seekToEndOfFile(); handle.writeData(content); handle.closeFile(); } catch (ex) { if (onError) { onError(new Error("Failed to write to file '" + path + "': " + ex)); } } } appendTextAsync(path, content, encoding) { const nsString = NSString.stringWithString(content); const actualEncoding = encoding || textEncoding.UTF_8; return new Promise((resolve, reject) => { try { const data = nsString.dataUsingEncoding(actualEncoding); const handle = NSFileHandle.fileHandleForWritingAtPath(path); handle.appendDataCompletion(data, (error) => { if (error) { reject(error); } else { resolve(); } handle.closeFile(); }); } catch (ex) { reject(new Error("Failed to append file at path '" + path + "': " + ex)); } }); } appendTextSync(path, content, onError, encoding) { const nsString = NSString.stringWithString(content); const actualEncoding = encoding || textEncoding.UTF_8; // TODO: verify the useAuxiliaryFile parameter should be false try { const data = nsString.dataUsingEncoding(actualEncoding); const handle = NSFileHandle.fileHandleForWritingAtPath(path); handle.seekToEndOfFile(); handle.writeData(data); handle.closeFile(); } catch (ex) { if (onError) { onError(new Error("Failed to append to file '" + path + "': " + ex)); } } } writeBufferAsync(path, content) { return new Promise((resolve, reject) => { try { FileSystemAccess.getBuffer(content).writeToFileAtomicallyCompletion(path, true, () => { resolve(); }); } catch (ex) { reject(new Error("Failed to write file at path '" + path + "': " + ex)); } }); } writeBufferSync(path, content, onError) { try { FileSystemAccess.getBuffer(content).writeToFileAtomically(path, true); } catch (ex) { if (onError) { onError(new Error("Failed to write to file '" + path + "': " + ex)); } } } writeAsync(path, content) { return new Promise((resolve, reject) => { try { content.writeToFileAtomicallyCompletion(path, true, () => { resolve(); }); } catch (ex) { reject(new Error("Failed to write file at path '" + path + "': " + ex)); } }); } writeSync(path, content, onError) { try { content.writeToFileAtomically(path, true); } catch (ex) { if (onError) { onError(new Error("Failed to write to file '" + path + "': " + ex)); } } } getKnownPath(folderType) { const fileManager = NSFileManager.defaultManager; const paths = fileManager.URLsForDirectoryInDomains(folderType, 1 /* NSSearchPathDomainMask.UserDomainMask */); const url = paths.objectAtIndex(0); return url.path; } // TODO: This method is the same as in the iOS implementation. // Make it in a separate file / module so it can be reused from both implementations. getFileExtension(path) { // TODO [For Panata]: The definitions currently specify "any" as a return value of this method //const nsString = Foundation.NSString.stringWithString(path); //const extension = nsString.pathExtension(); //if (extension && extension.length > 0) { // extension = extension.concat(".", extension); //} //return extension; const dotIndex = path.lastIndexOf('.'); if (dotIndex && dotIndex >= 0 && dotIndex < path.length) { return path.substring(dotIndex); } return ''; } deleteEntity(path, onError) { const fileManager = NSFileManager.defaultManager; try { fileManager.removeItemAtPathError(path); } catch (ex) { if (onError) { onError(new Error("Failed to delete file at path '" + path + "': " + ex)); } } } enumEntities(path, callback, onError) { try { const fileManager = NSFileManager.defaultManager; let files; try { files = fileManager.contentsOfDirectoryAtPathError(path); } catch (ex) { if (onError) { onError(new Error("Failed to enum files for folder '" + path + "': " + ex)); } return; } for (let i = 0; i < files.count; i++) { const file = files.objectAtIndex(i); const info = { path: this.concatPath(path, file), name: file, extension: '', }; if (!this.folderExists(this.joinPath(path, file))) { info.extension = this.getFileExtension(info.path); } const retVal = callback(info); if (retVal === false) { // the callback returned false meaning we should stop the iteration break; } } } catch (ex) { if (onError) { onError(ex); } } } getPathSeparator() { return '/'; } normalizePath(path) { const nsString = NSString.stringWithString(path); const normalized = nsString.stringByStandardizingPath; return normalized; } joinPath(left, right) { const nsString = NSString.stringWithString(left); return nsString.stringByAppendingPathComponent(right); } joinPaths(paths) { return iOSNativeHelper.joinPaths(...paths); } } // stub to avoid cross platform warning export class FileSystemAccess29 extends FileSystemAccess { } //# sourceMappingURL=file-system-access.ios.js.map