Objective-C / Cocoa: Uploading Images, Working Memory, And Storage - objective-c

I'm in the process of porting an application originally in java to cocoa, but I'm rewriting it to make it much better, since I prefer cocoa a lot anyway.
One of the problems I had in the application, was that when you uploaded images to it, I had the images created, (as say an NSImage object) and then I just had them sitting in memory, the more I uploaded the more memory they took up, and I ended up running out of memory.
My question is this: if I am going to have users upload images to this application in cocoa, how should I go about storing them? I don't just want to copy the file paths, because I want what is saved to contain the images, etc. Is there any way to upload an image and copy it into a different place only for my application? Then load that image with the new path name as needed?
Only I would like it all to be consolidated. I'm going to implement saving by archiving one "master" object into an NSData*- so I'd like the images to be saved with that.
Is there a temporary location maybe where I could write the images to disk for my application, and then when I saved, they would all be archived into a single file? Also, how do I do this? Thanks.

If you only want to store the images temporarily, you can store the images in the temporary folder that you get by calling NSTemporaryDirectory(). You would then be able to load the images only when you need to display them.
If you want to save the images with your document then you should investigate using a package format for your document, so that the document is actually a folder containing your images and your archived data file. You can create a file wrapper containing all the files for your document bundle using the various methods of NSFileWrapper and then you would implement the -fileWrapperOfType:error: method of NSDocument in order to handle saving.
This would allow you to store the images unaltered and then lazily load them from the document bundle when required.

Related

Saving an NSDictionary to File, and Creating a New File

I'm trying to discover a way to save an NSDictionary, (which will probably hold high scores and users on a particular system.) onto a new file. I would like to know how to create the file, and put it in the location that it should be, preferably where it can't be touched by the user. Is there any place that these kinds of files are meant to be?
There are many ways to do it.
Either make Plist of your NSDictionary and write to Documents Directory
OR
Make JSON string >> Convert it to NSData using NSJSONSerialization >> Write it to Documents Directory
You can write files in documents directory using NSFileManager
Have a look at this question:
How to save data into the User's Application Support?
The best place to save such files is the "Application Support" folder in the users "Library" folder. The user could go there, but most users won't.
The first answer also has a link on how to save an NSDictionary to a file.
Edit
My answer applies for osx applications only. If you making an app for ios, you should save your file to your app's "Documents" directory.

Processing large video feed on the iPad

I need to take UIImages that are being fed in a video stream, all of this is on the iPad with limited memory, save them to the file system quickly while the stream is still feeding, then process them after a "recording" session. I need to save the UIImages coming in quickly to avoid interrupting the feed which will still be viewing on the iPad. I'm thinking of saving each frame to a separate file then afterward reading these files sequentially and combining them into a .mov file.
The tricks are: how to save the UIImages quickly, maybe raw data, then when processing the movie, append each UIImage file to it to make a seamless movie file? I will need to do some processing of each frame like scaling and transforms before appending.
Any advice would be greatly appreciated.
Depending on how big your images are, you could let the new coredata "use external storage" attribute do this for you.
Here is the explanation what it does copied from another answer of mine:
Since we are on IO5 now, you no longer need to write images to disk neccessarily.
You are now able to set "allow external storage" on an coredata binary attribute. According to apples release notes it means the following:
Small data values like image thumbnails may be efficiently stored in a
database, but large photos or other media are best handled directly by
the file system. You can now specify that the value of a managed
object attribute may be stored as an external record - see
setAllowsExternalBinaryDataStorage: When enabled, Core Data
heuristically decides on a per-value basis if it should save the data
directly in the database or store a URI to a separate file which it
manages for you. You cannot query based on the contents of a binary
data property if you use this option.
There are several advantages using this approach.
First coredate is saving the files at least as fast as you could when writing to the file system. But if there are any small images which apply to the conditions described above, it'll be much faster because they will be saved directly in the coredata sqlite file.
Further with iOS 5 it is very easy possible to work on separate managed contexts and perform changes on a child context in background. If finished successfully you can merge this child context into your main managed object context and do the processing you need.
[child performBlock:^{
[childsave:&parentError]; //do this in background on child context
}];
There is a NSPrivateQueueConcurrentType for creating "child-moc" - see [apple documentation][1]
And at least you can work with coredata objects which enables you to cache, limit and optimize further processing after your download completed
[1]: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdConcurrency.html#//apple_ref/doc/uid/TP40003385 for more info

