UIWebView cache in iOS - ios

I'm hosting UIWebView in my app. it looks like UIWebView caches images and data itself.
I want to clear its cache on app startup.
Cleaning up Safari's cache doesn't help. the only way I found to flush UIWebView's cache is to power off my iPhone and turn it again. Closing the app doesn't help either.
Apple documentation says nothing about it...or I'm missing something. Just in case, app is created with monotouch.

If you want to obliterate all cached responses, something like this looks like the way to go:
[[NSURLCache sharedURLCache] removeAllCachedResponses];

There is a view cache that shows a bitmap of the last page used (like we see inside Safari) but that does not looks like what you're seeing (since it requires to reboot the device).
I've not noticed this behaviour before (never looked for it either ;-) but the following answer looks promising.
FWIW this is not something that would be specific to MonoTouch.

Ive tried all the suggestions on stack overflow and none of them work. The only way I got it to work and feel that its a reliable solution is to create new html files on a temp directory (with a different directory name - a Guid works best) every time, and copy all the related images, scripts, css, every time to that temp directory.
Then open it using an NSUrlRequest object
string tempdir = Path.Combine(UIController.Common.DataFolder,System.Guid.NewGuid().ToString ());
Directory.CreateDirectory (tempdir);
//-- create your html on the tempdirectory here
//-- copy all the images, and all the css, and js files
UIWebView wv = new UIWebView(new RectangleF(30,30,480,680));
NSUrlRequest req = new NSUrlRequest(new NSUrl (Path.Combine (tempdir,"default.html"), false),NSUrlRequestCachePolicy.ReloadRevalidatingCacheData,10);
wv.LoadFinished += delegate(object sender1, EventArgs e1)
{
//delete the tempdirectory
Directory.Delete(tempdir);
};

Related

How to copy from Storage to FileSystemStorage in Codenameone and display in BrowserComponent

