What is the difference between NSURLRequest's mainDocumentURL and URL properties? - objective-c

I've built a web browser for iOS and I'm trying to pull the URL to display in an address bar.
I look at the NSURLRequest in the webView:shouldStartLoadWithRequest:navigationType: and the webViewDidFinishLoad: UIWebView delegate methods.
Should I use the URL or the mainDocumentURL property of the NSURLRequest for that purpose, and what is the difference?

You want to use URL.
mainDocumentURL would be used when loading sub-parts of a page (like images, scripts, whatever) to tell the system which cookies it can safely use when loading those sub-parts. It’s nothing you’d want to display, and is documented to be unused in the current version of the framework anyhow.
/*!
#method mainDocumentURL
#abstract The main document URL associated with this load.
#discussion This URL is used for the cookie "same domain as main
document" policy. There may also be other future uses.
See setMainDocumentURL:
NOTE: In the current implementation, this value is unused by the
framework. A fully functional version of this method will be available
in the future.
#result The main document URL.
*/

The documentation is not very clear, but it appears that mainDocumentURL used for cookie policy internals.
From NSHTTPCookieStorage Class Reference > setCookies:forURL:mainDocumentURL:
mainDocumentURL
The URL of the main HTML document for the top-level frame, if known. Can be nil. This URL is used to determine if the cookie should be accepted if the cookie accept policy is NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain.
If it's possible that the URL you are using is a sub-frame or some media type embedded in an HTML document, then you may wish to know what the mainDocumentURL is; otherwise, just use URL.

Related

How to properly implement a QLPreviewPanel data source keeping in mind security-scoped URLs?

There is a problem with a poorly chosen, non-composable abstractions in Cocoa that arise when one tries to combine a Quick Look preview panel and a security-scoped URLs.
I have a concrete example:
Imagine we are trying to show a preview for some objects from the MediaLibrary (MediaLibrary.framework allows applications to browse the iPhoto, Aperture... and Photos libraries via convenient API).
The most simple and straightforward way to do so is to adapt the 'MLMediaObject' class (that represent a particular photo or video item) to implement the 'QLPreviewItem' protocol (which can be passed to the QLPreviewPanel):
MLMediaObject+PreviewItem.h
#import <MediaLibrary/MLMediaObject.h>
#import <Quartz/Quartz.h>
#interface MLMediaObject (PreviewItem) <QLPreviewItem>
#end
MLMediaObject+PreviewItem.m
#import "MLMediaObject+PreviewItem.h"
#implementation MLMediaObject (PreviewItem)
- (NSURL*) previewItemURL
{
return self.URL;
}
- (NSString*) previewItemTitle
{
return self.name;
}
#end
Simple. Now image the following QLPreviewPanel data source implementation:
AlbumViewController.m
- (NSInteger) numberOfPreviewItemsInPreviewPanel: (QLPreviewPanel*) panel
{
// 'currentAlbum' property contains the currently-represented MLMediaGroup object.
return self.currentAlbum.count;
}
- (id<QLPreviewItem>) previewPanel: (QLPreviewPanel*) panel previewItemAtIndex: (NSInteger) index
{
return self.currentAlbum[index];
}
So far so good. But if we look into the sparse and usually misleading Apple documentation, we may find out the following important details:
URL
The location of the media object. (read-only)
This property is provided as a security-scoped URL. In order to gain access to the file that this URL refers to, the caller must call startAccessingSecurityScopedResource before and stopAccessingSecurityScopedResource after using the URL to access the file.
So, it is obvious that access to the resource have to be bracketed with a startAccessingSecurityScopedResource/stopAccessingSecurityScopedResource calls pair.
The question is where should I put these calls given the current QLPreviewPanelDataSource protocol definition? It is up to QLPreviewPanel to access the resource, not my code, but unfortunately I hardly ever will believe that Apple updated QL to operate in a sandboxing-wise environment.
How do I handle the cases, when startAccessingSecurityScopedResource call returns NO, stating about failure to obtain an access?
Seems like when you try to startAccessingSecurityScopedResource on an URL that is already being accessed you get the failure flag on return. Like, everything is OK, but you get an error flag. Seems like these start/stop... calls have to be paired precisely, and even a balanced nesting is forbidden. So, how to differentiate between the two possibilities when you get a NO on return: a security-scoped URL that is already being accessed and a security-scoped URL that failed to 'resolve'?
It is an experimentally-proven fact, that your application can only access a finite amount of security-scoped URLs (you can take about ~1500 URLs before it will silently stop working). So, how should I properly relinquish access to the security-scoped URLs after I've passed them to the QLPreviewPanel? When it it a right time to do so? It seems to me like it is a private implementation detail of a QLPreviewPanel class and I can make no assumptions about its internal workings.
You can use:
- (void)beginPreviewPanelControl:(QLPreviewPanel *)panel {
[bookmarkURL startAccessingSecurityScopedResource];
//... Your code
}
and
- (void)endPreviewPanelControl:(QLPreviewPanel *)panel {
//... Your Code
[bookmarkURL stopAccessingSecurityScopedResource];
}

