App storage
Every app with an ID gets a private storage area.
App.Storage returns a
bellui.Storage for
it. Use it for files the user does not pick themselves: drafts, caches, saved
state.
store := a.Storage()
root := widget.NewLabel("Storage root: " + store.RootURI().String())Documents
The Storage methods work on named documents in a Documents folder inside
the storage area. The folder is created the first time you call Create.
| Method | What it does |
|---|---|
Create(name) | Opens a new document for writing. Fails with storage.ErrAlreadyExists if it exists. |
Save(name) | Opens an existing document for writing. Fails with storage.ErrNotExists if it does not exist. |
Open(name) | Opens a document for reading. |
Remove(name) | Deletes a document. |
List() | Returns the names of all documents. |
RootURI() | Returns the URI of the whole storage area. |
Writing is finished only when you close the writer. A save that should work
whether or not the document exists tries Create first:
// saveNote creates the document the first time and overwrites it after that.
func saveNote(store bellui.Storage, name, text string) error {
wr, err := store.Create(name)
if errors.Is(err, storage.ErrAlreadyExists) {
wr, err = store.Save(name)
}
if err != nil {
return err
}
if _, err := wr.Write([]byte(text)); err != nil {
wr.Close()
return err
}
return wr.Close()
}Reading is the same as for any other URI:
func loadNote(store bellui.Storage, name string) (string, error) {
r, err := store.Open(name)
if err != nil {
return "", err
}
defer r.Close()
data, err := io.ReadAll(r)
return string(data), err
}RootURI is an ordinary URI, so you can also use the functions in
Files and URIs on it, for example to make your own subfolders
next to Documents.
Where the storage area is
| Platform | Location |
|---|---|
| macOS | ~/Library/Preferences/bellui/<app ID> |
| Windows | %USERPROFILE%\AppData\Roaming\bellui\<app ID> |
| Linux and BSD | bellui/<app ID> inside the user configuration folder, usually ~/.config |
| Android | bellui inside the app’s private files folder |
| iOS | bellui inside the app’s Documents folder |
| Web | idbfile:///bellui/, kept in the browser’s IndexedDB |
On Android and iOS the operating system already gives every app its own sandbox, so the app ID is not part of the path. The same folder also holds the app’s preferences file.
RootURI logs an error and
returns the system temporary folder, and the document methods return an error
asking you to use app.NewWithID.The complete example
// Documents in the app's private storage area.
package main
import (
"errors"
"io"
"strings"
"time"
"github.com/belluikit/bellui"
"github.com/belluikit/bellui/app"
"github.com/belluikit/bellui/container"
"github.com/belluikit/bellui/storage"
"github.com/belluikit/bellui/widget"
)
func main() {
a := app.NewWithID("com.example.appstorage")
w := a.NewWindow("App storage")
store := a.Storage()
root := widget.NewLabel("Storage root: " + store.RootURI().String())
files := widget.NewLabel("")
refresh := func() {
names := store.List() // the documents saved by this app
files.SetText("Documents: " + strings.Join(names, ", "))
}
save := widget.NewButton("Save note", func() {
if err := saveNote(store, "note.txt", "Saved at "+time.Now().Format(time.Kitchen)); err != nil {
files.SetText("error: " + err.Error())
return
}
refresh()
})
load := widget.NewButton("Load note", func() {
text, err := loadNote(store, "note.txt")
if err != nil {
files.SetText("error: " + err.Error())
return
}
files.SetText(text)
})
remove := widget.NewButton("Delete note", func() {
_ = store.Remove("note.txt")
refresh()
})
refresh()
w.SetContent(container.NewVBox(root, save, load, remove, files))
bellui.ShowAndRun(w)
}
// saveNote creates the document the first time and overwrites it after that.
func saveNote(store bellui.Storage, name, text string) error {
wr, err := store.Create(name)
if errors.Is(err, storage.ErrAlreadyExists) {
wr, err = store.Save(name)
}
if err != nil {
return err
}
if _, err := wr.Write([]byte(text)); err != nil {
wr.Close()
return err
}
return wr.Close()
}
func loadNote(store bellui.Storage, name string) (string, error) {
r, err := store.Open(name)
if err != nil {
return "", err
}
defer r.Close()
data, err := io.ReadAll(r)
return string(data), err
}