Custom NSURLProtocol not used in second webview - ios

I've subclassed NSURLProtocoland registered it in didFinishLaunchingWithOptions with this code:
+ (void) registerProtocol
{
static BOOL registered = NO;
if (!registered)
{
[NSURLProtocol registerClass:[MyURLProtocol class]];
registered = YES;
}
}
For the first UIWebView in my app (in the mainwindow) the method canInitWithRequest is triggered, and I can execute my custom code.
However I have a second UIWebView, that is inside an UIViewController which is pushed at some point in the app (presented modally). The canInitWithRequest will NOT be called for the second UIWebView, thus I cannot execute my custom code. This is even true when the protocol is registered after both instances of UIWebView are created.
Anyone knows why?
[edit]
d'oh! i just found it was just a plain error in the javascript that is loaded in the second webview :(
works like a charm in both webviews now!

Not sure if this is related to your situation and solution, but posting for the record. Discovered today after much trial and tribulation that an ajax call sending a message to objective c which is to be intercepted by a subclass of NSURLProtocol should have cache set to false if you expect to be sending the same url more than once. Like so:
$.ajax({
url:"atavistcommand://" + name,
data:JSON.stringify(data),
type:"GET",
processData:false,
cache:false )};
If cache = true then objective c will never receive subsequent requests on the same url.

Related

UISearchBar, textDidChange events, MultipleNSUrlConnection, crashes on tableView

In y iPhone App.
For search functionality. I am using UISearchBar and WebService call.
Whenever UISearchBar 'TextDidChange' happen the web service call happens.
Generally we are typing very fast, so there are many web service call happens, and I am using NSURLConnection, and I am loading table on Finished Loading.
eg,
WebService Call for M
WebService Call for Mo
WebService Call for Mor
Here, the problem is one webservice is going to finish, in between another web service called. This makes chaos.
Here, I solved the problem with writing.
**[connectionSearch cancel];**
connectionSearch=[[NSURLConnection alloc] initWithRequest:request delegate:self];
Write this code in textDidChange method for calling Webservice
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self webserviceCallMethod];
You can also call service like below after cancelPreviousPerformRequestsWithTarget:self
[self performSelector:#selector(webserviceCallMethod:) withObject:searchText afterDelay:0.3f]; // after delay can be an anything that helps.
Don't forget to remove all objects from your array, before you add objects to array again in response of your webservice. (Array that is used in cells of UITableView)
Hope it helps.
I changed the code like,
**[connectionSearch cancel];**
connectionSearch=[[NSURLConnection alloc] initWithRequest:request delegate:self];
So, whenever the same Search web service call happens, and if the call is not finished, call is cancelled and the new web service call happens.

NSURLConnection.sendAsynchronousRequest issue

I'm sorry the title is not clearer, but it's hard to resume the problem in one sentence.
I'm using the Facebook SDK to retrieve user information. My intent is to instantiate a class I wrote myself called Profile, which contains several user information, including the binary for the avatar picture (NSData).
To do this, I first use FBRequestConnection.startWithGraphPath and then, in the completion handler, I create a Profile instance and request the avatar binary data, using NSURLConnection.sendAsynchronousRequest:queue:completionHandler:
As soon as I get the complete profile, I notify a delegate with didReceiveProfile:. Now, the delegate is LoginViewController, which is a subclass of UIViewController. This method then saves the profile data to disk and then does this:
var app: UIApplication = UIApplication.sharedApplication();
var delegate: AppDelegate = app.delegate as AppDelegate;
delegate.application(app, didReceiveProfile: profile);
So the idea is to notify the AppDelegate that a Profile is now available, so whatever view controller that follows LoginViewController can now be displayed. Here's the code for that:
var storyboard: UIStoryboard = UIStoryboard(name: MainStoryboardIdentifier, bundle: nil);
var profileVC: ProfileViewController! = storyboard.instantiateViewControllerWithIdentifier(ProfileViewControllerIdentifier) as ProfileViewController;
NSLog("Everything OK");
self.navigationController!.pushViewController(profileVC, animated: false);
I do get the log "Everything OK", but the view doesn't show.
A few more details.
When the application first loads, it checks whether the profile has been saved to disk. If it has, it loads the profile from the file and calls didReceiveProfile: which instantly jumps to the second screen. So, the same method is called and it works. It can't be the code.
Also, notice that NSURLConnection.sendAsynchronousRequest:queue:completionHandler: is called inside the FBRequestConnection.startWithGraphPath's completion handler. If I call the delegate's didReceiveProfile: method before sendAsynchronousRequest (that is, in Facebook's completion handler), then it works and the view is properly shown, but I do not have the avatar binary. But if I call it inside sendAsynchronousRequest's completion handler, then the view doesn't show. It just gets back to the login view.
I am new to iOS development, but by my experience it seems like the completion handler is being called in a different thread? Being asynchronous and all.
So, how do I get the view to show?
Thank you.
Ok, I realised I left out something really important. As soon as I did, I also realised what the problem was. I was calling sendAsynchronousRequest like this:
var request = NSURLRequest(URL: url);
let queue = NSOperationQueue();
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ /* RESPONSE CODE */ });
Obviously the queue is not right. It should be:
let queue = NSOperationQueue.mainQueue();
That solved it.

