iPhone & SQLite - Lost Database Connection? - iphone

I've had this weird bug in my application for a while, maybe you guys can help me.
Here's the given:
The application downloads (a lot of) images and stores them in the Library/Caches directory.
For every image downloaded, the url and filename is stored in an SQLite database. As you can see, I've re-invented the wheel and created my own "caching" system. I've done this to help reduce the memory footprint of the application when it has to download a lot of images.
I have a UITableViewController that displays some text data, as well as (some of) the downloaded images, in its cells. The filename is queried from the database everytime the following is called:
- (UITableViewCell*) tableView: (UITableView*) tableView
cellForRowAtIndexPath: (NSIndexPath*) indexPath
The queries are of the form:
SELECT filename, imageTitle, etcetera
FROM tbl_images
WHERE url = 'http://www.foo.com/bar'
Here comes the problem:
When scrolling up or down really fast on a tableView, thereby causing a lot of queries to be executed in a short amount of time, the app "breaks" and the queries return empty rows, and never works correctly again unless you restart it. I'm guessing the database connection is lost somehow or something, but I can't really be sure.
Anyone have an idea on what is happening and/or how to fix it?
EDIT
OK guys, I just found the cause of the bug. When it is reproduced, I found that the database has been corrupted somehow.
Thanks for the comments, though, and sorry for wasting your time. v^_^'

Well you can load the items from the data base into a datasource, may be a array of arrays. Each subarray will have the information for a particular row. Whenever new file is downloaded just update the file path. You can then store the images to disk and read from the file path. If the data is NULL you can show a activity indicator untill it gets downloaded.
In your downloading method, you can pass a value which indicates which row the image should fit into. When download completes you can update the filepath. Doing so you will reduce/remove the need to cache the images in a database.

Did you check return codes from sqlite3_prepare_v2? Also you may cache prepared statement to make it very fast on population to database. Did you open database connection each time when requesting this information?
Without code for working with database it's hard to tell why you get such picture.
You can use "lazy" method to represent data. For example in UIScrollViewDelegate *scrollViewDidEndDragging:willDecelerate:* and scrollViewDidEndScrollingAnimation: methods call for data from database and fill rows w/o data (or with some minimal data)

Related

Saving screenshot to CoreData

I'm developing an app that I want the user to be able to take a screenshot with a button (this works, but it's stored to camera roll). I have a need to keep history of these screenshots along with site name, etc.
I've been reading that it's not recommended to save binary data in a sqlite db, so I've stumbled across core data. I'm still learning, but one question that comes to mind is this. Some recommend to save the filename to coredata and then save the image to document directory. I want to read the data back into a table view controller and have the image part of the cell. Will I need to resize the image to the size I want when reading in, or will it automatically size down to what it needs?
Also, when saving to document directory, are those files accessible from the camera roll? I don't want them to be and I'd like to give the user the ability to be able to delete them with the tableviewcontroller.
Please let me know if I need to expand on anything. I'm learning Swift, so I'm sure I missed something.
You should not save it to the Documents directory.
You should instead Allow External Storage of the attribute. Core Data will take care of storing it for you. This is explained in the answer by jansenmaarten to this question.

Multiple Image storing and dealing with table view

Despite there are a lot of themes around this topic, I seem very frustrated in the following situation :(
The app downloads and parses a JSON with about 1000 objects, which have in their attributes link for a small image. I need to download (once) these images, store them and use in my TableView as icons.
What is the best way to store such amount of files and to work efficiently with them while displaying the TableView?
I will tell you how will I do it but before I would like to give you a warning about downloading files from the Internet and storing them in your app.
I got one of my apps rejected from Apple lately because I violated rule 2.23 from the App Store Review Guidelines which says:
2.23 Apps must follow the iOS Data Storage Guidelines
or they will be rejected
Since Apple is using iCloud lately you should minimize the traffic that each app will upload and therefore you should not have more than 40MB stored in your Documents folder of your app. They also say (if you read the links I provided):
Data that can be downloaded again or regenerated should be stored in
the /Library/Caches directory.
And since the cache folder can be deleted by the iOS if needed, you should design your app to be prepared to download the images again in case they are gone, so keep this in mind.
Now for my solution, I think downloading 1000 files is too much (even if they are small files). I suggest that you have the files saved in a ZIP file on the server from which you are willing to download and unzip them on your disc once downloaded. This will be much easier and more practical. You can look here to see how to zip and unzip in your app.
After you have your files unzipped I suggest to have a small database (SQLite) from which you can load load the file names, store these names in an array, and then use this array to fill the images in your table in the function cellForRowAtIndexPath.
I hope this helps you. By the way, it is my way to do it but I am not saying it is the "best way" as you are asking :)
Given that you would like to keep these images/data persistent, you now have two options: You can manage them yourself or you can utilize Core Data to help you do it. I recommend Core Data.
If your not familiar with Core Data, you can start reading about it Here. It's a little bit daunting to get started, but once you've run through a tutorial it's extremely straightforward and will make your life much easier.
If you would like store the images yourself, or would like a more efficient Core Data structure, I recommend writing all of your images into your Documents directory as see Here. Then you can just store File path strings in your CD structure or manually in your plist like This.
And finally, for loading them into your TableView you can use those stored image paths to load UIImages with:
[[UIImage alloc] initWithContentsOfFile:filePath];
This would be placed within your:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
function so that you can set the image where you would like.

Where to cache remote image files in iOS?

