I'm developing an Epub Reader for Ios, notice that i don't use any epub library and i parse the epub myself.
I needed a method for loading resource from epub into the UIWebView, for example images or CSS file ... (resource that requested in the html files).
for this purpose i decided to use NSURLCache class and override it's method (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
so i can intercept the request and find the right data from epub then create a NSCachedURLResponse and return it.
so far so good. BUT the the problem is that the data (i.e images) wont show in the webview. look at below code:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
/*
the request.URL is something like this applewebdata://UUID/OEBPS/Images/cover.jpg ,
so the getResourcePathFromRequest give me the path OEBPS/Images/cover.jpg,
so i can find the right data in epub
*/
NSString *resourcePath = [self getResourcePathFromRequest:request.URL];
/*
ResourceResponse is a class contaning data and mimeType,
i tested this and the data and mimeType are good so this is not the problem
*/
ResourceResponse *response = [[self getBook] fetch:resourcePath];
NSURLResponse *urlResponse = [[NSURLResponse alloc] initWithURL:[request URL] MIMEType:response.mMimeType expectedContentLength:[response.mData length] textEncodingName:#"UTF-8"];
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:response.mData];
return cachedResponse;
}
at this point everything looks normal, the method getting called, the right data being found and the response object return, but it wont load the image into the webview.
so what is the problem?
tnx in advance.
NOTE: i'm new in ios programming world (coming from android:)
I loaded the data with base url (http://localhost) and everything went fine.
Related
I'm making a simple POST request with some body-params, constructed like so:
_webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:[WKWebViewConfiguration new]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:<some_URI>]];
[request setHTTPMethod:#"POST"];
NSString *paramsStr = #"someKey=someValue"
[request setHTTPBody:[paramsStr dataUsingEncoding:NSUTF8StringEncoding]];
[_webView loadRequest:request];
My server endpoint never receives any POST params (the http-body seems to be empty)
I've seen discussions about WKWebView not providing POST data in the navigation delegate, but the use-case for those questions has been to fetch form data off of the webView. Further, I read that that WKWebView bug has been fixed.
My use case is very simple, I just want to make a POST request from a webview, but it's still not working. I'm on iOS 14.4 FWIW. Any tips on what could be causing the POST data to not be available on the server?
Use JavaScript to solve the problem that WKWebView cannot send POST parameters
Before I start, let me talk about the implementation ideas, so that everyone can understand it. If something goes wrong, you can know the wrong place:
Put the HTML code of a POST request containing JavaScript in the project directory
Load the code of this POST request containing JavaScript to WKWebView
After loading, use Native to call JavaScript's POST method and pass in parameters to complete the request
HTML code to create a POST request containing JavaScript
Related code:
<html>
<head>
<script>
//调用格式: post('URL', {"key": "value"});
function post(path, params) {
var method = "post";
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form.submit();
}
</script>
</head>
<body>
</body>
</html>
Copy this code and paste it into a text editor. You can choose any name, for example, save it as: JSPOST.html, and then copy it to the project directory. Remember to select the corresponding Target and check Copy items if needed (default It should be checked). At this time, you can use this JavaScript code to send a POST request with parameters.
Load the corresponding JavaScript code into WKWebView by loading a local web page
OC Code:
// JS sends the POST Flag, when it is true, it will call the JS POST method (only when the local JS is loaded for the first time)
self.needLoadJSPOST = YES;
// Create WKWebView
self.webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
//Set up proxy
self.webView.navigationDelegate = self;
// Get the path where JS is located
NSString *path = [[NSBundle mainBundle] pathForResource:#"JSPOST" ofType:#"html"];
// Get html content
NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
// load js
[self.webView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
// Add WKWebView to the current View
[self.view addSubview:self.webView];
This code is equivalent to loading the JavaScript scripts in the project into WKWebView, and we will see how to use it later. (Please change to your file name)
Native calls JavaScript scripts and passes in parameters to complete the POST request
Remember the section on the interaction between WKWebView and JavaScript? Now Native calls JavaScript. If you forget, please go ahead and review the story:-webView:didFinishNavigation: The proxy indicates that the page has been loaded. Let's do it here. The following code:
OC Code:
// Proxy method after loading
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
// Determine whether to load (only for the first time)
if (self.needLoadJSPOST) {
// Call the method of sending POST request using JS
[self postRequestWithJS];
// Set Flag to NO (you don’t need to load it later)
self.needLoadJSPOST = NO;
}
}
// Call JS to send POST request
- (void)postRequestWithJS {
// Send POST parameters
NSString *postData = #"\"username\":\"aaa\",\"password\":\"123\"";
// URL of the requested page
NSString *urlStr = #"http://www.postexample.com";
// Assembled into a string that calls JavaScript
NSString *jscript = [NSString stringWithFormat:#"post('%#', {%#});", urlStr, postData];
// NSLog(#"Javascript: %#", jscript);
// Call JS code
[self.webView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
}];
}
you can see more here: http://www.qw021.com/article-22.html
I have file.xlsx in my IOS app documents folder. I want to show open this excel file in UIWebview. but i am getting below error,
Error Domain=WebKitErrorDomain Code=102 "Frame load interrupted"
but pdf and CSV files are opening,
I am new to IOS and tried all possible things for it to work i guess from last 2 days. nothing worked out.. please help me
Update: Even if i rename it as file.xls its not opening
below is my code,
NSURL* nsUrl = [NSURL URLWithString:url];
_urlReq = [NSURL URLWithString:url];
[self performSelector:#selector(urlRequestForFile) withObject:nil afterDelay:0];
_webView.delegate = self;
NSURLRequest* request = [NSURLRequest requestWithURL: nsUrl];
[_webView loadRequest: request];
-(void)urlRequestForFile{
self.connection = nil;
NSURLRequest *requestForFile = [NSURLRequest requestWithURL:_urlReq cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:300];
_connection = [[NSURLConnection alloc]initWithRequest:requestForFile delegate:self startImmediately:YES];
_ongingServiceFlag = YES;
}
need help in showing xlsx file inside my IOS app either using UIWebView or is there any other way to show xlsx file inside the app without using third party apps?
Update(Solution):
I am very surprised to see that there is no support for XLSX mentioned even in apple site for UIWebView but actually UIWebView completely supports XLSX format. one thing you need to make sure is to specify the correct 'textEncodingName' value. if your file is stored with base64 binary encoding u have to mention it as textEncodingName:#"base64" otherwise u have to mention as "utf-8"
Below line worked for me:
[webView loadData:urlData MIMEType:#"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" textEncodingName:#"base64" baseURL:nil];
Use QLPreviewController for showing xlsx document in your app. Find the links for the tutorial below.
https://developer.apple.com/documentation/quicklook/qlpreviewcontroller
http://iosdevelopertips.com/data-file-management/preview-documents-with-qlpreviewcontroller.html
https://www.appcoda.com/quick-look-framework/
The .XLSX filetype is based on openXML which is good news! This means it's easily readable, we just need to let the webview know the type, or rather mimeTYPE, of the file we are loading/displaying. According to microsoft the mimetype to use for XLSX (OpenXML) files is:
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
source: https://blogs.msdn.microsoft.com/dmahugh/2006/08/08/content-types-for-open-xml-documents/
To do this we load the data (dataWithContentsOfFile: or dataWithContentsOfURL: or your prefered method) by calling the webView method:
[_webView loadData:<#(nonnull NSData *)#> MIMEType:<#(nonnull NSString *)#> textEncodingName:<#(nonnull NSString *)#> baseURL:<#(nonnull NSURL *)#>]
Example of my working code:
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"https://mycoolserver.com/file.xlsx"]];
[_webView loadData:data MIMEType:#"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" textEncodingName:#"utf-8" baseURL:nil];
.xlsx File cannot be opened using UIWebView. Though you can load .xls file using UIWebView.
Here is a list of files which you can load using UIWebView
https://developer.apple.com/library/content/qa/qa1630/_index.html
If you want to use .xlsx file, you have to use QuickLook FrameWork which contains QLPreviewController. Your code should be like this -
- (void) initQlController{
QLPreviewController *prev = [[QLPreviewController alloc]init];
prev.delegate = self;
prev.dataSource = self;
[self presentModalViewController:prev animated:YES];
[prev.navigationItem setRightBarButtonItem:nil]; }
Then you have to use the dataSource methods for the same : -
- (id <QLPreviewItem>)previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index
- (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller
I implemented a custom NSURLProtocol that allows me to use a static zipped version of a website as a target for a webView. It opens the zip on the go and load the required data.
But the issue is that NSURLProtocol seems not to behave properly with relative paths ? That is I have the following structure :
assets/css/main.css
assets/css/style.css
assets/images/sprite.png
index.html
And call sprite.png from the css using : background: url(../images/sprite.png) no-repeat;
but, the requestURL in my custom NSURLProtocol shows scheme://host/images/sprite.png, missing the assets part. It works fine if I switch the .. part for assets, but I would rather not have to do this.
I found the same issue here : Loading resources from relative paths through NSURLProtocol subclass but this got no answer.
I couldn't find any way to either fix this issue so that the request properly resolves the relative path, or fix the path myself afterwards (But i would need to know where the request originated from, and had no luck there either)
Any help appreciated, thanks in advance.
Side note :
Same problem using #import url("style.css"); in main.css
Edit :
I start by downloading the zip file from a remote server :
NSURL * fetchURL = [NSURL URLWithString:zipURLString];
[…]
NSString * filePath = [[self documentsDirectory] stringByAppendingPathComponent:fetchURL.path.lastPathComponent];
[zipData writeToFile:filePath atomically:YES];
So, from http://host/foo/archive.zip, i save it to documentsDirectory/archive.zip.
From there, I change the scheme and the url to point on the zip file :
NSString * str = [NSString stringWithFormat:#"myzip://%#", zipURL.path.lastPathComponent];
[_webView loadRequest:[NSURLRequest str]];
Which opens myzip://archive.zip, and if no such file was found in the zip file, I append /index.html to the current path.
Thus the following requests arrive in my NSURLProtocol subclass - (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id < NSURLProtocolClient >)client:
myzip://archive.zip (Changed to myzip://archive.zip/index.html)
myzip://archive.zip/assets/css/main.css
myzip://archive.zip/styles.css (Problem here)
Finally fixed it.
I had the following in my NSURLProtocol :
- (void)startLoading {
[self.client URLProtocol:self
didReceiveResponse:[[NSURLResponse alloc] init]
cacheStoragePolicy:NSURLCacheStorageNotAllowed];
//Some other stuff
}
and solved the issue with the following :
- (void)startLoading {
[self.client URLProtocol:self
didReceiveResponse:[[NSURLResponse alloc] initWithURL:_lastReqURL MIMEType:nil expectedContentLength:-1 textEncodingName:nil]
cacheStoragePolicy:NSURLCacheStorageNotAllowed];
//Some other stuff
}
Where _lastReqURL is _lastReqURL = request.URL;, from
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id < NSURLProtocolClient >)client {
self = [super initWithRequest:request cachedResponse:cachedResponse client:client];
if (self) {
_lastReqURL = request.URL;
// Some stuff
}
}
I can only assume the URL part in NSURLResponse is critical when dealing with relative-paths (seems logical).
I think this might refer to the way you do load the Request or the HTML.
Could you paste the code for your request? I guess, you load the HTML locally, so don't forget to set the baseURL accordingly, else the relative pathes won't work anymore:
For example the following:
[self.webView loadHTMLString:html baseURL:[NSURL URLWithString:#"host"]];
I use this code to load a UIImage from a URL:
NSURL *imageURL = [NSURL URLWithString:#"http://testwebsite.com/image.png"];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:imageURL];
imageView.image = [UIImage imageWithData: imageData];
but I'm stuck at a URL that looks like this one :
http://www.testwebsite.com/getFile?userID=123
this works fine in the browser but returns nil in the imageData variable above.
how can I load the image from such an obscured URL (i.e. from a URL that does not show off the file name but rather redirects to the image file) ?
thank you in advance.
for some weird reason I have to use :
[NSURL URLWithString:[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
when I create the URL and this solved the issue.
NSData initWithContentsOfURL: returns nil if the data cannot be loaded (i.e. if you get a HTTP error code).
Chances are the URL you point to has some referrer blocker or cookie requirement that prevents its use from within your app (just one suggestion of the problem).
I recommend you look at using the following variant instead:
- (id)initWithContentsOfURL:(NSURL *)aURL options:(NSDataReadingOptions)mask error:(NSError **)errorPtr
And pay attention to the value of errorPtr in case the return value is nil.
For example:
NSError *error;
NSData* imageData = [[NSData alloc] initWithContentsOfURL:
[NSURL URLWithString:#"http://example.com/notfound"]
options:0 error:&error];
if(imageData == nil) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"Got %u bytes", imageData.length);
}
You can see the following error:
Error: Error Domain=NSCocoaErrorDomain Code=256 "The operation couldn’t be completed. (Cocoa error 256.)" UserInfo=0xa881c20 {NSURL=http://example.com/notfound}
If you load the image like that it's blocking the main thread and leaving your application unresponsive and I'm not sure if the initWithContentsOfURL: method is handling redirects at all if that is the issue... You can create another class (ImageFetcher for example) that uses NSURLConnection and implements the NSURLConnectionDataDelegate. Using the connection:willSendRequest:redirectResponse: method from the NSURLConnectionDataDelegate you can handle redirects and in the connection:didReceiveData: method you can append the received data to a NSMutableData (it's not guaranteed that you receive all the data in one go). Furthermore you don't have to wait for the connection to finish or download any data if you implement the connection:didReceiveResponse: method and check if the response is indicating an error and in the connectionDidFinishLoading: method you can call another method to update your image view (or call the delegate of the ImageFetcher class with the appropriate data to update the image view).
NSURL URLWithString:#"" will return nil if the URL does not conform to RFC 2396 and they must be escaped
If you read through rfc2396 in the link you will get loads of details
A great site I found for checking where the offending character is, choose the path option for URL
http://www.websitedev.de/temp/rfc2396-check.html.gz
I'm an iOS newb (.NET professional), so this may be a simple issue but I couldn't find anything through the SO search or Google (and maybe not looking for the right terms).
I'm writing an app that displays information from a DD-WRT router through it's web interface. I have no problem displaying the initial page and navigating through any of the other pages, but if I make any change on a form (and it redirects to apply.cgi or applyuser.cgi), the UIWebView is blank - it's supposed to display the same page, with the form submission changes. The site works fine in Mobile Safari, which I find intriguing, but I guess UIWebView isn't totally the same.
I think the iOS code is pretty standard for display a webpage, but I'll list it below. I can't give you access to my router because, well, that's not a good idea :) Hopefully someone with a DD-WRT router can help (or know what my issue is anyway).
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *sURL = #"http://user:pass#XXX.XXX.X.X";
NSURL *url = [NSURL URLWithString:sURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
self.webView.delegate = self ;
}
And I'm doing a few things with Javascript in the webViewDidFinishLoad method, but I know that's not the culprit because it still happens when I comment it out.
Well I figured out the problem on my own. I think part of it was putting the username & password in the URL (which was just a temporary measure) because I found that method provided the same results in mobile Safari and desktop Chrome.
So I added MKNetworkKit to my project that provided a simple way to add authentication to my request, and found I had to make a specific request to POST the data, then reloaded the page the to see the changes.
In the (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType method, I check if ([request.HTTPMethod isEqualToString:#"POST"]) and do this:
NSString *sPostData = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
NSArray *aPostData = [sPostData componentsSeparatedByString:#"&"];
NSMutableDictionary *dPostData = [[NSMutableDictionary alloc] init];
//i don't know if this is the best way to set a dictionary, but it works
for (id apd in aPostData)
{
NSString *key = [apd componentsSeparatedByString:#"="][0];
NSString *val = [apd componentsSeparatedByString:#"="][1];
[dPostData setValue:val forKey:key];
}
MKNetworkEngine *engine = [[MKNetworkEngine alloc] init];
MKNetworkOperation *op = [engine operationWithURLString:[request.URL description] params:dPostData httpMethod:#"POST"];
[op setUsername:#"myUserName" password:#"myPassword" basicAuth:YES];
self.postedRequest = TRUE; //a bool I set so, when it comes to webViewDidFinishLoad, I reload the current page
[op start]; //send POST operation