A function returning file contents?

I need a function to return the contents of a file as a list given the filename. I can see no way to do this because of the odd event nature of AI2's File component. All the examples I have seen use a ReadFrom call and then a separate block with a GotText event -- this appears to leave no way to get the result back the function to return the contents to the caller. The alternative is to split a very large block into many blocks all containing huge amounts of duplicated processing (and then maintaining all these different blocks if a change is required).

If a wait-for-event or sleep block existed in the tooling, then the event loop could run until the event occurs and return to the block that had been waiting or sleeping (a polling loop doesn't work because it doesn't allow the event loop to process what one is waiting for). Another way this can be handled is to implement an update block (similar to update in tcl/tk) which would force the event loop to process everything that has been queued before returning to the code where it was called. {Maybe a branch block to jump back to the line after the ReadFrom would work. This is frowned upon by modern computer science and I'm sorry to break this to you, but that is what actually happens once the code gets to the processor.}

If neither of the above are appealing, then why not a more standard file open, read, file close paradigm? The way Files are implemented in AI2 seems very unnatural and forced.

I look forward to reading any comments regarding this issue.

Not sure why you are experiencing a problem getting the contents of a "text" file?

Provide your file / example of your file, so that we can show you how to access the contents, and if it is a correctly formatted list, how to convert this to an AI2 list.

1 Like

The key is that I want a function -- not a procedure.
Screenshot 2022-08-04 at 20-50-17 MIT App Inventor

Does it get any more simple than read, then return contents ?

Are you suggesting I call the function multiple times depending on the size of the file? There is no way of knowing when the global filedata has been updated.

How do you get the result from the "File1.GotText" event back to the "to readDataFile" function? You can see from the disabled block that I tried polling global readComplete and found it never changes. I understand why that is, but am looking for a solution to have a working function.

Just pass global filedata to where you want it to go in the File1.GotText event.

Are you talking about a component? I can't see that working for me. I need to do some processing on the data before it gets sent to a component (a canvas in this case). I thought about sending it to a hidden component, but still have the problem of knowing when it has been updated without a polling loop and that stops the event loop unfortunately.

Maybe you could add an extra function, is_file_ready(filename) to your app, to check a central dictionary (filename:contents) for previously read file content, and if necessary, initiate a read / update cycle? (Add the file:contents to the dictionary when they arrive.) Return true if available, false if too early.

This does not fix your determination to make an event-oriented architecture into a command and control architecture.

P.S. For an event-oriented architecture like AI2, the solution to this kind of problem is to envision the app as a factory floor with multiple workers, connected by conveyor belts (lists) delivering data to the worker stations.

A sample queue-driven app:


Do you really see reading a file as something that is event driven? This forces the whole file to be read even if only a small part of the data is important to you. I understand event driven thought processes. I worked with the tcl/tk stuff 30 years ago. To me it looks like reading a file has been forced into an event driven context. Maybe to add two numbers one should supply the two numbers to some block and wait for an event to give you the result.

The FAQ I linked in a prior post has fine essays by the designers of AI2 on the AI2 event model.

AI2 is meant to run on cell phones, with limited battery life and operating systems that demand their fair share of processor time. This is not a hardwired Arduino system, where you have access to all the power and processor cycles.

As I pointed out on a device that has limited memory perhaps one doesn't want to slurp in the whole file either.

Are you looking to use a file as a pipe, Unix-style?

I haven't seen it done yet in AI2.

It sounds like the kind of thing one of our enthusiastic extension writers would like to tackle.

P.S. If you check the Activity Starter component doc, there are facilities to start other apps with startup data, and to be started with startup data by another app.

Maybe this could be useful to you?

SQLite ?

A pipe was not my intention. My app has multiple files (the number of which isn't known before runtime) specified by the user. The information required from each file depends on other user inputs (mathematical formulas, let's say) and the contents read from previous files up until that point.

I don't know if it implemented pipes, but I ran across an extension the other day that did lots of other /bin/sh type of things. Sorry, but I don't recall the name of the extension.

Seems like one must always travel through Berlin or Tokyo on their way from Dallas to Fort Worth.

AI2 has in its File component a directory reading facility (I have not used it yet.)

If you have control over the incoming files' nomenclature, a datetimestamp file name prefix might be used to indicate which files are new, and the app could poll the directory for new files, under control of a Clock Timer (Sensors Drawer.)

You could always not save to file, but use the tinydb to store your user generated data, this is persistent on the app, and available without an event block to return the content.

I have used the listDirectory method. It is fairly simple but seemed to return all the Shared files on the device without regard to the directory that is specified if Shared scope is used. Legacy scope worked as expected. I didn't try the other scopes as my data is coming from /Download (external to the app).

The order things appear in the text string supplied by the user is important and changes what happens in the big loop. The only thing I can see that can work is to process the list and pre-fetch all the datafiles. This means I have to have enough available memory to hold every byte of every file even though I will eventually be discarding much of it. The ability to read a line or a fixed buffer length could eliminate this penalty.

The files are generated outside the app. I am already using tinydb to handle sharing between pages and to save the state of the app for later invocations. Is tinydb memory-based or "disk"-based? I didn't see many details in the docs.

Data for tinydb is saved to an XML file, buried in the private user area of the app.