My project was started over 15 years ago in REALbasic by a consultant, and he did not use a Resources folder. Perhaps that early IDE didn’t use one. Anyway, my app is now having a problem finding its data folders on some Macs when the parent folder and its contents have been zipped and expanded.
I am currently using Xojo 2016r4.1. Everything is in the parent folder OpVP. It contains, along with other data files, OptimumVP.app and a folder “RequiredFiles” containing several text files. My code looks for that folder as follows:
dim f as FolderItem
f = GetFolderItem( “RequiredFiles” )
if not f.directory then …[error code]
This works fine except on some Mac systems (especially Mojave) when the parent folder and its contents have been zipped (to be sent by email) and expanded. In that case, f.directory is nil.
Should I create a Resources folder?
Should it be next to the app in the OpVP folder?
Should all the data files be in the Resources folder?
Is there anything (other than the app) that should not be in the Resources folder?
What should the code be to locate a folder within the Resources folder?
What should the code be to open a data file within that folder?
That resources folder in there is where all app files should be bundled. If you need to write back into them, you need to copy them out of the app on your first launch. If it’s read only, then you should be fine to work with them in there - except for SQLite. Xojo can’t open SQLite read only.
I hope this helped clear some things up. I’m sure I left some things out so if you have more questions please ask! I’m happy to help people package their Mac apps correctly.
Thank you, Tim, for your quick response. I’ve been programming computers for over 50 years, but prior to this project it was all in machine/assembler code so I had complete control over everything. The object-oriented IDE is great, but there are many things going on below the surface that I don’t understand.
So you’re absolutely right that I’m not grasping the idea. Are you saying that the app must create the Resources folder at run time start up? And then it copies the sub-folders into that folder? I guess I’m entirely lost. Is there example code somewhere?
I’m not using SQLite at all. The app never writes into the files in the RequiredFiles folder, but there are other folders containing text files that are read and written by the app.
I was hoping that some simple solution like
f = GetFolderItem.parent.child( “RequiredFiles” )
would work. No?
The code I showed in my first post has been working fine for 25 years, and fails only when the package is compressed and then expanded on a different computer. Something is not restored when the package is expanded.
For macOS this is how an app is normally bundled into a single directory that, to the end user, appears to be one file
Windows has no exact equivalent
On macOS, and WIndows, this may cause you issues since you cannot write to the internals of a mac App bundle
And on Windows if the files are in a protected folder then a user may also not be able to write to them
In both cases you’re better off creating a directory in SpecialFolder.ApplicationData that is for files YOUR application reads and writes that users will have access to manipulate
The code I showed in my first post has been working fine for 25 years, and fails only when the package is compressed and then expanded on a different computer. Something is not restored when the package is expanded.[/quote]
It probably is expanded
But depending on where the application was unzipped it can fail for other reasons hving to do with security etc
Recent changes in macOS and Windows make what used to be common practice no longer “safe” and so your code has to adopt newer and safer practices
So you’re absolutely right that I’m not grasping the idea. Are you saying that the app must create the Resources folder at run time start up? And then it copies the sub-folders into that folder? [/quote]
A macOS app is actually a folder (called a package). This fact is hidden from the user. You double-click on it and the app runs. If you right-click on an app, and choose “Show package contents” you can see what is actually inside the app. The Resources folder is one such item. The Xojo IDE will create all that for you when you build the .app. As Norman says, Windows seems not to have an equivalent structure, so whereas with a macOS .app the support files for the app are hidden inside it, under Windows and Linux they have to sit alongside it.
The macOS security is getting tighter with newer OS versions, so that although your app can read the contents of the Resources folder, it should avoid trying to write to it.
Thanks, Norman and Tim, for clarifying that quite a bit. I find that my app’s Resources folder contains all my graphics elements. Since the IDE asks to find these when I do a build, it’s clear that they are put in the Resources folder by the IDE during the build. But my data files can’t be put there because the app must be able to write to them at any time.
The data folders are not created by the IDE. They are created independently and put together with the app in the parent folder by me after the build. Also, some data files within these folders are created at run time.
So back to the original question. Why does f = GetFolderItem( “RequiredFiles” ) work fine for both Mac and Windows EXCEPT on a Mac when the package has been compressed and expanded on a different computer?
And most importantly, what can I do about it?
Would something like f = GetFolderItem.parent.child( “RequiredFiles” ) work?
Is there some special place the zipped file should be put before it’s expanded?
What “safer” practices are you referring to?
You should not put data created at runtime in dirs next to the app on just about any OS as you can experience various issues.
Files you do create at runtime should be placed in SpecialFolder.ApplicationData in a dir your app creates the first time it is launched.
Security. Your app has no access to anything except locations explicitly provided by the user via an Open/Save dialog, Temporary Items, and your own child of SpecialFolder.ApplicationData. In modern apps, you need to design as if GetFolderItem() does not exist.
Correctly package your app to work with current security standards. Trying to fight them will only cause you headaches.
No. You’re asking for access to files next to the app, which is not allowed by today’s security standards.
What ZIP file? The finished package? No, this does not matter.
Forget GetFolderItem() exists
Do not access files without permission (Open/Save dialog)
Only read from app folder, never write
Only write in temporary, SpecialFolder.ApplicationData.Child("com.my.bundle.identifier"), and where told to with Open/Save dialog
And these are just starters. You basically have to design your app as if to prove it’s not malware because there’s just so much out there.
You can include the graphic elements in your project via the IDE, so you don’t have to put up with the IDE asking you for them at build time. I use a folder from the IDE’s library pane (see the Project Items section) for things like these. Just drag and drop them onto the folder.
Thanks to all for trying to help, but it’s all very confusing. You tell me I have to do many things differently. It appears that I would have to completely redesign many parts of my project to do that, but I can’t find any examples on how to do that. And all that to avoid a problem that occurs only if the package has been compressed.
How about if I put the app separate from a folder holding the data folders? I’ll call that folder “OpVPdata”. If necessary, the end user could use Get Info to set the permissions for that folder. The app would be by itself. Can I then use GetFolderItem to open the OpVPdata folder and its sub folders and then read and write to the text files contained therein?
What would be the actual code to find the OpVPdata folder? What if the user put the app in the Applications folder and the OpVPdata folder somewhere else?
If you ever want to put the app in the Mac App Store, this is a big no-no.
Why don’t you try to wrap your head around the fairly simple explanations provided in this thread ? I know it is possible. I started developing back in the early eighties, when none of that was around.
All this is so very confusing, I don’t even know how to ask pointed questions. And there are probably many other things that would keep my program out of the Mac App Store. That doesn’t concern me at all.
Tim said that the data should not be beside the app, but I didn’t see anything about not putting data in a separate folder. In fact, I find that I have several commercial programs that do just that. What about iTunes, iMovie, etc.?
So here are two pointed questions:
Why shouldn’t I just put the app separate from the data folder?
What would be the actual code to find that folder wherever it might be?
This is probably due to translocation. MacOS literally moves your app to a sandbox location in order to run it. The data files don’t go with it. This is new and very different. You will have to change the way you do things to adapt to it. @Tim Parnell outlined the main points above.
Basically, MacOS doesn’t trust an app that came from anywhere other than the app store and tries to protect itself from it.
OK, I can’t put the data folders next to the app because that’s a no-no, and anyway the app actually gets moved when it’s run. So why does that work unless the package has been compressed and expanded?
And I can’t put the data folders in the Resources folder because I’m not allowed to write to things in that folder.
What does that leave but to put the data folders in a separate folder which I’ve tentatively called OpVPdata? It seems that it shouldn’t be hard to find OpVPdata wherever it might be, but I don’t know exactly how to do that. Is there example code somewhere?
It’s not necessarily compress/expand that messes you up, it’s expanded from a foreign source that get’s you flagged as suspect and therefore sandboxed. I believe there’s a way for the user to tell MacOS to not sandbox your app, but that’s a manual process.
As a separate issue, you cannot write to anything in your bundle any more. So what you do is include files in your app’s bundle (via a Copy step as part of your build settings) and upon app launch, you check to see if those files already exist in SpecialFolder.ApplicationData and if not, you copy them there. You also can create any files you need there. That should work whether you’re sandboxed or not (I think?). From that point on, you read/write to ApplicationData.
OK, Im envisioning six things on the CD:
The Mac application: OptimumVP.app
The Windows application: OptimumVP.exe
The Windows libs folder: OptimumVP Libs
The OpVPdata folder, which contains 3 sub folders, each containing several text files
Read Me First, a pdf document that tells how to install the package
The User Manual, a pdf document
To install the package, the user copies the appropriate app to the Applications folder (or maybe to some sub folder) and runs the app from there.
On startup, the app looks for OpVPdata in SpecialFolder.ApplicationData. If its not there, it puts a copy there.
How do I do that copy?
If its a Windows system, what do I do with the Libs folder?
The app accesses the Games data folder with
dim f as FolderItem
f = GetFolderItem(“SpecialFolder.ApplicationData”).child(“OpVPdata”).child(“Games”)
Or can I just write
dim f as FolderItem
f = GetFolderItem(“SpecialFolder.ApplicationData”).child(OpVPdata)
and save f for all access to all the data folders?