Mac App Store Submission - File Locations & Sandboxing

Hi. Today I am making a start on creating a MAS version of my app and have a question about file location permissions.

Right now, when my app launches it creates some default folders in “Users/Username/Documents/My App Name” folder such as “projects, backup, examples” etc and copies some resource files from the bundle into those folders so the app can access them. The default empty “Projects” folder would be where the user is prompted to save work etc.

If I understand correctly, this would not be allowed in a MAS submission and instead all of this would have to be created in SpecialFolder.ApplicationSupport.Child(“My App Name”)?

Also, can someone please explain what “sandboxing” means in terms of Xojo testing my app for MAS and how to do this to ensure it complies before submitting it ?

I believe you can ask for special entitlements to write to he user’s documents folder, but as you’ve described your app, I don’t see any need to. Just use SpecialFolder.ApplcationData.mycompany and go with the MAS flow.
“Sandboxing” refers to how your app will handle its files. They will be put into a special container but that will all be done for you. As I’m sure you have read on the Forum, I highly recommend using Sam Roland’s App Wrapper to sign and prepare your app when it’s finished. AW will handle the sand boxing, create the necessary installer, and assure you that all is well before you ship it off to Apple.
Are your certificates in order?

Thanks, Roger. I believe my certificates are in order and I will check out App Wrapper but all of that will be the last steps for me. I’ve got a lot of other key stuff to handle first like the folder writing locations and disabling my registration and update checker system and adding the MAS in-app purchase in place of my own system that currently uses esellerate.

You say use “SpecialFolder.ApplcationData.mycompany”…is that the preferred method as opposed to using “SpecialFolder.ApplicationSupport.Child(“My App Name”)” ? And by “mycompany” do you mean the app’s bundle identifier reverse domain value etc?

I think most developers use:
SpecialFolder.ApplcationData.Child(“mycompany”).Child(“myAppData”) but the “my company” folder is not required. Do as you like.

Can be, or I just use “COS”, the initials of my company.
BTW - since that folder is hidden from the user without special steps, you might want to provide a link within your app to that folder if your user’s need access to the data which resides there outside of your app.

Good tip, thanks!

Use the TPSF module for accessing your own Application Support folder. It uses declares on the Mac to get the bundle identifier and provides you with the proper location. You are still responsible for creating the directories, but across Mac, Windows, and Linux it will give you the location you are supposed to be using.

It’s free and will save lots of headaches https://github.com/devtimi/TPSF

It should be the bundle identifier of your application. App Wrapper for instance is “com.ohanaware.appWrapper”.

Sandboxing is the process of locking what your application can and cannot do. The idea behind it is that if your application gets infected with a virus, the damage it can create is very minimal.

IMHO Apple got it the wrong way around, apps sold outside MAS should be Sandboxed and apps sold via MAS shouldn’t need to be (as apps sold via MAS are subject to scrutiny by Apple). I digress however.

[quote=354256:@Roger Clary]I think most developers use:
SpecialFolder.ApplcationData.Child(“mycompany”).Child(“myAppData”) but the “my company” folder is not required. Do as you like.[/quote]
I personally would advise you follow Apple’s guidelines, for the simply reason that they have the habit of becoming a requirement. Take Frameworks for instance, many 3rd party frameworks do not meet Apple’s guidelines. They work fine in applications, but now they cannot be code signed unless they follow Apple’s guidelines to a t. Or version numbers for an application, if you don’t meet Apple’s “Guidelines” you can’t sell in the App Store.

Thanks, Tim I’ll check it out.

Thanks, Sam. I think I will follow that route.

I found your post about recent items (https://forum.xojo.com/3428-sandboxing-done-right-recent-items/0) and am unsure whether it is relevant to my app or not. Right now I maintain my own recent items list at the bottom of the File menu, not via “Open Recent>” etc. Is this okay for app store submission or do I have to use a different system as suggested by your post?

Also, if I understand correctly, unlike normal Mac app distribution, all other items normally contained in the app’s folder have to be included in the bundle itself. As such, is their a way to do a conditional post build script to do this only for a MAS target and/or can AppWrapper handle this?

FYI: Right now I have created a “MASVersion” constant and refer to this throughout my project so I can maintain a single source file for all platforms etc but this would not be accessible for post build scripts I don’t think. Yet I need to be able to move my user guide pdf and some other files into the bundle and would prefer to do it in code rather than manually after the build.

If you allow your user to create files wherever on the disk and want to provide them with a quick means of opening the last few files (from a Sandboxed application) then yes it does apply. If you keep all of their data within your app’s container (that’s the folder within the user’s library that you have access to) then no.

Technically applications should be self contained already, recent OS “security” changes will cause problems for your users otherwise. The only thing that should outside of the application bundle, is the users data.
Currently this isn’t something that App Wrapper handles automatically. You could extend the “Pre-Build” script that App Wrapper creates for you, so that if you’re targeting MAS, you can have these files copied into the built application when Xojo builds the application.

You can use project constants in Xojo IDE build scripts, and as I mentioned above, App Wrapper creates a “Pre-Build” script that you can extend, this script will allow you to change things in your project depending on whether the application is being processed for the MAS or website, and if the app is being Sandboxed. I currently use it to set the project constant I use in my apps to switch on and off functions for the different targets.

Thanks, Sam. I think I may need to use your recent items class then because it seems unrealistic not to be able to display recent items that may have been saved to a USB on a previous session and then reloaded. Only displaying those within the container doesn’t really make any sense. Your link to SDR_RecentItems.zip file doesn’t seem to work though … do you have a current link?

I will also have to look into your pre-build script as you suggested.

In terms of permissions in AppWrapper for files within the container, do these need to be set? Because I’m testing right now in my sandboxed app and trying to invoke the Open file dialog and Save file dialog and the console is showing a failed entitlements check error and my app doesn’t display the dialogs.

Rather than creating a pre-build script in AppWrapper I wanted to try and create a post build script in Xojo (as Sam mentioned was possible) to copy some files into the bundle’s resources folder conditionally according to my app’s “MASVersion” project constant but the IDE script editor doesn’t seem to recognise the constant and raises a compile error if I try to refer to it.

This is the link I referred to:
http://www.realsoftwareblog.com/2009/03/post-build-scripts.html

I have never written an IDE script before so don’t know what I am doing wrong. Or is the problem that I am trying to create a post build script the wrong way and confusing that with an IDE script? Any help would be appreciated. Thanks.

I would advise not doing it conditionally.

  1. You would then be able to use a simple Copy Files step for your user guide and other resources
  2. Sandboxed or not, resources should be inside the resources folder anyway
  3. This would make your code easier to handle, as now for Mac, Windows, Linux, and the MAS you are looking in the same place for the files
  4. Prepared and ready for when Translocation Hits You™ (Mac security feature prevents you from accessing files not inside the bundle on newer versions of the OS)
  5. Theres neat code that exists already to help with accessing the internal resources, like TPSF (free!)

There is no harm in defensively building your app as if it was going to be sandboxed. Even more so if you plan to target the MAS.

Hi Tim. Thanks for the advice and your logic is correct but right now I am not ready to change the behaviour of the entire app since it is working well the way it is for Mac and Windows and I want to do minimal changes as possible just for the MAS Version.

It may not make sense to you but for me right now this is the way I need to handle it.

As such, I need to know how to either write a MASVersion conditional copy step post build script … or how to create a pre-build script for app wrapper. Both of which I have absolutely zero experience with and about two hours of trudging through forum posts hasn’t helped me :frowning:

I need to be able to copy individual files and a folder of files from my source control folder into the bundle resources folder.

If I can’t work this out I’ll have to manually do it after build but that’s obviously not the best solution.

Thanks.

Here’s what I’ve found in the documentation for using constants in a build script:
http://developer.xojo.com/ide-scripting-constants

Hope it offers some help!

Yes, unfortunately those few pre-defined constants are no use since I need to be able to refer to a custom constant such as App.MASVersion. Thanks for trying :slight_smile:

Well, on that page I saw ConstantValue so I thought the details of that were there.
Turns out, they’re here: http://developer.xojo.com/userguide/ide-scripting-project-commands$ConstantValue

The constants are a tiny bit convoluted. If you have an integer constant for the version then the correct syntax for the build script is:

if ConstantValue("App.kMaxVersion") = "0" then 'do something elseif ConstantValue("App.kMaxVersion") = "1" then 'do something else end if

However building 2 different variations of an app only properly works with an IDE communicator script or you have to set the constant manually:

dim theVersion as String = "0" ConstantValue("App.kMaxVersion") = theVersion if ConstantValue("App.kMaxVersion") = "0" then PropertyValue("App.Application Identifier") = "com.mothsoftware.mailarchiverx" 'and so on

But this IDE communicator script stuff is majorly cool because it allows me to delete the old apps, build up to 5 apps at once and can do 32 or 64bit.

HI Denise,
sorry about the late reply.

  1. Open & Save dialogs; in the “Capabilities” section of App Wrapper, change the popup menu next to “Open & Save Dialogs” to “Read & Write”.

  2. The Recent Items code was combined with a whole bunch of other code I’ve written for working in the App Sandbox and bundled as the “Sandbox Kit”, which is available from http://ohanaware.com/sandboxkit/

  3. Here’s an example “preBuildScript” which I use for our HDRtist NX.

[code]const isSandboxed = “True”
const isAppStoreEdition = “False”

ConstantValue( “ORCAController.isAppStoreEdition” ) = isAppStoreEdition[/code]

The first two lines are automatically updated by App Wrapper (unless you move the script). The forth line is where I set an internal project constant from the constants that App Wrapper writes.

BTW, by default we Sandbox our applications even if they’re not being released on the Mac App Store as this reduces complications for us (and customers who change between). Except App Wrapper, as Apple’s rules prevent a Sandboxed application from code signing another application Doh! (apart from their Xcode which has ‘special’ privileges that can’t be used by us, only Apple).

[quote=354700:@Tim Parnell]Well, on that page I saw ConstantValue so I thought the details of that were there.
Turns out, they’re here: http://developer.xojo.com/userguide/ide-scripting-project-commands$ConstantValue[/quote]

Thanks!

Thanks, Beatrix, that helped a lot.

No problem and thank you for all the helpful information.

I’ve managed to get the conditional constant now working with the post-build script so I should be okay without the AppWrapper pre-build script.

Day three and I seem to be making good progress so thank you all for your help so far.

Ok, so now I’ve run into a major problem that is so frustrating. I have created my default folder structure for project files and templates within the allowed Application Support container folder, but when I display a SaveAsDialog to the user to save a file there, the OS ignores the InitialDirectory I set as my sandboxed project folder and just displays the “Documents” folder. Why?!

This makes no sense to me since the user is not allowed to save there and I have specified the sandboxed location. All I can think of is that for some reason the OS doesn’t want this hidden location displayed and so is overriding it but then how on earth are we as developers supposed to work with files and make the save file process work?

It feels like I’m stuck in a non-sensical loop. I need to create a default folder structure for my app’s user documents and templates on app launch (behind the scenes without any user interaction) but am not allowed to do so in the non-sandboxed location so am forced to put it in another location but then cannot direct the user there to save their documents?

Any ideas of either what I am doing wrong or how to resolve this?