How to prevent the user from choosing some file types?

I want to prevent the user to choose some file types when he opens NSOpenpanel.
what i can do at the moment is preventing him from accessing all the files and allow some but i want to allow all the files except some.
NSOpenpanel*Openpane = [NSOpenpanl openpanel];
[Openpanel setAllowFileTypes(NSArray*)];
but i want the user to choose all the files except files not choose some files out of all the files.
You can set a list of acceptable file types by calling [-NSOpenPanel setAllowedFileTypes:] and passing in an array of allowed file extensions. See the documentation for details:
A file type can be a common file extension, or a UTI. A nil value indicates that any file type can be used. The default value is nil.
If you want to filter out some filenames and not others, I think you can use the NSOpenSavePanelDelegate method -panel:shouldEnableURL:
For NSOpenPanel delegates, asks the delegate whether the specified URL should be enabled in the panel.
Implement a delegate for the panel. NSOpenPanel inherits a delegate property from its superclass NSSavePanel.
In the delegate, implement either:
- (BOOL) panel:(id)sender shouldEnableURL:(NSURL*)url;
or:
- (BOOL) panel:(id)sender validateURL:(NSURL*)url error:(NSError**)outError;
You should use the first one if you can decide whether a given URL should be enabled quickly and efficiently. It will be called frequently. It controls whether or not a given URL is selectable in the panel.
You should use the second one if your decision is slow or requires significant CPU or I/O. It is only called when the user clicks the Open button. That's not the best user experience. It's better to prevent the user from making a bad choice than to let them make a bad choice and reject it at the last moment.
All of that said, it's kind of weird to allow a user to select any kind of file except MP3s. Is there really no other restriction?

How to perform apprequest using native dialogs

We are living confusing times were documentation from the past merges with documentation from the present.
I am trying to make an app request, I have FB SDK 3.1 and iOS6.
I am checking code from the address:
https://developers.facebook.com/docs/howtos/send-requests-using-ios-sdk/
I can not make it work, takes my attention the next paragraph:
In your app delegate import the Facebook.h header file and replace the Facebook framework
"FacebookSDK/FacebookSDK.h" import declaration: #import "Facebook.h"
I don't have Facebook class anymore in my libraries.
Facebook class had to be initialized with app id, delegate, etc... I don't know how it is supposed to work now, specialy having FBSession in place.
My question is, how to make a modern apprequest? And... what is with the documentation?
You can only get the requests dialog with the previous API, it's not available for the current one.
Make sure you follow this step from the docs -
The headers can be found here ~Documents/FacebookSDK/FacebookSDK.framework/Versions/A/DeprecatedHeaders. Drag the whole DeprecatedHeaders folder and deselect the ''Copy items into destination group's folder (if needed)'' option to add the headers as a reference.
Add those headers to your project and you'll have access to the Facebook class.

Saving a user edited text field to a .js file in iOS

I'm creating an iOS5 app (programmed in Objective-C) which provides a couple of functions. One of them is to allow a user to fill out a text field with JavaScript. When the user presses a "Test" button, I want it to save to a specific JS file and move to a new view, displaying an HTML page that will display the results of that JavaScript on a canvas element.
Unfortunately for me, I have no idea how to save to a JS file for a text field. Nor do I know if this is actually the best way to achieve the results I'm after.
So can anyone tell me the code I'd need to place in the IBAction of my test button to save the file, or if there is a better way to get the user's script into the HTML file with the canvas element?
You have some options to do that.
Add a callback in WebView is possible only in desktop apps, but you can make a workaround.
1- Set a handler to click in javascript;
2- This handler parse the value via windows.location (trying to change the current url);
3- In UIWebviewDelegate set webView:shouldStartLoadWithRequest:navigationType: to recognize this values parsed by javascript and returning NO (canceling the url change).
After you have the code typed by user, is easier pass to the new one UIWebView. You can save the file via NSData or other class and load in by the path, or you can parse directly the code to be showed via NSString.
EDIT
I, still, belive what I said is what you want, but with a little more info. Yes, 3 NSString probably solve your problem. You even can call eval in javascript, via objective-c and parse user code too. Those logic ideas are a good approaching. You can choose the easier for you.
There are more info in this another Q&A
You can use html 5 local storage for that.

