Sandbox, SpecialFolder.Documents and SaveAsDialog

I have a sandboxed app where the user can save listbox contents as a textfile. A method is supposed to check in the documents folder whether a file is already existing and if yes, it adds a counter to the name.

The problem I have is that SpecialFolder.Documents points to a folder inside of the container, but when used with the SaveAsDialog, then it points to the documents folder outside of the container. So my method never can check at the right place.

With AppWrapper I have added the documents folder to “Access Specific Files” under Package->Apple Events & Specific Files. I checked “Relative” and “Write”

I thought like this I would have read/write access to the standard documents folder.

Is there a way how I could check for a file existing in the standard documents folder, from a sandboxed app?

My code:

[code] Dim ExportFolder As FolderItem = SpecialFolder.Documents
Dim fName As String = im.GetNewFileName(ExportFolder,myname,".txt")
Dim dlg As New SaveAsDialog
Dim ExportFile As FolderItem
dlg.InitialDirectory=ExportFolder
dlg.promptText=imStrings.imListboxSaveEnterFileName // Enter a new filename
dlg.SuggestedFileName=fName
dlg.Title=imStrings.imListboxSaveAsTabDelimited

ExportFile = dlg.ShowModal[/code]

dim f as folderitem = SpecialFolder.UserHome.Parent.Parent.Parent.Parent.child(“Documents”).child(“myfile”)

You still need to set access to /Documents as relative ; I did not check Write but was able to see the folderItem Exists

Thanks Michel! I’ll give it a try when I’m back in my office.

This is very fragile and will break if Apple moves where containers are stored.

Less fragile ?

dim f as folderitem = SpecialFolder.UserHome dim folds(10) as string = f.ShellPath.split("/") f = GetFolderItem("/"+folds(1)+"/"+folds(2)+"/", FolderItem.PathTypeShell) f = f.child("Documents").child("myfile")

This method works just as well sandboxed or not.

    return volume( 0 ).child( "Users" ).child( ns.userName ).child( "Documents" )

With ns.username being

Protected Function userName() As string #if TargetCocoa then declare function NSUserName lib "Foundation" as CFStringRef return NSUserName #endif End Function

[quote=115675:@Sam Rowlands] return volume( 0 ).child( "Users" ).child( ns.userName ).child( "Documents" )

With ns.username being

Protected Function userName() As string #if TargetCocoa then declare function NSUserName lib "Foundation" as CFStringRef return NSUserName #endif End Function [/quote]

Home directories do not have to be in that folder.

Okay, so it seems that there may be a perfect way of getting to a users documents folder pragmatically.

Which leads me to ask… Why do you need this?

There is a migration system that fires first time the app is run, which can pull certain files and folder into the application container.

[quote=115702:@Sam Rowlands]Okay, so it seems that there may be a perfect way of getting to a users documents folder pragmatically.

Which leads me to ask… Why do you need this?

There is a migration system that fires first time the app is run, which can pull certain files and folder into the application container.[/quote]
I’m not pulling files, but writing files out so that they can be used with other software.

The current use case is as follows:

The user can define the columns to be displayed in list boxes and records shown can be exported as Excel files or as text files. The InitialDirectory is the documents folder outside of the sandbox.

For convenience I wanted to add a counter to the proposed file name, in case the name is already taken.

It is not working as expected, but this is not a big deal, because now the user gets a warning and is asked whether an existing file should be replaced.

I was not aware of the fact that the home folder can be put elsewhere, but I’m still going to experiment with your and Michel’s suggestions. Thanks for that!

[quote=115705:@Oliver Osswald]I’m not pulling files, but writing files out so that they can be used with other software.

The current use case is as follows:

The user can define the columns to be displayed in list boxes and records shown can be exported as Excel files or as text files. The InitialDirectory is the documents folder outside of the sandbox.

For convenience I wanted to add a counter to the proposed file name, in case the name is already taken.

It is not working as expected, but this is not a big deal, because now the user gets a warning and is asked whether an existing file should be replaced.

I was not aware of the fact that the home folder can be put elsewhere, but I’m still going to experiment with your and Michel’s suggestions. Thanks for that![/quote]

[quote=115705:@Oliver Osswald]I’m not pulling files, but writing files out so that they can be used with other software.

The current use case is as follows:

The user can define the columns to be displayed in list boxes and records shown can be exported as Excel files or as text files. The InitialDirectory is the documents folder outside of the sandbox.

For convenience I wanted to add a counter to the proposed file name, in case the name is already taken.

It is not working as expected, but this is not a big deal, because now the user gets a warning and is asked whether an existing file should be replaced.

I was not aware of the fact that the home folder can be put elsewhere, but I’m still going to experiment with your and Michel’s suggestions. Thanks for that![/quote]

I am on iPad so i cannot experiment, but in the case the user folder was burried elsewhere ant not right under /Users one could look for /Library inside the user home folder by requesting Specialfolders.Documents. Then pick the immediate parent. Otherwise it should be possible to use a shell and look for ~user. I will try to look into these two methods when I get back.

Here it goes. I tested it fine. It should work fine if Apple does not change /Library inside the user home folder

Sub Action() //Works only if sandboxed ! If needed used if instr(f.shellpath,"Containers") >0 to make sure dim msg as string dim f as folderitem = SpecialFolder.Documents dim folds(10) as string = f.ShellPath.split("/") dim i as integer while folds(i) <> "Library" msg = "/"+msg+"/"+folds(i) i = i+1 wend f = GetFolderItem(msg+"/", FolderItem.PathTypeShell) f = f.child("Documents").child("myfile") return End Sub

I should mention that without App Wrapper ease of use, I would have not been able to test that so easily, and would have probably renounced for the cruel process of Apple free tools.

[quote=115729:@Michel Bujardet]Here it goes. I tested it fine. It should work fine if Apple does not change /Library inside the user home folder
<…>[/quote]

Cool. Works for me as well! Thanks again, much appreciated!

I wonder about this leading slash you are using? It works, even though it leads to a msg string looking somthing like this: “////Users/ …”

   msg = "/"+msg+"/"+folds(i)

I have slightly changed that code:

[code]Function GetPublicDocumentsFolder() As FolderItem
'******************************************************************
’ Last change: 2014-07-29
’ Purpose: Retrieve documents folder outside of sandbox container
'******************************************************************

Dim f As folderitem = SpecialFolder.Documents

#If TargetCocoa
Dim p As String
Dim folds(-1) As String = f.ShellPath.Split("/")
Dim i As Integer

While folds(i) <> "Library"
  If folds(i) <> "" Then
    p = p + "/" + folds(i)
  End If
  i = i+1
Wend

Dim f2 As FolderItem
f2 = GetFolderItem(p + "/",  FolderItem.PathTypeShell)

If f2 <> Nil And f2.Exists Then f2 = f2.child("Documents")
If f2 <> Nil And f2.Exists Then f = f2

#EndIf

Return f
End Function
[/code]

Oliver, you are right, the idea was good, but an oversight made me add “/” several times. For some reason it still worked.

Thank you for spotting it, and glad it now works for you :slight_smile: