OSFiles

Cross-platform file and folder selection helper. Wraps Windows FileDialog and macOS AppleScript pickers behind a unified API. Supports single and multi-selection for both files and folders, persists the last-used folder across sessions, and provides iterator-style enumeration over multi-selections. State bag for OS detection, selections, and cursor positions. Exposed methods Workbook-level Name used to persist the last used folder path.

Version: 1.0 (2026-02-09)

Initialization

create #

Factory returning an initialised interface

Signature:

Public Function Create() As IOSFiles

Returns: IOSFiles. Ready to use.


Elements

file #

Current loaded file path

Signature:

Private Property Get File() As String

Returns: String. The first selected file path.


files #

All loaded file paths from a multi-selection

Signature:

Private Property Get Files() As Variant

Returns: Variant. Zero-based string array of file paths.


folder #

Current loaded folder path

Signature:

Private Property Get Folder() As String

Returns: String. The first selected folder path.


folders #

All loaded folder paths from a multi-selection

Signature:

Private Property Get Folders() As Variant

Returns: Variant. Zero-based string array of folder paths.


Selecting

load-folder #

Show a single-folder picker dialog

Signature:

Private Sub LoadFolder()

load-file #

Show a single-file picker dialog

Signature:

Private Sub LoadFile(ByVal filters As String, _ 
                     Optional ByVal customMacFilter As String = vbNullString)

Parameters:

  • filters: String. File type filters (e.g. "*.xlsx").
  • customMacFilter: String. Optional UTI override for macOS. Defaults to vbNullString.

load-files #

Show a multi-file picker dialog

Signature:

Private Sub LoadFiles(ByVal filters As String, _
                      Optional ByVal customMacFilter As String = vbNullString)

Parameters:

  • filters: String. File type filters.
  • customMacFilter: String. Optional UTI override for macOS. Defaults to vbNullString.

load-folders #

Show a multi-folder picker dialog

Signature:

Private Sub LoadFolders()

Iteration

reset-files-iterator #

Reset the file iteration cursor to the beginning

Signature:

Private Sub ResetFilesCursor()

reset-folders-iterator #

Reset the folder iteration cursor to the beginning

Signature:

Private Sub ResetFoldersCursor()

has-next-file #

Check if another file is available in the iterator

Signature:

Private Function HasNextFileValue() As Boolean

has-next-folder #

Check if another folder is available in the iterator

Signature:

Private Function HasNextFolderValue() As Boolean

next-file #

Return the next file path and advance the cursor

Signature:

Private Function NextFileValue() As String

next-folder #

Return the next folder path and advance the cursor

Signature:

Private Function NextFolderValue() As String

Validation

has-valid-file #

Whether the loaded file path is valid

Signature:

Private Function HasValidFile() As Boolean

Returns: Boolean. True when a non-empty file path exists.


has-valid-files #

Whether the loaded files array contains entries

Signature:

Private Function HasValidFiles() As Boolean

Returns: Boolean. True when at least one file was selected.


has-valid-folder #

Whether the loaded folder path is valid

Signature:

Private Function HasValidFolder() As Boolean

Returns: Boolean. True when a non-empty folder path exists.


has-valid-folders #

Whether the loaded folders array contains entries

Signature:

Private Function HasValidFolders() As Boolean

Returns: Boolean. True when at least one folder was selected.


Internal members (not exported)

Initialization

class-initialize #

Set default values for file and folder state

Signature:

Private Sub Class_Initialize()

reset-file-state #

Clear file-related state and reset the iteration cursor

Signature:

Private Sub ResetFileState()

reset-folder-state #

Clear folder-related state and reset the iteration cursor

Signature:

Private Sub ResetFolderState()

empty-string-array #

Return an empty variant array

Signature:

Private Function EmptyStringArray() As Variant

Returns: Variant. Empty array.


select-folders-on-windows #

Show the Windows multi-folder picker

Signature:

Private Function SelectFoldersOnWindows() As Variant

Returns: Variant. Zero-based string array of folder paths.


try-get-array-bounds #

Safely probe array bounds

Signature:

Private Function TryGetArrayBounds(ByVal candidate As Variant, _
                                   ByRef lowerBound As Long, _
                                   ByRef upperBound As Long) As Boolean

count-array-items #

Count the number of items in a variant array

Signature:

Private Function CountArrayItems(ByVal candidate As Variant) As Long

to-string-array #

Coerce a variant into a zero-based string array

Signature:

Private Function ToStringArray(ByVal value As Variant) As Variant

clone-string-array #

Create a safe copy of a string array

Signature:

Private Function CloneStringArray(ByVal source As Variant) As Variant

has-next-array-item #

Check whether the cursor is within array bounds

Signature:

Private Function HasNextArrayItem(ByVal items As Variant, ByVal cursor As Long) As Boolean

get-next-array-item #

Fetch the item at the cursor and advance it

Signature:

Private Function GetNextArrayItem(ByVal items As Variant, _
                                  ByRef cursor As Long) As String

set-files-selection #

Persist file selections and prime the iterator

Signature:

Private Sub SetFilesSelection(ByVal selectedItems As Variant)

set-folders-selection #

Persist folder selections and prime the iterator

Signature:

Private Sub SetFoldersSelection(ByVal selectedItems As Variant)

first-string-or-empty #

Return the first array entry or an empty string

Signature:

Private Function FirstStringOrEmpty(ByVal source As Variant) As String

normalize-folder-path #

Trim trailing separators and clean the folder path

Signature:

Private Function NormalizeFolderPath(ByVal folderPath As String) As String

extract-folder-from-path #

Identify the parent directory from a file path

Signature:

Private Function ExtractFolderFromPath(ByVal filePath As String) As String

remember-folder #

Cache the folder locally and in the workbook