I am planning to cache the images from a server and use show it as a sort slide show in my App. I would be asynchronously loading the images.
I have two options:
Either to cache the images as a File and use it whenever necessary.
Cache the images objects in memory and use it when ever necessary and write it in to files when Application quits.
Which one would be better?
Please let me know if you you have any kind of suggestions regarding caching images.
Your second approach has 2 major flaws:
If there's too many images then your application will get low memory warning and you'll have to dispose your images from memory anyway
It's also not a good idea to save all images to file on application quit - it is not guaranteed that your saving code will finish on application exit (e.g. if it takes too long system may just terminate your app and your images will be lost)
I'd suggest saving images to files right after you download them and keep in memory reasonable number of images you need to show without visible delay (loading extra images when required and disposing of unnecessary ones)
I would recommend you the first option. Leaves you more flexibility, e.g. when the data size increases the memory size.
I'd do it like this: Have a NSMutableDictionary with the cached images (as UIImage objects). If the image is not in the cache, look whether it's available as a file. If it's not available as a file, load it, put it into your dictionary and also write it to a file.
As for where to write the files to: you can either use the NSTemporaryDirectory() or create a directory inside your NSLibraryDirectory (use NSSearchPathForDirectoriesInDomains to locate it). The later has the advantage/disadvantage that it will be in the iTunes backup (whether that's an advantage or not depends on the use case). Using the Library directory is Apple's recommended way of storing data that is backed up but does not appear in the iTune's file exchange thingy (Documents directory).
I have started using EGOImageView to handle my caching; it's very versatile and handles the intricacies of caching for you.
It works very well for pulling images via http, you can find it on the EGO developer website here
http://developers.enormego.com/
For image caching solution on iOS platform, you might want to consider SDWebImage framework available at: https://github.com/rs/SDWebImage. It is very easy to integrate and takes care of all your image caching worries.: read more about the working here: https://github.com/rs/SDWebImage#readme
We recently picked this up for our app and it works great.

How often should I save to the file?

I'm developing a simple text editor for iPad.
Instead of Core Data, the app just saves its content to *.txt file in the document folder by calling writeToFile function of NSString. In this way, users can easily transfer files via iTunes. Please, advise me if this approach is bad or inefficient!
How often should the program save its content to the text file? The iOS human guide says that the app should save it's content "frequently" but it doesen't give specific seconds.
I read somewhere that the app should save its content every two seconds. If this is correct, do I just need to make one function and call it using NSTimer with repetition mode?
Thank you!
There's little point in saving "frequently" unless you actually expect to crash. iOS gives you notification of potential app shutdown situations, so I'd just save upon: 1) Receiving a memory warning 2) Termination 3) Moving to background and 4) Whenever the user asks for it (be that via a button, or by navigating away from the editing view). Any saving beyond that is really a waste of resources, as the actual editor isn't going anywhere, short of crashing out.
The writeToFile method of NSString is fine for plaintext and assuming you've got your data as an NSString already. If your data isn't already inside an NSString, then there are more efficient ways of saving it than converting to an NSString and calling writeToFile (output streams, for example). Also, if you're using very large files, you'll probably want a more incremental approach (i.e. saving from just the point of change to the end). The odds of anyone editing a file large enough on the iPad to need this though, are slim.

How to programmatically fill a database

I currently have an iPhone app that reads data from an external XML file at start-up, and then writes this data to the database (it only reads/writes data that the user's app has not seen before, though)
My concern is that there is going to be a back catalogue of data of several years, and that the first time the user runs the app it will have to read this in and be atrociously slow.
Our proposed solution is to include this data "pre-built" into the applications database, so that it doesn't have to load in the archival data on first-load - that is already in the app when they purchase it.
My question is whether there is a way to automatically populate this data with data from, say, an XML file or something. The database is in SQLite. I would populate it by hand, but obviously this will take a very long time, so I was just wondering if anybody had a more...programmatic solution...
I'm going to flesh out Jason's answer, I've marked my post as a community wiki so I shouldn't get any points for this.
He's not talking about a dummy app - write the app as you normally would, but check to see if the database exists in your main bundle before you call the code that populates the plist. You run that in the simulator, pull out the generated sqllite database, and add it to your project - if you only need to read from it, you can read it from the main bundle directory. If you need to do further writes then copy it into the writable documents area, and use it from there. So basically for the main user, the code to populate the DB would never be called...
The only downside is you also end up including the plist files you are reading from, even though you only need the database. You could make a different build target that was a copy of the main one with the only difference being that it held the plist files, while the main target you built for the app store did not.
Not to take Jason's answering thunder, I can't comment yet so it has to be here.
The nice thing is that you can access the filesystem of the simulator right on your Mac. I am away from mine at the moment or I could tell you exactly where to look, but I just find it by putting the name of the db file into searchlight and just running with that.
You also do not need to wright any code to populate the db since you can use the command line tool to do the initial setup if that is more convenient.
You will need to copy it over though since resources are stored in the read only signed portion of the app bundle.
I had the same problem of you using sqlite, on massive insert it's really slow. So the best way it's provide directly a filled sqlite database.
You have another way, instead of INSERT INTO, to populate a sqlite db. You can produce a csv file for each table and load into the tables using your computer and the sqlite shell:
Just 2 simple commands:
.separator SEPARATOR
.import FILE TABLE
Example:
adslol:~ user$ sqlite3
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .separator ;
sqlite> .import myData.csv nameOfMyTable
sqlite> .quit
I hope it's what you was looking for :)
If you need a good client for sqlite3 try SQLite Manager, it's a Firefox add-ons.