My first question is how do I get the didAcceptConnectionWithInputStream:outputStream: callback in NSNetServiceDelegate to get called?
Follow up question: can I still establish a connection between a client and server, although I never get a callback saying that a connection was accepted (via didAcceptConnectionWithInputStream:outputStream:)?
I understand that calling publishWithOptions, while passing in the NSNetServiceListenForConnections option is supposed to result in the NetServiceDelegate callback (didAcceptConnectionWithInputStream:outputStream:) to be called. However, that callback is not getting called.
Here are the steps I am taking, to publish:
Create NSNetService with
self.netService = [[NSNetService alloc] initWithDomain:#""
type:_serviceType
name:(_name == nil) ? #"" : _name
port:0];
Schedule service in current runloop, in default mode
Set the delegate to my Server wrapper object
call publishWithOptions:NSNetServiceListenForConnections
Here are the steps I take, to browse services:
Create an NSNetServiceBrowser, and set its delegate to my client wrapper object
Call searchForServicesOfType for the same service type and domain as NSNetService
List services in a UITableView for the UI, to allow a user to select a service
When a user selects a service, set the service's delegate to my client object, and call getInputStream:outputSteam: on the service
After getInputStream:outputSteam: returns success, I would expect didAcceptConnectionWithInputStream:outputStream: to get called. However this does not occur.
Thanks for your help!
The problem is that didAcceptConnectionWithInputStream:outputStream: must be called from the side accepting the connection.
Once the service is available, you call get the streams
[service getInputStream:&istream outputStream:&ostream]
Once this happens on the side receiving the request the delegate method
- (void)netService:(NSNetService *)sender didAcceptConnectionWithInputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream
will be called
In my experience, it is not the act of calling getInputStream:outputStream: on the client that causes didAcceptConnectionWithInputStream:outputStream: to be called on the server.
On the client, after calling getInputStream:outputStream:, your client then needs to call [inputStream open] and [outputStream open] before the didAcceptConnectionWithInputStream:outputStream: will be called.
It's all a part of lazy initialization.
Calling getInputStream:outputStream: will give you back two perfectly good NSStreams ready to use. So, say, you want to write some data? First, open the write stream...
BAM! netService:didAcceptConnectionWithInputStream:outputStream: is called.
Related
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.
I'm using Dropbox iOS api in an application that need to upload file generated local by the app. When the app generate a file it is inserted in a queue (a separate thread), and I need that DBRestClient send it to Dropbox in background; but unfortunately the DBRestClient need a ViewController delegate (that I haven't in my context; is a generic NSObject class), and when I call the upload file method nothing seem to happen.
Can anyone help me ?
After you call an uploadFile method, one of the delegate methods should be called to give you the response:
- (void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath from:(NSString*)srcPath
metadata:(DBMetadata*)metadata;
- (void)restClient:(DBRestClient*)client uploadProgress:(CGFloat)progress
forFile:(NSString*)destPath from:(NSString*)srcPath;
- (void)restClient:(DBRestClient*)client uploadFileFailedWithError:(NSError*)error;
// [error userInfo] contains the sourcePath
Have you implemented these?
If so, there are a few things that might cause your delegate methods to not be called:
Your rest client is nil or is being released (e.g., by ARC) prematurely.
You're making the call in a background thread that doesn't have a run loop.
Your delegate method that should be called back has a typo in it. Unfortunately the SDK doesn't warn you if it can't find a delegate method to call; it just completes without telling anyone.
The second one may be relevant, given your description, in which case this may be helpful:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW1
In my app, I need to make https calls to a restful web api and process the results upon return. The number of simultaneous service calls is never fixed, hence the related code has been written accordingly. The data fetched from the service is temporarily stored on an SQLite DB within the app. Following is the structure how it works.
When the user navigates to any screen or UI component thereof for which data needs to be fetched, the view controller calls a method on its designated model object. This method then checks whether the data is already present in the DB or it needs to be fetched. In case data is present, it returns the same to the view controller. Otherwise, it initiates an asynchronous service request and waits till the response comes, after which it returns the data to the VC. Therefore, the VC initialises a loading indicator before calling the specified model, and dismisses the same after control is returned from this function.
Here it is important that the function on the model waits till the response is received from the web api. This is done by registering for an NSNotification which will be issued by the service module once returned data is written to the DB. A boolean variable it set to false upon making the service request and set to true once the response is received. An NSRunLoop runs on the false condition of this boolean variable. Hence once the variable is set to true, the rest of the processing can continue.
Following are the relevant pieces of code in which all this is implemented:
[serviceModule initServiceCall:#"25" withDictionary:[NSDictionary dictionaryWithObjects:#[asOfDate] forKeys:#[#"toDate"]]];
dataReady=NO;
NSString *notificationName = #"dataReady";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(useNotificationFromServiceModule:) name:notificationName object:nil];
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (!dataReady && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
The rest of the function continues after this.
This is the function that handles the notification:
-(void)useNotificationFromServiceModule:(NSNotification *)notification {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
dataReady=YES;
});}
The usual process is that once the notification is sent, the NSRunLoop quits and the rest of the method completes, returning to the view controller which then dismissed the loading indicator. The problem is that sometimes this does not happen. While the notification is issued (I can see the console log), the NSRunLoop does not end. The loading indicator continues to appear on the screen and stays that way until the screen is tapped once. When the screen is tapped, the NSRunLoop ends and the rest of the process continues randomly.
This does not happen always. It happens quite randomly, maybe about 4-5 times out of 10. Kindly provide some inputs/pointers to indicate why this may be happening.
If you are using the run loop directly, you are either very clever or very stupid. In the first case, you'll find the answer yourself. In the second case, it would be much much better if you followed the same pattern as everyone else does, which is running your networking code on a background thread and using dispatch_async when the results arrive.
In my project I am using the C-based XPC API, since NSXPCConnection is not available on the platform I am targeting. Currently I use a weak reference to prevent the connection handler block from retaining self, as follows:
__block VTVoltControllerProxy *proxy = self;
xpc_connection_set_event_handler(_connection, ^(xpc_object_t object) {
xpc_type_t type = xpc_get_type(object);
...
if (type == XPC_TYPE_ERROR && object == XPC_ERROR_CONNECTION_INVALID) {
if ([[proxy delegate] respondsToSelector:#selector(voltControllerDidDisconnectFromHost:)]) {
[[proxy delegate] voltControllerDidDisconnectFromHost:proxy];
}
}
});
However, an issue is introduced whenever the connection is cancelled inside the -dealloc method of my class:
- (void)dealloc
{
...
xpc_connection_cancel(_connection);
xpc_release(_connection);
...
}
Because cancelling an XPC connection is an asynchronous operation, the connection handler is called after the class instance has already been deallocated, causing proxy to point to an object that no longer exists.
Is there a way that I can safely cancel the connection in -dealloc and have the connection handler call the delegate method after cancellation?
You should be able to change the event handler to point at an event handler which is only used for the purpose of watching that the connection closes. You can either queue the pending connections in another object (perhaps a global or static) or just make the assumption that any connection calling this separate event handler is being called because it is being cancelled (check the event type of course).
Running into the same problem today. I don't know if you already resolved this or not. But what if dealloc waits for the XPC connection to be closed before continue.
It's possible to introduce a conditional variable to achieve this behavior.
But I am wondering what the drawback it could bring.
In iOS I am using NSURLConnection -
NSURLConnection *aConnection = [[NSURLConnection alloc....
is there a difference between:
[aConnection cancel];
and
aConnection = nil;
Thanks
When you send the connection a cancel message, it will stop to invoke your delegates as soon as possible and tear down the connection.
Note: it may happen in rare cases that you still get one already queued delegate message other than connection:didFailWithError: after you send cancel from a different tread than the one the connection schedules delegates.
With setting your reference to the connection to nil, you simply do this. This does not cancel the connection - and if this was your only reference, you also can't send a cancel anymore. ;)
Basically by doing this:
aConnection = nil;
you cannot be sure that there are no other references to this object. So you should do this:
[aConnection cancel];