Strategy for Saving Modified Data

One of the Newton 2.x OS Q&As
Copyright © 1997 Newton, Inc. All Rights Reserved. Newton, Newton Technology, Newton Works, the Newton, Inc. logo, the Newton Technology logo, the Light Bulb logo and MessagePad are trademarks of Newton, Inc. and may be registered in the U.S.A. and other countries. Windows is a registered trademark of Microsoft Corp. All other trademarks and company names are the intellectual property of their respective owners.


For the most recent version of the Q&As on the World Wide Web, check the URL: http://www.newton-inc.com/dev/techinfo/qa/qa.htm
If you've copied this file locally, click here to go to the main Newton Q&A page.
This document was exported on 7/23/97.


Strategy for Saving Modified Data (4/8/97)

Q: What's the best way to save modified data to a soup? For example, if I try to save the contents of a clEditView every time some change is made, typing is very slow. So, when should it be saved?

A: The best way we've found is to start a timer every time a change is made, and save the data when the timer expires. If a change is made before the timer expires, you can reset the timer. This way, the longer operation of saving to a soup won't happen until after the user pauses for a few seconds, but data will still be protected from resets. The data should also be saved when the views that edit it are closed, or when switching data items.

The timer can be implemented several ways. If no view is available, AddDelayedCall or AddDelayedSend can be used. The OS also provides AddProcrastinatedSend and AddProcrastinatedCall, which more or less implement the timer-resetting feature for you.

The best way to implement the timer when views are available is using the viewIdleScript. The viewIdleScript is preferred over AddProcrastinatedCall/Send because of better management of the event queue by the OS. When you call SetupIdle to start an idle timer, any existing idle timer is reset. Procrastinated calls/sends aren't currently implemented by resetting an existing timer, but rather by creating a delayed event which fires for each call and then checking a flag when the timer expires to see if it's the last one.

Where these methods are implemented depends on what layer of your code manages the soup entry. With the NewtApp model, the Entry layer manages the data, and each view in the Data layer is responsible for stuffing the modified data in the target frame, which is usually a soup entry. The entry layer implements StartFlush to start the timer, and EndFlush is called when the timer expires and which should ensure that the data is saved to the soup.

Your StartFlush equivalent could be implemented something like this:
    StartFlush: func()
        begin
            self.entryDirty := TRUE;
            :SetupIdle(5000);    // 5 second delay
        end;


Your viewIdleScript would look something like this:
    viewIdleScript: func()
        begin
            :EndFlush();
             nil;            // return NIL to stop the idler until next StartFlush
        end;


And your EndFlush equivalent would look something like this:
    EndFlush: func()
        if self.entryDirty then
            begin
                // getting data from editView may not
                // be necessary at this point

myEntry.editViewData := <editView/self>.viewChildren;
                EntryChangeXmit(myEntry, kAppSymbol);
                self.entryDirty := nil;
            end;


Implementing EndFlush as a separate method rather than just putting the contents in the viewIdleScript makes it easy to call the method from the viewQuitScript or viewPostQuitScript, to guarantee that changes are saved when the view is closed. (The viewIdleScript may not have been called if the user makes a change then immediately taps the close box or overview or whatever.)