My CoreBluetooth application need to enable the "indication bit" in Client Characteristic Configuration descriptors. Here is what I did:
Start to scan
Start to connect to the device
Call discoverServices
Call discoverCharacteristics inside the callback
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
Call discoverDescriptorsForCharacteristic inside callback
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
Inside callback
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
I called:
if ( [[descriptor.UUID representativeString] isEqualToString:#"2902" ] )
{
const unsigned char raw_data[] = {0x02};
NSData *myData = [NSData dataWithBytes: raw_data length: 2];
[self.cBCP writeValue:myData forDescriptor:descriptor];
}
But My app crashes in writeVale: . The error message in console is :
Cannot write Client Characteristic Configuration descriptors using
this method!
Any idea? Thanks
Pretty old question, but since it wasn't answered, seems like if the method setNotifyValue(_:for:) will handle that for you, it depends on the charcateristics properties below:
Notify only: Notifications will be enabled.
Indicate only: Indications will be enabled.
Indicate & Notify: ONLY Notifications will be enabled.
So if you need to enable indications, the characteristic must only have the indicate property, and notify should be removed.
I guess the main reasoning behind that is indications are much slower, so iOS will always prefer the fastest possible option unless it's a requirement to indicate.
Read more on the Apple docs
The problem is that you cannot use the writeValue:forDescriptor: method to write the value of a Client Configuration Descriptor (UUID = 2902) as you did.
Instead, you should use the setNotifyValue:forCharacteristic: method of the CBPeripheral class to configure client indications or notifications of a characteristic’s value on a server.
Related
Given iOS's SCNetworkReachability API configured like this:
SCNetworkReachabilityRef reachabilityRef = SCNetworkReachabilityCreateWithName(NULL, "example.com");
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context);
SCNetworkReachabilitySetDispatchQueue(reachabilityRef, dispatch_queue);
It works nice if I configure it with a domain name as in the example above. However, if I configure it with an IP address the callback is never called.
Anyone can confirm that this is the expected behavior of the SCNetworkReachability API? If so, any clues why?
Try using SCNetworkReachabilityCreateWithAddress instead of SCNetworkReachabilityCreateWithName.
Use eppz!reachability that works well with IP addresses, too. Simple usage like:
// Get status on-demand.
[EPPZReachability reachHost:#"eppz.eu"
completition:^(EPPZReachability *reachability)
{
if (reachability.reachable) [self postSomething];
}];
It is true. It won't work nor with SCNetworkReachabilityCreateWithAddress (It actually works on future reachability condition changes, but the first callback never get called).
If you design needs only on-demand reachability check, consider using SCNetworkReachabilityGetFlags.
A bit more at: Why asynchronous reachability with IP address doesn’t work? and Reachability with blocks for everyday use
How to send/receive file in iphone sdk using xmpp framework ?
Currently i am using XEP-0065 classes, and use the following code:
ObjTURNSocket = [[TURNSocket alloc] initWithStream:((TestAppDelegate*)[[UIApplication sharedApplication] delegate]).xmppStream
toJID:chatuser.jid];
[ObjTURNSocket start:self];
And I am getting following response from the server:
<iq type="error" id="AB2ED567-B97F-4DFE-B789-7731A617C239" to="kapil#testweb/6df6dc96" from="jabber.org">
<query xmlns="http://jabber.org/protocol/disco#items"/>
<error code="404" type="cancel">
<remote-server-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
</error>
</iq>`
Any help or suggestion will be well appreciated.
First, call setProxyCandidates with an array of the possible servers that you might be able to use for proxying. The default is "jabber.org", and your server is not federated with jabber.org, which is why you are getting the disco error back.
Next, in your delegate, implement the following methods:
- (void)turnSocket:(TURNSocket *)sender didSucceed:(GCDAsyncSocket *)socket;
- (void)turnSocketDidFail:(TURNSocket *)sender;
Then, in your didSucceed implementation, send data on the socket that was passed in, using:
- (void)writeData:(NSData *)data
withTimeout:(NSTimeInterval)timeout
tag:(long)tag;
Finally, close the socket:
- (void)disconnectAfterWriting;
I figured this out by looking at the source for TURNSocket.m, looking for the hardcoded "jabber.org", then searching for [delegate to find the places the delegate was getting called. This took me back to TURNSocket.h, which had a protocol documented for the delegate to implement.
I ended up having to customize the TURNSocket class to meet my specific needs to be able to transfer a file from my iOS device to another device. If there is a proxy server available, then the TURNSocket class might work for one's needs. But if this is a direct connection where a proxy server may not be available, then some extra work is necessary to set up your device to be able to connect to another device and directly transfer a file.
I was able to receive a file using TURNSocket in its current form with only one minor modification. As the code currently stands, the id and sid are assigned the same value, which cannot be guaranteed that a received stanza will have the same unique identifier value for both the id and sid.
You should have use xep-96 to make it possible to share and receive files.
after that just initiate xmppSifiletranfer with relevant data.
like
-(void)sendToOtherDevice:(NSData *)fileData receiverJid:(XmPPJId *)senderFullID file:(NSString *)fileName{
myFileTransferID=[xmppStream generateUUID];
XMPPJID *jid =senderFullID;
sifiletransfer=[[XMPPSIFileTransfer alloc]init];
[sifiletransfer initiateFileTransferTo:jid withData:fileData file:fileName passedsid:myFileTransferID];
if ([jid.domain isEqualToString:[xmppStream myJID].domain]) {
[TURNSocket setProxyCandidates:[NSArray arrayWithObjects:jid.domain, nil]];
} else {
[TURNSocket setProxyCandidates:[NSArray arrayWithObjects:jid.domain,[xmppStream myJID].domain, nil]];
}
TURNSocket *socket = [[TURNSocket alloc] initWithStream:xmppStream toJID:jid sid:myFileTransferID];
// [socket setDataToSend:fileData];
[socket startWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}
# delegater of turnsocket
- (void)turnSocket:(TURNSocket *)sender didSucceed:(GCDAsyncSocket *)socket
{
NSLog(#"Socket Suceeed Port For File Transfer: %d",socket.localPort);
DDLogInfo(#"TURN Connection succeeded!");
DDLogInfo(#"You now have a socket that you can use to send/receive data to/from the other person.");
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Hurray!!"
message:#"Conection Established"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}
if you guys have any other issue regarding file transfer comment below.I will surely help you.
In my app I doing something very similar to what is done in the WiTap project. I use Bonjour to discover peers and then send data over the socket to perform an initial handshake.
I'm able to see the data being sent OTA using Cocoa Packet Analyzer. But the stream: handleEvent: function is never called on the receiving peer side.
What I am able to see is:
Sometimes, when the peer that sent the data exits, the receiver peer seems to get the data.
Sometimes I am able to see an NSStreamEventErrorOccurred error in the handler function.
I'm unable to see any noticeable pattern on when the above behavior occurs.
Here is a bit of the code that might be helpful.
PacketSender and PacketReceiver objects are singletons.
I have verified multiple times that the correct (and the only) instance of these objects are set as delegates while debugging:
if (![netService getInputStream:&_inStream outputStream:&_outStream])
{
[Utilities showAlert:#"Failed connecting to server"];
return BM_ERROR_NETSERVICE_STREAM_FAIL;
}
if(!sharedProtocolManager.mPacketSender)
{
sharedProtocolManager.mPacketSender = [PacketSender sharedSender];
}
if(!sharedProtocolManager.mPacketReceiver)
{
sharedProtocolManager.mPacketReceiver = [PacketReceiver sharedReceiver];
}
if(!sharedProtocolManager.mPacketSender || !sharedProtocolManager.mPacketReceiver)
{
return BM_ERROR_FAIL;
}
[PacketReceiver setupInStream:_inStream];
[PacketSender setupOutStream:_outStream];
}
Inside the PacketReceiver setupInStream: function I have:
if (sharedPacketReceiver->mInStream != inStream)
{
[sharedPacketReceiver->mInStream release];
sharedPacketReceiver->mInStream = [inStream retain];
}
sharedPacketReceiver->mInStream.delegate = sharedPacketReceiver;
Any answers or even suggestions on ways to debug this further would be greatly appreciated.
Thanks!
I've been trying to automate tests on asynchronous requests but I haven't been able to run anything in a different thread while the test function was waiting.
Here is the test function:
- (void) testBoxManagerConnexionStatus
{
ControlSender* cs = [ControlSender get];
requestShouldSucceed = YES;
[cs startCheckingReachabilityWithDelegate:self];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:TIMEOUT_INTERVAL+1.0]];
STAssertTrue(downloadComplete, #"Download should be over by now");
}
My test class implements the callback methods this way:
- (void)controlSender:(ControlSender *)controlSender sentSuccessfullyCode:(FreeboxControl)code
{
if (requestShouldFail) {
STAssertTrue(NO, #"Request should have failed");
}
downloadComplete = YES;
}
- (void)controlSender:(ControlSender *)controlSender couldntSendCode:(FreeboxControl)code details:(NSHTTPURLResponse*)details
{
if (requestShouldSucceed) {
STAssertTrue(NO, #"Request should have succeded");
}
downloadComplete = YES;
}
But whenever my usual code try to run something in a different thread nothing happens. For example the NSURLConnection never call its delegate methods when allocated:
m_connexion = [[NSURLConnection alloc] initWithRequest:m_networkRequest delegate:self];
Neither the -connectionDidFinishLoading: nor the -connection:didFailWithError:
Same thing for calls like this one:
[self performSelectorInBackground:#selector(BG_startCheckingReachabilityWithDelegate:) withObject:delegate];
Nothings gets called in background when running the test.
The same code works fine outside of the test though.
Is there any way to test asynchronous url request with OCUnit?
Thanks for the help.
you could look at https://github.com/danielpunkass/RSTestingKit which has a way to wait on the run loop in unit tests you can see his slides at http://www.red-sweater.com/talks/UnitTesting.pdf for some background. It may have some info to help you get started.
You can try running requests like this in a background thread, but you don't want to. For your sanity, you don't want your tests dependent on external services. What if the remote service is down? Your test fails. What if the remote service returns an error? Your test fails? What if you want to what your code does when the remote service responds with an error to a valid request? You can't (consistently). Even if your tests pass they'll run slowly, depending on how long the remote service takes to respond.
Your life will be easier if you stub out the dependency on any remote services for the purpose of your tests. See this answer (and the associated question) for more detailed reasoning.
Is there a best practice way to store username and password on the iPhone? I am looking for something that is obviously secure but will also keep the info between app updates.
Use the Apple Keychain.
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error;
The first method allows you to request
the password associated with an
existing username for a particular
service name (I’ve just been using the
name of my app as a service name). The
second allows you to store a
username/password/service name combo,
and allows you to specify whether or
not the appropriate keychain item
should be updated with the provided
password if an existing one is found
that matches the username and service
name pair. The last parameter of each
is a reference to an NSError object
which will contain lower level error
information if something goes wrong
(and be nil if it does not).
For more information see his blog
The keychain is what you are looking for.
Use the Keychain, here is some code to make it very easy. Works on the device and simulator.
See the Generic Keychain example source. That's the way to go IMHO