Signature:

Private Sub RememberFolder(ByVal folderPath As String)

remember-folder-from-files #

Persist the parent directory of the first selected file

Signature:

Private Sub RememberFolderFromFiles(ByVal filesSelection As Variant)

remember-folder-from-folders #

Persist the first folder from a folder selection

Signature:

Private Sub RememberFolderFromFolders(ByVal folderSelection As Variant)

resolve-last-known-folder #

Resolve the last-used folder from cache or workbook storage

Signature:

Private Function ResolveLastKnownFolder() As String

has-workbook-context #

Check whether ThisWorkbook is accessible

Signature:

Private Function HasWorkbookContext() As Boolean

read-last-folder-from-workbook #

Read the last-used folder from a workbook-level Name

Signature:

Private Function ReadLastFolderFromWorkbook() As String

write-last-folder-to-workbook #

Persist the folder path in a hidden workbook Name

Signature:

Private Sub WriteLastFolderToWorkbook(ByVal folderPath As String)

encode-workbook-storage-value #

Convert a folder path into a Name.RefersTo-safe string literal

Signature:

Private Function EncodeWorkbookStorageValue(ByVal folderPath As String) As String

decode-workbook-storage-value #

Undo Name serialization to recover the original folder path

Signature:

Private Function DecodeWorkbookStorageValue(ByVal storedValue As String) As String

wrap-apple-script-string #

Escape a string for safe insertion into AppleScript

Signature:

Private Function WrapAppleScriptString(ByVal value As String) As String

wrap-apple-script-alias #

Build an AppleScript alias literal from a colon path

Signature:

Private Function WrapAppleScriptAlias(ByVal aliasPath As String) As String

build-apple-script-list #

Translate a VBA array into an AppleScript list literal

Signature:

Private Function BuildAppleScriptList(ByVal entries As Variant) As String

build-mac-filter-list #

Construct the UTI list for the AppleScript file picker

Signature:

Private Function BuildMacFilterList(ByVal filters As String, _
                                    ByVal customMacFilter As String) As String

parse-mac-selection-result #

Split newline-delimited POSIX paths from AppleScript output

Signature:

Private Function ParseMacSelectionResult(ByVal response As String) As Variant

normalize-folder-selections #

Normalize picker results into clean folder paths

Signature:

Private Function NormalizeFolderSelections(ByVal folderSelection As Variant) As Variant

convert-posix-to-colon-path #

Translate a POSIX path to AppleScript colon-delimited format

Signature:

Private Function ConvertPosixToColonPath(ByVal posixPath As String) As String

get-mac-default-folder-alias #

Resolve the default folder alias for macOS pickers

Signature:

Private Function GetMacDefaultFolderAlias() As String

build-mac-file-selection-script #

Compose the AppleScript for file selection on macOS

Signature:

Private Function BuildMacFileSelectionScript(ByVal filters As String, _
                                             ByVal customMacFilter As String, _
                                             ByVal defaultAlias As String, _
                                             ByVal allowMultiple As Boolean) As String

build-mac-folder-selection-script #

Compose the AppleScript for folder selection on macOS

Signature:

Private Function BuildMacFolderSelectionScript(ByVal defaultAlias As String, _
                                               ByVal allowMultiple As Boolean) As String

run-mac-file-picker #

Execute the AppleScript file picker and return POSIX paths

Signature:

Private Function RunMacFilePicker(ByVal filters As String, _
                                  ByVal customMacFilter As String, _
                                  ByVal allowMultiple As Boolean) As Variant

run-mac-folder-picker #

Execute the AppleScript folder picker and return POSIX paths

Signature:

Private Function RunMacFolderPicker(ByVal allowMultiple As Boolean) As Variant

Elements

self #

Self-reference as IOSFiles

Signature:

Public Property Get Self() As IOSFiles

Returns: IOSFiles. Self-reference.


os #

Current operating system flag

Signature:

Public Property Get OS() As String

Returns: String. "Mac" or "Windows".


os-set #

Override the detected OS flag

Signature:

Public Property Let OS(ByVal currentOS As String)

Parameters:


file-set #

Assign a single file path through the selection pipeline

Signature:

Private Property Let File(ByVal ff As String)

Parameters:


folder-set #

Assign a single folder path through the selection pipeline

Signature:

Private Property Let Folder(ByVal ff As String)

Parameters:


select-folder-on-mac #

Show the macOS single-folder picker

Signature:

Private Function SelectFolderOnMac() As String

select-folders-on-mac #

Show the macOS multi-folder picker

Signature:

Private Function SelectFoldersOnMac() As Variant

select-folder-on-windows #

Show the Windows single-folder picker

Signature:

Private Function SelectFolderOnWindows() As String

select-files-on-windows #

Show the Windows multi-file picker

Signature:

Private Function SelectFilesOnWindows(ByVal filters As String) As Variant

select-file-on-mac #

Show the macOS single-file picker

Signature:

Private Function SelectFileOnMac(ByVal filters As String, _ 
                                 Optional ByVal customMacFilter As String = vbNullString) As String

select-files-on-mac #

Show the macOS multi-file picker

Signature:

Private Function SelectFilesOnMac(ByVal filters As String, _
                                  Optional ByVal customMacFilter As String = vbNullString) As Variant

select-file-on-windows #

Show the Windows single-file picker

Signature:

Private Function SelectFileOnWindows(ByVal filters As String) As String

Test Support

assign-files-for-testing #

Inject file selections without showing a dialog

Signature:

Public Sub AssignFilesForTesting(ByVal selections As Variant)

Parameters:


assign-folders-for-testing #

Inject folder selections without showing a dialog

Signature:

Public Sub AssignFoldersForTesting(ByVal selections As Variant)

Parameters:


Used in (17 file(s))