12

I have a Cocoa/Objective-C application which embeds a WebKit WebView. I need to turn on database support and local storage. I know it can be done--I have it working in Safari--but I can't find an example of how to set this up in my own application.

I found this (unanswered) SO question which provides an example but, as the original poster mentions, doesn't work. And in fact, the methods he uses (setDatabasesEnabled, setLocalStorageEnabled) aren't defined in my WebKit.framework (Xcode 3.2.5), although they appear to exist if I define them myself.

Can anyone provide an example of how to enable local database storage for a WebKit-based Cocoa application? Many thanks if so!

Update: I've got something working...I was confused by "databases" vs. "local storage", which are apparently quite different things. Here's the code that works:

WebPreferences* prefs = [webView preferences];
[prefs _setLocalStorageDatabasePath:@"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];

So that works, but it requires the private method _setLocalStorageDatabasePath, which means no App Store for me. So my amended questions is now: is there a way to make this work without using a private method? I found the WebDatabaseDirectory preference key in this answer, which controls where databases go. But I couldn't find a corresponding key for local storage anywhere in the sources. Or is there a way for me to force local storage to use the database, and so the WebDatabaseDirectory key? Any ideas?

Community
  • 1
  • 1
J. Perkins
  • 4,156
  • 2
  • 34
  • 48

3 Answers3

11

I submitted an app using this code to the Mac App Store, and Apple approved it:

Objective-C

WebPreferences* prefs = [webView preferences];
[prefs _setLocalStorageDatabasePath:@"~/Library/Application Support/MyApp"];
[prefs setLocalStorageEnabled:YES];

Swift 3

var prefs: WebPreferences? = webView.preferences
prefs?._setLocalStorageDatabasePath("~/Library/Application Support/MyApp")
prefs?.localStorageEnabled = true

Whether they will continue to approve that, I don't know, but they allowed it for my application as of 2011-01-29. Update: They also approved a version update to the same app, so it has gone through twice.

Fizzix
  • 23,679
  • 38
  • 110
  • 176
Marcus Cavanaugh
  • 374
  • 4
  • 12
  • Finally managed to clear the final set of hoops, and my app is now in the store as well. – J. Perkins Apr 05 '11 at 11:07
  • 4
    where exactly do you put that? @implementation? .h? clarification appreciated.. sorry for stupidity... – Alex Gray Aug 17 '11 at 02:03
  • Generally, Apple will approve most WebKit private methods because WebKit itself is open source. – Rob Keniger Dec 28 '11 at 23:51
  • 2
    This question was last active almost a year ago, so Apple may have removed support for the `_setLocalStorageDatabasePath:` and `setLocalStorageEnabled:` methods because I can't find a way to use them anywhere, but if you are aware that the support is still out there, how would I get support for these methods into my code so that I can use them? – Carter Pape May 19 '12 at 13:43
  • How do you do it in Swift? – Juanjo Conti Jun 03 '15 at 15:04
4

I'm going to take the Javascript to Objective-C bridge approach and store everything in core data. Set localStorage to false, then build a JS object and an instance named "localStorage" with the same methods. My javascript devs won't know the difference, and I already had to do the same thing with Air (basically). There's another way to leave the localStorage intact even though it doesn't actually store them in a persistent db. The elements can be iterated through in javascript and manipulated from there, but I think it will be better to simply replace the object with my own.

tbranch
  • 41
  • 3
3

After a lot of pain and frustration I found a way to enable local storage and have it persist across application runs properly. This solution is specifically for OSX, but it may be applicable to iOS as well.

Download and add this header file into your project. It's not included in the XCode Webkit distribution.

click to download WebStorageManagerPrivate.h

Add to it, the following lines:

static NSString* _storageDirectoryPath();
+ (NSString *)_storageDirectoryPath;

These allow you to retrieve the directory location of the WebKit local storage tracker database. This is important because due to a bug in WebKit, if you don't store your LocalStorage WebView files in the same directory as the tracker database, they are deleted every other time you run your application. I didn't see a way in the WebStorageManager code to change this location for an individual application. It is always read from the user preferences.

Include the WebStorageManagerPrivate.h in your appDelegate.

#include "WebStorageManagerPrivate.h"

You need to download and include in your project another header not included in XCode distribution. Save it as WebPreferencesPrivate.h

click to download WebPreferencesPrivate.h

Include the WebPreferencesPrivate.h in your appDelegate.

#include "WebPreferencesPrivate.h"

Now use the code below in your applicationDidFinishLaunching handler to initialize and enable LocalStorage. The code assumes you have an IBOutlet named 'webView' for the WebView you are using.

    NSString* dbPath = [WebStorageManager _storageDirectoryPath];

    WebPreferences* prefs = [self.webView preferences];
    NSString* localDBPath = [prefs _localStorageDatabasePath];

        // PATHS MUST MATCH!!!!  otherwise localstorage file is erased when starting program
    if( [localDBPath isEqualToString:dbPath] == NO) {
        [prefs setAutosaves:YES];  //SET PREFS AUTOSAVE FIRST otherwise settings aren't saved.
        // Define application cache quota
        static const unsigned long long defaultTotalQuota = 10 * 1024 * 1024; // 10MB
        static const unsigned long long defaultOriginQuota = 5 * 1024 * 1024; // 5MB
        [prefs setApplicationCacheTotalQuota:defaultTotalQuota];
        [prefs setApplicationCacheDefaultOriginQuota:defaultOriginQuota];

        [prefs setWebGLEnabled:YES];
        [prefs setOfflineWebApplicationCacheEnabled:YES];

        [prefs setDatabasesEnabled:YES];
        [prefs setDeveloperExtrasEnabled:[[NSUserDefaults standardUserDefaults] boolForKey: @"developer"]];
#ifdef DEBUG
        [prefs setDeveloperExtrasEnabled:YES];
#endif
        [prefs _setLocalStorageDatabasePath:dbPath];
        [prefs setLocalStorageEnabled:YES];

        [self.webView setPreferences:prefs];
    }

I hope this helps others have struggled or are still struggling with this issue, until it is fixed properly within WebKit.

Derek Wade
  • 697
  • 8
  • 11
  • What issue are you experiencing? error? not saving? I have tested it under 10.7 fine. – Derek Wade Jul 02 '14 at 20:33
  • Under 10.11 (El Capitan) this no longer works. I had to resort to bridging to native code for save and load operations. To minimize the changing of Javascript, you can override the standard "set" and "get" functions in the Storage.prototype and call your bridge functions. Apple closed the gap in their security handling of storing localStorage for Webkit apps using local file references. – Derek Wade Mar 30 '16 at 21:52