10
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

[prefs setObject:self.webView forKey:@"webView"];

The code above is made to save a WKWebView object but I'm getting a compiler error:

2014-11-08 14:58:52.561 Restoration[2431:482391] Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType') 2014-11-08 14:58:52.564 Restoration[2431:482391] Attempt to set a non-property-list object > as an NSUserDefaults/CFPreferences value for key webView 2014-11-08 14:58:52.566 Restoration[2431:482391] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object > for key webView' * First throw call stack: (0x2c491c1f 0x39c78c8b 0x2c491b65 0x2c4cbe51 0x2c436599 0x2c4358d7 0x2c4cc0d1 0x2c4cb769 0x2c4ce95b 0x2c4ce875 0x2d0eab69 0x66bdf 0x2f981c2b 0x2f981bd1 0x2f96c863 0x2f98163d 0x2f981317 0x2f97abe1 0x2f9513dd 0x2fbc4c29 0x2f94fe39 0x2c458377 0x2c457787 0x2c455ded 0x2c3a4211 0x2c3a4023 0x3379d0a9 0x2f9b01d1 0x66ead 0x3a1f8aaf) libc++abi.dylib: terminating with uncaught exception of type NSException

How can I save the WKWebView and restore it with its history intact?

EDIT:

8 months have passed and there's no solution to this problem...

EDIT 2:

I'm making a web browser and it is crucial when the user restarts the app the web views (tabs) to restore so he can press the back button and go back.

If I save the previous URLs in a simple list I can't put them back into the web view when the app restarts. I can only load the first page. So, the user can't press back because there is no page to back. If it was as simple as saving a list of URLs I wouldn't start a bounty. Since, a web browser is a very complex app I've been very close to the fundamentals of the webViews and I have even submitted 3 bugs from which only 2 have been resolved. Including the infamous screenshot bug.

The standardUserDefaults method does work with UIWebView but Apple states that all developers should use the WKWebView following the release of iOS 7. I'm expecting UIWebView to be deprecated in the near future.

Community
  • 1
  • 1
Vulkan
  • 1,004
  • 16
  • 44
  • 3
    That's because there's no **obvious way** to serialize a web view object. How would you store a view in a file? Serialization is for **data.** Serializable, structured data. Not for arbitrary user interface objects. – The Paramagnetic Croissant Nov 08 '14 at 13:24
  • What behaviour are you expecting when you restore the webview? If you're wanting it to simply restore the current and past page URLs then you can instead save those into the NSUserDefaults as NSStrings – SomeGuy Jul 17 '15 at 00:48

4 Answers4

20

There is no easy answer here. The WKWebView cannot be archived and it does also not participate in UI state preservation/restoration. You already discovered this.

For Firefox for iOS we took a different route to work around these limitations. This far from ideal but it does work.

When we restore a tab that has session info (previously visited pages) attached, we load a special html page from a local web server (running on localhost) that modifies the push state of the page and pushes previously visited URLs on the history stack.

Because these URLs need to be of same origin, we basically push urls like `http://localhost:1234/history/item?url=http://original.url.com

In our UI we translate this to display original.url.com. When the user goes back in history to load these, we intercept and instead of loading the page from localhost, we load the original page.

It is a big hack but that is all we have at this point.

See the source code at https://github.com/mozilla/firefox-ios

Stefan Arentz
  • 34,311
  • 8
  • 67
  • 88
  • I've thought of that before but I think it's way too complicated for an event that rarely happens. I have never opened a browser and click the "back" button. Who does that? Either you browse your last page, a page from your last session or go to a new page altogether. "Modern" web browsers have a crippled navigation system in favour of easily recyclable tabs. – Vulkan Jul 21 '15 at 12:19
  • 2
    @user3737190 Agreed .. but remember the OS will also suspend/resume your application when needed. So it may not be an event that rarely happens .. it could also happen when the user quickly switches back and forth between browsing and looking something up in another app for example. – Stefan Arentz Jul 22 '15 at 13:25
  • What about the forward pages? Do you simply ignore this? – Vulkan May 15 '16 at 15:03
  • What about the time it takes for a URL to be pushed to the history stack? What is really special about the html page loaded from the local web server? – Vulkan May 15 '16 at 15:13
  • 1
    I'm actually using Firefox for iOS and it DOES NOT restore the state of the tab. All previous pages are lost. – Vulkan May 15 '16 at 16:26
8

you can only store certain types of objects in NSUserDefaults, namely

NSArray
NSData
NSDate
NSDictionary
NSNumber
NSString

(you cant bypass this by putting your webview inside an NSArray)

what you could do is store relevant information about the webview in an NSDictionary that would allow you to be able to get your webview back into its current state when loading from the NSUserDefaults

you can refer to the documentation for more details

Fonix
  • 11,447
  • 3
  • 45
  • 74
  • This doesn't quite completely answer the question. You might explain how one could save and then restore the webview. – nhgrif Nov 08 '14 at 13:24
  • Aside from this is not appropriate for NSUserDefaults the answer is correct. You should save non plist data to a file normally. – uchuugaka Nov 13 '14 at 08:38
4

As @fonix mentioned, you can only store a select few data types in NSUserDefault.

If you just wanted the url, it would be pretty easy to convert that to a string and save it to NSUserDefaults.

However, the crux of the question is how to "restore the WKWebview with its history intact."

Since, to 'save' the web view, you want not only the current URL being shown, but also the history, you need access the backForwardList on WKWebview.

    NSMutableArray *history = [NSMutableArray array];
    for(WKBackForwardListItem *item in webview.backForwardList.backList){
       // use -absoluteString to convert NSURL to NSString
       [history addObject:[item.url absoluteString]];
    }

   [[NSUserDefaults standardUserDefaults] addObject:history forKey:@"webviewHistory"];
   [[NSUserDefaults standardUserDefaults] synchronize];

Saving those URLS into an NSArray, will allow you to save them to NSUserDefaults.


Then when you want to 'recreate' the webview, iterate through this array and request each url to simulate the history.

Hope this helps!

Community
  • 1
  • 1
Matt
  • 1,359
  • 18
  • 22
3

In case you really want the webview from user default, here is a choice.

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:[NSKeyedArchiver archivedDataWithRootObject:self.webView] forKey:@"webView"];

// get it back from user default
NSData *data = [prefs objectForKey:@"webView"];
self.webView = [NSKeyedUnarchiver unarchiveObjectWithData:data];
gabbler
  • 13,626
  • 4
  • 32
  • 44
  • You got me here, after I got it, it was no longer subview of self.view. I reset its properties such as delegate and add it as a subview of the self.view, it was working for me,I loaded a video and it was played, however it was a test on UIWebView. – gabbler Nov 08 '14 at 14:08
  • 1
    Yeah, it worked for the UIWebView as you described but it seems that WKWebView can't be restored. – Vulkan Nov 08 '14 at 14:13
  • 2
    WKWebView is new stuff, apple will make it better in the future. – gabbler Nov 08 '14 at 14:15
  • 1
    But, but... I need it today :( – Vulkan Nov 08 '14 at 14:27
  • It's 2023 and WKWebView is still terribly bad @Vulkan :P – AnonBird Jul 26 '23 at 10:11