I've been reading a lot of StackOverflow posts that discuss copying data from FileSystemStorage to Storage in CodenameOne, such as described in this answer from Shai, as seen below:
InputStream stream =
FileSystemStorage.getInstance().openInputStream(i);
OutputStream out =
Storage.getInstance().createOutputStream("MyImage");
Util.copy(stream, out);
Util.cleanup(stream);
Util.cleanup(out);`
I've been trying to do the reverse: save from Storage to FileSystemStorage in order to show a PDF in the BrowserComponent (while using iOS), but have not been able to do so. I need to show the PDF within the app (so I don't want to use Display.getInstance().execute()).
Basically, I'm trying to dynamically populate a Container with whatever files the user selects-- I am using the FileChooser library for CN1 from Steve Hannah. (Disclaimer: I have made slight modifications to this library as it used in the app I'm working on-- HOWEVER, when I choose images with this library and pull them from Storage to an Image via InputStream, they display perfectly in an ImageViewer so I know that all files are being saved correctly in Storage.)
Here is my code (with help from Steve Hannah's comment on GitHub):
//fileLocation and fileName are slightly different but both end with file extension
File file = new File(fileToUpload.getFileName());
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = Storage.getInstance().createInputStream(fileToUpload.getLocation());
OutputStream os = fss.openOutputStream(file.getAbsolutePath());
Util.copy(is, os);
ToastBar.Status status = ToastBar.getInstance().createStatus();
String message = file.exists() + " " + file.isFile() + file.getAbsolutePath();
status.setMessage(message);
status.setExpires(3000);
status.show();
NativeLogs.getNativeLogs();
if (Display.getInstance().getPlatformName().equals("ios")) {
//Log.p("in ios !!!!");
BrowserComponent browserComponent = new BrowserComponent();
browserComponent.setURL(file.getPath());
horizontalContainer.add(browserComponent);
}
The ToastBar displays true and true for file.exists() and file.isFile().
I stipulate iOS because as far as I've seen while researching previewing PDFs within an app, I've seen that Android needs to have a different implementation, like adding a NativeInterface with an Android library. I also saw in different answers on the Google Group that this functionality (using browserComponent to view PDFs) is only available for iOS and not on the simulator. In the simulator, I see a blank space. My iPhone just freezes and/or crashes after displaying the ToastBar (and I work on a Windows machine, so not much ability to see native logs....)
What can I do to access the file and show it in the BrowserComponent?
Thank you!
Simple solution -- the file had a space in it (eg. "Test page.pdf") and didn't show! When I used files that didn't have spaces this worked and after removing spaces in the file names, thankfully everything worked. I'll have to add code to handle this scenario.
Thanks for your help!

Loading local images into WKWebView

I'm trying to get WKWebView to display locally downloaded images in a WKWebView. The webview normally displays HTML, which is retrieved remotely. The contents of the HTML can sometimes contain remote links to images. My app parses the HTML and looks for these HTML tags, downloads the file it is referencing and subsequently replaces the remote link with a local one.
Normally speaking, this wouldn't be very difficult but the images aren't being displayed, presumably due to the images and the local HTML files for the webview being in two separate directories (the documents directory and the app bundle directory respectively).
I've seen people suggest moving the download destination of the images to the same directory as where the HTML files are but this isn't an option for me as I don't want to start mixing up files downloaded by the user with local assets.
What would be my best course of action here?
Well, I've found a workaround. Instead of locally storing the images and referencing them in the HTML files, I'm now instead converting the images to Base64 and then adding them to the HTML. It's not ideal but it gets the job done. I'm going to leave this question open in case someone ever manages to find an actual solution.
To display cached HTML referencing cached resources in a WKWebView:
For each of the resources within your HTML content string, cache it into the directory as provided by NSTemporaryDirectory(). So an image tag like:
...<img src='https://www.myimage.com/example_image.png'/>...
should be cached and replaced into something like this:
...<img src='/private/var/mobile/Containers/Data/Application/527CF4FC-9319-4DFF-AB55-9E276890F5DC/tmp/example_image.png'/>...
Now cache the HTML content string with the replaced resource URLs. It must also be cached in the directory provided by NSTemporaryDirectory(). One difference here is that it must be cached (and later referenced) using the file:// protocol as a restriction of caching the string using NSData (see sample code).
For example file:///private/var/mobile/Containers/Data/Application/527CF4FC-9319-4DFF-AB55-9E276890F5DC/tmp/my_html_content_string.html
A few things to point out:
You cannot load the HTML as a raw string (loadHTMLString:baseURL:).
You cannot reference the cached resource within your HTML string using the file:// protocol. That may work in a UIWebView, but will not work in the WKWebView.
Objective-C
// To cache the HTML string:
NSString *HTML = <HTML CONTENT WITH CACHED RESOURCES>;
NSData *data = [HTML dataUsingEncoding: NSUTF8StringEncoding];
[data writeToURL: cachedHTMLURL atomically: YES];
// To load the store HTML file:
[myWKWebView loadRequest: [NSURLRequest requestWithURL: cachedHTMLURL]]; // (file://.../tmp/my_html_content_string.html)
Swift
// To cache the HTML string:
let HTML = <HTML CONTENT WITH CACHED RESOURCES>
let data = HTML.data(using: String.Encoding.utf8)
do {
try data.write(to: cachedHTMLURL, options: .atomic)
} catch {
print(error)
}
// To load the store HTML file:
myWKWebView.load(URLRequest(url: cachedHTMLURL)) // (file://.../tmp/my_html_content_string.html)
I had the same problem with WKWebView as it can not load both html strings and images at the same time for security purposes. I switched to UIWebView, which is deprecated, but I was able to load both html strings and referenced images at the same time.
I developed a definitive solution for the company I work for. But it relies on the html / javascript side. Anywhere inside your html code where you will reference to a local image <img src="..."/> you should set this "src" dynamically, and it will work seamlessly.
function getLocalURL(path) {
let origin = window.location.origin
if (origin == "file://") {
return origin + window.location.pathname.replace("/index.html","") + path
}
return path
}
You should, clearly, rename index.html to whatever is your main .htm(l) filename :)
Usage:
getLocalURL("/local_images/location_icon.png")
Will return a WKWebView working path for the referenced local image path:
"file:///Users/arthurdapaz/Library/Developer/CoreSimulator/Devices/5073AF19-26A0-460E-BC82-E89100B8E1AB/data/Containers/Data/Application/2B099343-0BF5-4849-B1C2-2512377A9772/Documents/distDriver/local_images/location_icon.png"

IOS Memory buildup when changing IFRAME src with angular/Embedding youtube in cordova?

I'm afraid im having the same probem as this unasnwered question.
I have a blank IFRAME In my cordova ionic IOS App for embedded YouTube videos.
I change the NG-SRC="" of iframe by clicking Next Video. But upon testing it appears that the phone/cordova/webview is caching the old content of the iframes. I can only get through 20 videos or so before crashing out of memory.
I have tried using angular.element.remove() to remove the iframe as well as setting the iframe src to blank first, and neither seemed to affect how much memory is in use, per Xcode. I've also tried the cordova plugin ClearCache and that didnt clear any memory either.
Please help! Is there a better way to embed youtube in a cordova app?
I have spent weeks working on this all to have it crashing down around me (no pun intended)
My Video view is like:
<ion-view view-title="Random Video">
<iframe id="youtube" sandbox="allow-scripts allow-same-origin" ng-src="{{video.url | trustAsResourceUrl}}" frameborder="0" allowfullscreen></iframe>
<h4>{{video.title}}</h4>
<button ng-click="nextVideo()">
</ion-view>
My controller is like:
angular.module('starter.controllers')
.controller('VideoCtrl', function(VideoService) {
$scope.video = {};
$scope.nextVideo = function() {
$scope.video = null; //doesnt seem to help
//$scope.$destroy(); //doesn't help
//angular.element(document.querySelector( '#youtube' )).attr("src", " ");
//angular.element(document.querySelector( '#youtube' )).remove();
//neither of the above 2 remove any memory
VideoService.getVideo().then(function(response){
$scope.video = response.data;
});
}
$scope.nextVideo();
});
Note, when I load my app onto a website instead, and load in chrome, I can cycle through videos without seeing the memory usage go up (looking at taskmgr.exe at least)
It might seem like setting the iframe to an empty string should be enough, but for some browsers and some situations it isn't. It might be necessary to recursively delete event listeners and elements one by one. Maybe surprisingly, the recursive method (1) below is faster than just setting to an empty string (2):
1.Recursive
while (box.lastChild) {
box.removeChild(box.lastChild);
}
2. Setting empty string
myNode.innerHTML = '';
See https://stackoverflow.com/a/3955238/1158376 for reference.
Additionally, in the recursive approach, one might need to apply special treatment to some items, for example first remove event listeners, nullify functions (http://javascript.crockford.com/memory/leak.html), and use dedicated cleanup methods, like with jQuery (http://javascript.info/tutorial/memory-leaks).
Another strategy you could try is to load a new web page with a fresh iframe for every video you play. Loading a new page should enable the browser to release the previously claimed memory.

Paste a copied image into an AIR iOS app?

Is it possible to paste a copied image from the Safari browser to an Adobe AIR built iOS app?
I tried it with this image but couldn't find a solution yet.
Below is a screenshot of the copy button from the Safari browser on iOS 8:
Yes and no. You can't use the native dialogs or handling, but I believe you can access the data in the clipboard, even if it is an image.
You'll want to use Clipboard#getData() with a format of ClipboardFormats.BITMAP_FORMAT. That will give you access to the raw image data in a BitmapData object. You can then draw that BitmapData to a Bitmap container (or Image/BitmapImage if using Flex). You'll have to include a separate button for instantiating the paste process, but that isn't difficult to do.
Untested, but this should be a good place to start:
pasteButton.addEventListener(MouseEvent.CLICK, this.pasteEventHandler);
function(e:MouseEvent):void {
var clipboard:Clipboard = new Clipboard()
var bd:BitmapData = clipboard.getData(ClipboardFormats.BITMAP_FORMAT);
bitmap.bitmapData = bd;
}
I believe you'll need to handle errors in there as well, since the clipboard could contain text would either throw an error on the getData() line or return null.

(IOS) Cordova Camera Plugin Referring to deleted images

I'm building a cordova app (primarily for IOS & Android) in which the user can take an image, retake (, etc.) it and save it locally.
I'm currently struggling with the cordova cameraPlugin. So, here a short description of the problem.
When the user takes an image, it's saved locally in the apps temp folder and the user is able to view in in the UIWebView. On retaking, the image will be deleted from the temp folder and should not be available any longer (in RAM and local FS).
It works as long as the user doesn't retakes the image 2 or more times, if he does instead of the last image the first image will be referenced/rendered in WebView. After reopening the app, the image is displayed correctly.
An Example:
The user takes the first image. ==> cdv_photo_001.png
The second. ==> cdv_photo_002.png and the first one will be deleted (which seems to work correctly)
And the third. ==> cdv_photo_001.png and the second image will be deleted.
The third image will look the same as the deleted first one. This happens with every image after the third one.
It works fine after restarting the app
I've already tried to disable the App-Cache, delete the app cache before updating the image, refreshing the page and looking for answers online.
I'm getting an error when opening the camera UI, but I could not find a solution for it either.
Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before
snapshotting or snapshot after screen updates.
The code for the camera Call:
function getPhoto() {
navigator.camera.getPicture(getPhotoOnSuccess, getPhotoOnFail, {
quality: 25,
destinationType: Camera.DestinationType.FILE_URL,
correctOrientation: true,
encodingType: Camera.EncodingType.PNG
});
}
In getPhotoOnSuccess I'm basically saving the image path to a db and appending it with jQuery to the view.
And the code to delete the image: (sidenote I`m new to Objective C)
- (void) deleteImageByPath:(NSString *)imagePath withSelector:(SEL)selector{
NSError *error = nil;
NSFileManager *mgr = [NSFileManager defaultManager];
NSString *tempFolder = NSTemporaryDirectory();
if([mgr removeItemAtPath: imagePath error:&error] == NO) {
NSLog(#"File deleted");
}
//The files can be edited as well, so there can be two files in different directories
if(error != nil){
NSString *imgEl = tempFolder;
imgEl = [imgEl stringByAppendingPathComponent:imagePath.lastPathComponent];
if(![mgr removeItemAtPath:imgEl error:&error]){
NSLog(#"Old element couln't be deleted.");
}
}
[self performSelector:selector withObject:error];
}
The file is not in the directory anymore after deleting it, so I guess it works.
An important detail could be, that I wrote my own IOS cordova plugin, because the method for the file system access provided by cordova sucks.
So thats it.
The specific question is: Why and how is this happening and is there a chance to change this behavior? If yes, how should I proceed?
By the way, I`m using cordova 3.1.0 and the build target is IOS 7.
Thanks in advance.
Ok folks, I finally got it.
The whole problem was not related to my code or any of the cordova code.
So why did it happen? ==> I don't exactly know that, for it seems that this bug or whatever you might call it, has occurred to many people.
And they all tried to delete or deactivate the cache as I did, some of their problems are very close to my own but most aren't, so it took a while til I found a solution.
I read this thread and tried to append a timestamp to the image path and it worked!
My conclusion to this it, that there might be a problem with the UIWebView and the cache management.
Or it might proof as a general WebView problem, I will be able to check that in a few days on an Adroid device.

Resources