-1

I have an NativeScript 6.8 Javascript app that downloads newer data files. I'm discovering that on iOS I cannot create files within the app folder. (At least, in release builds; in debug builds I can.) I can change my code to read data files from the Documents folder, but how can I pre-populate the Documents folder at build time with the original data files? I'd rather not copy all the data files at run time.

Or, have I misinterpreted the restriction that files cannot be created in the app folder (or subfolders) in iOS release builds?

David
  • 578
  • 3
  • 16
  • When your app is installed on iOS, your bundle is copied to the device. If you have any files that you need to be outside the bundle (such as files in the documents directory that you want to start with some content but be updated later) then your app needs to create those files from files stored in your bundle the first time it runs. – Paulw11 Nov 17 '21 at 23:27
  • @Paulw11 - yes, that's the conclusion I've come to. I'll post the function I'm developing when I have it complete. – David Nov 19 '21 at 00:20

1 Answers1

0

Live-updating files on iOS is more involved than one might expect. So, yes, you need to access the live-updated files from the Documents folder, not back the files up to iCloud, and handle numerous timing conditions, such as the live-update running just before what would seem to be the initial copy of the file to the Documents folder (seems unlikely, but I've seen it happen while testing).

I've included the function I developed, below. For context, when I find a file online to be live-updated, I use an appSetting to save the file's date as a string (storing as a value loses precision).

The function isn't perfect, but for now, it gets the job done. I call this from app.js in a NativeScript 6.8 JavaScript project.

/**
* Copy files in app/files folder to Documents folder so they can be updated 
*/
async function copyFilesToDocuments() {

  let filesCopied = appSettings.getBoolean("copyFilesToDocuments", false);

  if (!filesCopied) { // only copy files on first invocation

    let filesFolder = fs.knownFolders.currentApp().getFolder("files"); // Folder object 
    let documentsFolder = fs.knownFolders.documents(); // Folder object 
  
    let fileEntities = await filesFolder.getEntities();
  
    for (entity of fileEntities) {
      let sourceFile = fs.File.fromPath(entity.path);
      let targetFilePath = fs.path.join(documentsFolder.path, entity.name);
      let targetDate = parseInt(appSettings.getString(entity.name, "0"), 10); // live-update date or 0 

      if (fs.Folder.exists(targetFilePath) && targetDate > global.dataDate ) { // if file has been live-updated 
        console.log("app.js copyFilesToDocuments: file '" + entity.name + "' skipped to avoid overwrite. ");
        continue; // don't overwrite newer file 
      }

      appSettings.remove(entity.name); // remove any live-update timestamp
      let targetFile = fs.File.fromPath(targetFilePath);
      let content = await sourceFile.read();
      try {
        await targetFile.write(content);
        if (platform.isIOS) {
          // Prevent file from being backed up to iCloud
          // See https://stackoverflow.com/questions/58363089/using-nsurlisexcludedfrombackupkey-in-nativescript
          // See https://stackoverflow.com/questions/26080120/cfurlcopyresourcepropertyforkey-failed-because-passed-url-no-scheme
          NSURL.fileURLWithPath(targetFilePath).setResourceValueForKeyError(true, NSURLIsExcludedFromBackupKey);
        }
        // console.log("app.js copyFilesToDocuments file copied: " + entity.name);
      } catch(e) {
        console.warn("app.js copyFilesToDocuments error: " + e);
        //TODO:  app will fail at this point with some files not found :-(
      } // end catch
    } // end for
    appSettings.setBoolean("copyFilesToDocuments", true);
  } // end files not yet copied
} // end copyFilesToDocuments
David
  • 578
  • 3
  • 16