Files
NativeScript/nativescript-core/file-system/file-system-access.ios.ts
Alexander Vakrilov cc97a16800 feat: Scoped Packages (#7911)
* chore: move tns-core-modules to nativescript-core

* chore: preparing compat generate script

* chore: add missing definitions

* chore: no need for http-request to be private

* chore: packages chore

* test: generate tests for tns-core-modules

* chore: add anroid module for consistency

* chore: add .npmignore

* chore: added privateModulesWhitelist

* chore(webpack): added bundle-entry-points

* chore: scripts

* chore: tests changed to use @ns/core

* test: add scoped-packages test project

* test: fix types

* test: update test project

* chore: build scripts

* chore: update build script

* chore: npm scripts cleanup

* chore: make the compat pgk work with old wp config

* test: generate diff friendly tests

* chore: create barrel exports

* chore: move files after rebase

* chore: typedoc config

* chore: compat mode

* chore: review of barrels

* chore: remove tns-core-modules import after rebase

* chore: dev workflow setup

* chore: update developer-workflow

* docs: experiment with API extractor

* chore: api-extractor and barrel exports

* chore: api-extractor configs

* chore: generate d.ts rollup with api-extractor

* refactor: move methods inside Frame

* chore: fic tests to use Frame static methods

* refactor: create Builder class

* refactor: use Builder class in tests

* refactor: include Style in ui barrel

* chore: separate compat build script

* chore: fix tslint errors

* chore: update NATIVESCRIPT_CORE_ARGS

* chore: fix compat pack

* chore: fix ui-test-app build with linked modules

* chore: Application, ApplicationSettings, Connectivity and Http

* chore: export Trace, Profiling and Utils

* refactor: Static create methods for ImageSource

* chore: fix deprecated usages of ImageSource

* chore: move Span and FormattedString to ui

* chore: add events-args and ImageSource to index files

* chore: check for CLI >= 6.2 when building for IOS

* chore: update travis build

* chore: copy Pod file to compat package

* chore: update error msg ui-tests-app

* refactor: Apply suggestions from code review

Co-Authored-By: Martin Yankov <m.i.yankov@gmail.com>

* chore: typings and refs

* chore: add missing d.ts files for public API

* chore: adress code review FB

* chore: update api-report

* chore: dev-workflow for other apps

* chore: api update

* chore: update api-report
2019-10-17 00:45:33 +03:00

488 lines
15 KiB
TypeScript

import { encoding as textEncoding } from "../text";
import { ios } from "../utils/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 {
public getLastModified(path: string): Date {
const fileManager = NSFileManager.defaultManager;
const attributes = fileManager.attributesOfItemAtPathError(path);
if (attributes) {
return attributes.objectForKey("NSFileModificationDate");
} else {
return new Date();
}
}
public getFileSize(path: string): number {
const fileManager = NSFileManager.defaultManager;
const attributes = fileManager.attributesOfItemAtPathError(path);
if (attributes) {
return attributes.objectForKey("NSFileSize");
} else {
return 0;
}
}
public getParent(path: string, onError?: (error: any) => any): { path: string; name: string } {
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;
}
}
public getFile(path: string, onError?: (error: any) => any): { path: string; name: string; extension: string } {
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;
}
}
public getFolder(path: string, onError?: (error: any) => any): { path: string; name: string } {
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;
}
}
public getExistingFolder(path: string, onError?: (error: any) => any): { path: string; name: string } {
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;
}
}
public eachEntity(path: string, onEntity: (file: { path: string; name: string; extension: string }) => any, onError?: (error: any) => any) {
if (!onEntity) {
return;
}
this.enumEntities(path, onEntity, onError);
}
public getEntities(path: string, onError?: (error: any) => any): Array<{ path: string; name: string; extension: string }> {
const fileInfos = new Array<{ path: string; name: string; extension: string }>();
const onEntity = function (entity: { path: string; name: string; extension: string }): boolean {
fileInfos.push(entity);
return true;
};
let errorOccurred;
const localError = function (error: any) {
if (onError) {
onError(error);
}
errorOccurred = true;
};
this.enumEntities(path, onEntity, localError);
if (!errorOccurred) {
return fileInfos;
}
return null;
}
public fileExists(path: string): boolean {
const result = this.exists(path);
return result.exists;
}
public folderExists(path: string): boolean {
const result = this.exists(path);
return result.exists && result.isDirectory;
}
private exists(path: string): { exists: boolean, isDirectory: boolean } {
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 };
}
public concatPath(left: string, right: string): string {
return NSString.pathWithComponents(<any>[left, right]).toString();
}
public deleteFile(path: string, onError?: (error: any) => any) {
this.deleteEntity(path, onError);
}
public deleteFolder(path: string, onError?: (error: any) => any) {
this.deleteEntity(path, onError);
}
public emptyFolder(path: string, onError?: (error: any) => any) {
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;
}
}
}
public rename(path: string, newPath: string, onError?: (error: any) => any) {
const fileManager = NSFileManager.defaultManager;
try {
fileManager.moveItemAtPathToPathError(path, newPath);
} catch (ex) {
if (onError) {
onError(new Error("Failed to rename '" + path + "' to '" + newPath + "': " + ex));
}
}
}
public getLogicalRootPath(): string {
const mainBundlePath = NSBundle.mainBundle.bundlePath;
const resolvedPath = NSString.stringWithString(mainBundlePath).stringByResolvingSymlinksInPath;
return resolvedPath;
}
public getDocumentsFolderPath(): string {
return this.getKnownPath(NSSearchPathDirectory.DocumentDirectory);
}
public getTempFolderPath(): string {
return this.getKnownPath(NSSearchPathDirectory.CachesDirectory);
}
public getCurrentAppPath(): string {
return ios.getCurrentAppPath();
}
public readText = this.readTextSync.bind(this);
public readTextAsync(path: string, encoding?: any) {
const actualEncoding = encoding || textEncoding.UTF_8;
return new Promise<string>((resolve, reject) => {
try {
(NSString as any).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));
}
});
}
public readTextSync(path: string, onError?: (error: any) => any, encoding?: any) {
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));
}
}
}
public read = this.readSync.bind(this);
public readAsync(path: string): Promise<NSData> {
return new Promise<NSData>((resolve, reject) => {
try {
(NSData as any).dataWithContentsOfFileCompletion(path, resolve);
} catch (ex) {
reject(new Error("Failed to read file at path '" + path + "': " + ex));
}
});
}
public readSync(path: string, onError?: (error: any) => any): NSData {
try {
return NSData.dataWithContentsOfFile(path);
} catch (ex) {
if (onError) {
onError(new Error("Failed to read file at path '" + path + "': " + ex));
}
}
}
public writeText = this.writeTextSync.bind(this);
public writeTextAsync(path: string, content: string, encoding?: any): Promise<void> {
const nsString = NSString.stringWithString(content);
const actualEncoding = encoding || textEncoding.UTF_8;
return new Promise<void>((resolve, reject) => {
try {
(nsString as any).writeToFileAtomicallyEncodingCompletion(
path,
true,
actualEncoding,
(error) => {
if (error) {
reject(error);
} else {
resolve();
}
},
);
} catch (ex) {
reject(new Error("Failed to write file at path '" + path + "': " + ex));
}
});
}
public writeTextSync(path: string, content: string, onError?: (error: any) => any, encoding?: any) {
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));
}
}
}
public write = this.writeSync.bind(this);
public writeAsync(path: string, content: NSData): Promise<void> {
return new Promise<void>((resolve, reject) => {
try {
(content as any).writeToFileAtomicallyCompletion(
path,
true,
() => { resolve(); },
);
} catch (ex) {
reject(new Error("Failed to write file at path '" + path + "': " + ex));
}
});
}
public writeSync(path: string, content: NSData, onError?: (error: any) => any) {
try {
content.writeToFileAtomically(path, true);
} catch (ex) {
if (onError) {
onError(new Error("Failed to write to file '" + path + "': " + ex));
}
}
}
private getKnownPath(folderType: number): string {
const fileManager = NSFileManager.defaultManager;
const paths = fileManager.URLsForDirectoryInDomains(folderType, 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.
private getFileExtension(path: string): string {
// 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 "";
}
private deleteEntity(path: string, onError?: (error: any) => any) {
const fileManager = NSFileManager.defaultManager;
try {
fileManager.removeItemAtPathError(path);
} catch (ex) {
if (onError) {
onError(new Error("Failed to delete file at path '" + path + "': " + ex));
}
}
}
private enumEntities(path: string, callback: (entity: { path: string; name: string; extension: string }) => boolean, onError?: (error) => any) {
try {
const fileManager = NSFileManager.defaultManager;
let files: NSArray<string>;
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);
}
}
}
public getPathSeparator(): string {
return "/";
}
public normalizePath(path: string): string {
const nsString: NSString = NSString.stringWithString(path);
const normalized = nsString.stringByStandardizingPath;
return normalized;
}
public joinPath(left: string, right: string): string {
const nsString: NSString = NSString.stringWithString(left);
return nsString.stringByAppendingPathComponent(right);
}
public joinPaths(paths: string[]): string {
return ios.joinPaths(...paths);
}
}