NSBundle, can it be used to save a project document?

I'm building a Mac OS X application that can be used to create 'projects'. When a user saves a project, they will be saving many resources: image files, text files, sqlite files etc.
I can either create a folder in Documents for each project, and within that folder I can place all the project assets, and just include a single project file that is used to open the project.
I've read about NSBundle which I'd like to use. But I've only read about them in the context of application bundles. Is it possible to use NSBundle in this way? Where the user only sees a single file, and can move it wherever they like.
Does it make sense to do what I'm trying, using NSBundle? Or is there another way to do this?
(I'm fairly new to MacOS X programming)
UPDATE
I believe iPhoto uses this method to store the "iPhoto Library", this is what I'd like to do with my application, is NSBundle what I should be looking into?
Yes, it is possible and was used in quite a few apps in the past. The method is described in this document. Once it's clearly declared in your app's Info.plist, Finder will show the resulting bundle as one file.
However, I can't recommend you to do that. Apple's own Keynote was using the bundle approach in the past, but it no longer uses it any more. Similarly, OmniGraffle (which is a diagramming app on OS X with a long history) used bundles to save projects, but it stopped doing that, too.
The reason is that the bundle is still seen as a directory by the non-Apple email software, or any browser, etc, although the Finder shows it as a file. It would be a mess if a user wants to attach your document in the bundle format to an email s/he is composing in Gmail inside a browser, say. That confused a lot of people.
So, it's possible, but I don't recommend it. One way out is to use the zipped bundle as the user-visible file, and to unzip it when the user opens it into a temporary directory. Then you can use NSBundle and/or NSFileWrapper apis to access files inside it.
Apple's File Wrapper sample seems to be what you want:
http://developer.apple.com/library/mac/#samplecode/PersistentDocumentFileWrappers/Introduction/Intro.html
It also demonstrates how to save a Core Data persistent store in the bundle. You can leave that part out if you just want to store resources.
The NSBundle class represents the application bundle, and can be used to access resources within the applicationm but you would you it for application data, not user data.
For each of the resources you mention, there's a way of saving this type, for example, for image files, you could use NSData to save the image data to disk, and for text files you could use the method writeToFile:atomically:encoding:error:.
You may very well want to take a look at Core Data (http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CoreData/cdProgrammingGuide.html), a very good framework for managing the user's data model, to see if this would fit your needs.

iPad - how should I distribute offline web content for use by a UIWebView in application?

I'm building an application that needs to download web content for offline viewing on an iPad. At present I'm loading some web content from the web for test purposes and displaying this with a UIWebView. Implementing that was simple enough. Now I need to make some modifications to support offline content. Eventually that offline content would be downloaded in user selectable bundles.
As I see it I have a number of options but I may have missed some:
Pack content in a ZIP (or other archive) file and unpack the content when it is downloaded to the iPad.
Put the content in a SQLite database. This seems to require some 3rd party libs like FMDB.
Use Core Data. From what I understand this supports a number of storage formats including SQLite.
Use the filesystem and download each required file individually. OK, not really a bundle but maybe this is the best option?
Considerations/Questions:
What are the storage limitations and performance limitations for each of these methods? And is there an overall storage limit per iPad app?
If I'm going to have the user navigate through the downloaded content, what option is easier to code up?
It would seem like spinning up a local web server would be one of the most efficient ways to handle the runtime aspects of displaying the content. Are there any open source examples of this which load from a bundle like options 1-3?
The other side of this is the content creation and it seems like zipping up the content (option 1) is the simplest from this angle. The other options would appear to require creation of tools to support the content creator.
If you have the control over the content, I'd recommend a mix of both the first and the third option. If the content is created by you (like levels, etc) then simply store it on the server, download a zip and store it locally. Use CoreData to store an Index about the things you've downloaded, like the path of the folder it's stored in and it's name/origin/etc, but not the raw data. Databases are not thought to hold massive amounts of raw content, rather to hold structured data. And even if they can -- I'd not do so.
For your considerations:
Disk space is the only limit I know on the iPad. However, databases tend to get slower if they grow too large. If you barely scan though the data, use the file system directly -- may prove faster and cheaper.
The index in CoreData could store all relevant data. You will have very easy and very quick access. Opening a content will load it from the file system, which is quick, cheap and doesn't strain the index.
Why would you do so? Redirect your WebView to a file:// URL will have the same effect, won't it?
Should be answered by now.
If you don't have control then use the same as above but download each file separately, as suggested in option four. after unzipping both cases are basically the same.
Please get back if you have questions.
You could create a xml file for each bundle, containing the path to each file in the bundle, place it in a folder common to each bundle. When downloading, download and parse the xml first and download each ressource one by one. This will spare you the overhead of zipping and unzipping the content. Create a folder for each bundle locally and recreate the folder structure of the bundle there. This way the content will work online and offline without changes.
With a little effort, you could even keep track of file versions by including version numbers in the xml file for each ressource, so if your content has been partially updated only the files with changed version numbers have to be downloaded again.

Recommended document structure. File Wrappers? Roll my own?

I'm currently working out the best structure for a document I'm trying to create. The document is basically a core data document that uses sqlite as its store, but uses the Apple provided NSPersistentDocument+FileWrapperSupport to enable file wrapper support.
The document makes heavy use of media, such as images, videos, audio files, etc. with potentially 1000s of files. So what I'm trying to do is create a structure similar to the following:
/myfile.ext/
/myfile.ext/store.sqlite
/myfile.ext/content/
/myfile.ext/content/images/*
/myfile.ext/content/videos/*
/myfile.ext/content/audio/*
Now, first of all I went down the route of creating a temporary directory and placing all of my media in there. Basically creating the paths and file names '/content/images/image1.jpg' as I wanted them to appear in the saved file wrapper, and then upon save I attempted to copy these all into the filewrapper...
What I found was that the files were indeed copied into the wrapper with the file structure I wanted, but when the actual wrapper was saved, these files all magically disappeared.
Great.
So, I trashed my existing solution and tried to use file wrappers instead. This solution involved creating a content wrapper file directory when a new document was created, or loading in a content directory file wrapper upon opening a document.
When an image was added/modified, I created the necessary directory wrappers inside this root content wrapper (i.e. an images directory wrapper if it didn't already exist, or any other intermediary directory wrappers that needed to be created) and then created a regular file wrapper for the media, removing any existing wrapper for that file name if one was there.
Saving the document was just a case of making sure the content file wrapper was added to the document file wrapper, and the document would save.
Well... it did. The first time. However, any attempts to make any subsequent changes i.e add an image, save. Then replace image, save. Did not behave as expected, only showing the image from the first save.
So, my question is... first of all, which of the above approaches is the correct one, if at all, and what am I doing that wrong for them to fail.
And secondly, as I expect to be managing 1000s of images, is using file wrappers the correct way to go about things at all.
With that much media in play, you should likely give your users control over whether the media resides in the document or only a reference to the media is included in the document, and the media resides elsewhere, such as in a library/repository managed by your application. Then they could save out a (potentially many times larger) copy with all references resolved.
You might want to zip/unzip any directory so that users don't get confused trying to attach the document to an email. I believe iWork has been doing this with its document bundles for a while now.
As far as what you are doing wrong, no-one can say, as you haven't provided any code demonstrating what you are doing.
Why don't you create a one-off application that lets you select files on disk and saves those files in a document using a file wrapper? This would let you tackle this functionality without any interference from other issues in your application. Once you understand how to use file wrappers, you can port the code back or just write new code that works.