@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
JavaScript
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