How to use completionHandler of application performFetchWithCompletionHandler

In the new iOS 7 SDK, you can perform background fetches, by implementing performFetchWithCompletionHandler (http://bit.ly/15ofOx8).
They say you are supposed to execute completionHandler when you are done. When exactly is that?
My performFetch looks like this:
NSString *result = [super.viewController.webView stringByEvaluatingJavaScriptFromString:#"loadFeeds();"];
loadFeeds is a JavaScript function in a WebView, that makes an AJAX call. Which means it's probably going to go to another thread and loose the completionHandler. How can I execute completionHandler when the Ajax call is done?
I thought about using UIWebView's shouldStartLoadWithRequest to catch a custom URL, but by then I have completely lost completionHandler.

what is the propper way to use Titanium eventListerner/fireEvent to communication with webview

so I'm working on an application that uses webview to display data. At the moment i'm trying to get data from, and send data to the webview. It seems that getting data from the webview works fine, but sending data back to the webview forms the problem.
I use fireEvents and Eventlisteners to communicat. It looks somethhing like this:
webview : index.html
// declared at the beginning of the html file
Ti.App.addEventListener('sendToWebview', function(data) {
alert('alert in webview');
});
// fires when button is pushed
function onClick(){
Ti.App.fireEvent('sendToTi', { "someDataToTi" });
}
app.js
Ti.App.addEventListener('sendToTi', function(data) {
alert('alert in Ti');
Ti.App.fireEvent('sendToWebview', { "someDataToWebview" });
});
What works is the sendToTi event. here i always get the alert. What doesn't seem to work all the time is the sendToWebview event. The weird thing is that is sometimes seem to work, other times not and even when I go back to the code that worked, it seems to not work anymore.
What am I doing wrong? is there a way to make it work?
Your 'sendToTi' is correct. But you can't send events to the WebView in that way.
To execute JavaScript (which is sending events) in your WebView you can use
webview.evalJS('someJSFunction(with, parameters, for, instance);');
webview.evalJS('alert("Hello World!");');
There is no need of EventListeners (especially no app-wide event listeners).

Cancelling a javaScript event from a UIWebViewDelegate

Before I deem my weak-long custom re-implementation of UITextView (using an UIWebView in designMode) useless, is there any way to handle/cancel javaScript onKeyUp, etc. events?
AFAIK there is only messaging via -shouldLoadRequest: & stringByEvaluatingScriptWithString:. However, these calls are asynchronous and the javaScript event handler has already exited it's function by the time stringByEvaluatingScriptWithString: is performed thus event cancellation methods do not work.
If not for this capability, implementing shouldReplaceCharactersInString: seems impossible. :(
Maybe you can check my open source implementation of a "safari like" browser :
https://github.com/sylverb/CIALBrowser
In this one, I did reimplement the long tap handling, and to disable the standard one, I use this :
- (void) webViewDidFinishLoad:(UIWebView *) sender
{
// Disable the defaut actionSheet when doing a long press
[webView stringByEvaluatingJavaScriptFromString:#"document.body.style.webkitTouchCallout='none';"];
[webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.style.webkitTouchCallout='none';"];
}

Resources