iOS access to Safari Reader feature through UIWebView

I am using iOS 4.3 & was wondering if there is any way that I can access the Safari's "Reader" feature through which webpages are removed of ads & other riff raff & the content takes the center stage.
If one opens any article in Safari (on say Wikipedia website), then a "Reader" button appears on the URL bar. Clicking on it presents a new window presenting the content beautifully.
How can I leverage this this functionality in iOS through UIWebView ?
PS: I know there is something called Readability Project. But I have no idea how to use this through UIWebView. Also for some websites Safari's Reader takes a call not to enable "Reader" feature, maybe it has no sufficient confidence?
Important: THIS ANSWER NO LONGER WORKS!
Readability shut down on September 30, 2016.
Here is something they recommend as a replacement:
https://mercury.postlight.com/web-parser/
Keeping the answer as a historical reference
--- Original answer ---
You can use Readability mobilizer for this. You will get a cleaned up version of any article, in the Readability styling:
http://www.readability.com/m?url=http://{URLOFTHEARTICLE}
Just prepare the URL and load it in your UIWebView. Here is how it looks in action:
http://www.readability.com/m?url=http%3A%2F%2Fwww.cnn.com%2F2013%2F01%2F11%2Fshowbiz%2Ftv%2Fgolden-globes-tv-vineyard%2Findex.html%3Fhpt%3Dhp_abar
Apple is making a pretty big deal about the inclusion of "Reader" in iOS 5. I'm assuming by the noise it's not available in 4.3
re: How to use through UIWebView
I can't find any mention of it in the Web Content Guide.
There's nothing about it in the UIWebView class reference.
And there's nothing in QA1630.
Dont parse HTML natively on iOS, I have done it before and its a messy business. Either create your own web service to do all the nasty work or look into using readability (readability.com) they provide an API.
There is also an open source ruby, python and php readability port that you can find here
https://github.com/iterationlabs/ruby-readability
https://github.com/gfxmonk/python-readability
http://code.fivefilters.org/p/php-readability/source/tree/master/
For you ruby enthusiasts, readability is also available as a gem, just google it.
Actually reader button do a bit of analysis where it parse the HTML Page and then it sees a clear body tag to parse. If that plugin is able to extract the exact body it will enable the reader button (My understanding from the readability source code). Now to implement the same for webview you just need to embed java script in your code (this java script is already available in the readability source code) and then you can achieve the same effect.
But I suspect the future plan from apple for the same. Because they can not just let anyone else do this content extraction with the huge business opportunity associated with iCloud with the combination of readability.
If you want you can simple extract the HTML from UIWebView and then extract the body and use it for your purpose. It's not a very rocket science to extract.
For analysis point of view, just have randomly some 10 HTML pages with Reader button enabled, you will see the core cotent belongs to body only and rest of the add, header, footer are separated.
I believe this is the time to re-invent the web content we use, and this is the perfect example of doing the same.
You can even do this by injecting javascript.
#define readJS #"(function(){window.baseUrl='https://www.readability.com';window.readabilityToken='';var s=document.createElement('script');s.setAttribute('type','text/javascript');s.setAttribute('charset','UTF-8');s.setAttribute('src',baseUrl+'/bookmarklet/read.js');document.documentElement.appendChild(s);})()"
And then when your webpage finishes loading
- (void)webViewDidFinishLoad:(UIWebView *)webview
{
[webview stringByEvaluatingJavaScriptFromString:readJS];
You can do it in iOS9.
first import SafariServices:
#import <SafariServices/SafariServices.h>
Afterwards we are instantiating SFSafariViewController and adding it as a subview. We have two options doing so:
Creating with only base URL
Creating with bas URL as well as entering 'Reading Mode' in case it is available
NSString *sURL = #"http://google.com";
NSURL *URL = [NSURL URLWithString:sURL];
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL]; // 1.
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL entersReaderIfAvailable:YES]; // 2.
[self presentViewController:safari animated:YES completion:nil];