handling NSStream events when using EASession in MonoTouch - xamarin.ios

Does anyone have an example of how to handle read and write NSStream events in Monotouch when working with accessories via EASession?
It looks like there isn't a strongly typed delegate for this and I'm having trouble figuring out what selectors I need to handle on the delegates of my InputStream and OutputStream and what I actually need to do with each selector in order to properly fill and empty the buffers belonging to the EASession object.
Basically, I'm trying to port Apple's EADemo app to Monotouch right now.
Here's the Objective-C source that I think is relevant to this problem:
/
/ asynchronous NSStream handleEvent method
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventNone:
break;
case NSStreamEventOpenCompleted:
break;
case NSStreamEventHasBytesAvailable:
[self _readData];
break;
case NSStreamEventHasSpaceAvailable:
[self _writeData];
break;
case NSStreamEventErrorOccurred:
break;
case NSStreamEventEndEncountered:
break;
default:
break;
}
}
/ low level write method - write data to the accessory while there is space available and data to write
- (void)_writeData {
while (([[_session outputStream] hasSpaceAvailable]) && ([_writeData length] > 0))
{
NSInteger bytesWritten = [[_session outputStream] write:[_writeData bytes] maxLength:[_writeData length]];
if (bytesWritten == -1)
{
NSLog(#"write error");
break;
}
else if (bytesWritten > 0)
{
[_writeData replaceBytesInRange:NSMakeRange(0, bytesWritten) withBytes:NULL length:0];
}
}
}
// low level read method - read data while there is data and space available in the input buffer
- (void)_readData {
#define EAD_INPUT_BUFFER_SIZE 128
uint8_t buf[EAD_INPUT_BUFFER_SIZE];
while ([[_session inputStream] hasBytesAvailable])
{
NSInteger bytesRead = [[_session inputStream] read:buf maxLength:EAD_INPUT_BUFFER_SIZE];
if (_readData == nil) {
_readData = [[NSMutableData alloc] init];
}
[_readData appendBytes:(void *)buf length:bytesRead];
//NSLog(#"read %d bytes from input stream", bytesRead);
}
[[NSNotificationCenter defaultCenter] postNotificationName:EADSessionDataReceivedNotification object:self userInfo:nil];
}
I'd also appreciate any architectural recommendations on how to best implement this in monotouch. For example, in the Objective C implementation these functions are not contained in any class--but in Monotouch would it make sense to make them members of my

It looks like the latest version of MonoTouch (I had to upgrade to iOS 4.2 first to get it) now implements a strongly typed delegate for HandleEvent and a new NSStreamEvent type so it can be more easily handled:
// asynchronous NSStream handleEvent method
public override void HandleEvent (NSStream theStream, NSStreamEvent streamEvent)
{
switch (streamEvent)
{
case NSStreamEvent.None:
break;
case NSStreamEvent.OpenCompleted:
break;
case NSStreamEvent.HasBytesAvailable:
LowReadData((NSInputStream)theStream);
break;
case NSStreamEvent.HasSpaceAvailable:
LowWriteData((NSOutputStream)theStream);
break;
case NSStreamEvent.ErrorOccurred:
break;
case NSStreamEvent.EndEncountered:
break;
default:
break;
}
}

Related

How to detect if a bluetooth headset plugged or not IOS 8?

In my project, I use AVAudioSession to detect any headphone is plugged or unplugged. But in this case, I can't detect when bluetooth device is plugged. Here is my code for headphone state.
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
//NSLog(#"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
NSLog(#"Headphone/Line plugged in");
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-on.png"] forState:UIControlStateNormal];
_headSetState=YES;
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
NSLog(#"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
NSLog(#"Headphone/Line was pulled. Stopping player....");
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-off.png"] forState:UIControlStateNormal];
if(_isPlaying==YES)
{
[self.player pause];
[_audioButtonOutlet setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateNormal];
_isPlaying=NO;
}
_headSetState=NO;
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
// called at start - also when other audio wants to play
NSLog(#"AVAudioSessionRouteChangeReasonCategoryChange");
break;
}
- (BOOL)isHeadsetPluggedIn
{
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
{
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-on.png"] forState:UIControlStateNormal];
_headSetState=YES;
return YES;
}
else
{
[_soundButtonOutlet setImage:[UIImage imageNamed:#"sound-off.png"] forState:UIControlStateNormal];
_headSetState=NO;
return NO;
}
}
return NO;
}
}
- viewWillAppear {
[AVAudioSession sharedInstance];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification object:nil];
[self isHeadsetPluggedIn];
}
So how can I detect if a bluetooth headset plugged or not iOS 8?
You can detect currently active bluetooth output devices (instead of input devices)
Swift Code:
import AVFoundation
func bluetoothAudioConnected() -> Bool{
let outputs = AVAudioSession.sharedInstance().currentRoute.outputs
for output in outputs{
if output.portType == AVAudioSessionPortBluetoothA2DP || output.portType == AVAudioSessionPortBluetoothHFP || output.portType == AVAudioSessionPortBluetoothLE{
return true
}
}
return false
}
Bluetooth devices are based on the following question: What's the difference among AVAudioSessionPortBluetoothHFP, A2DP and LE?
I hope it helps someone
Edit for Swift 5.1 (Thanks iago849 for the fix)
var bluetoothDeviceConnected: Bool {
!AVAudioSession.sharedInstance().currentRoute.outputs.compactMap {
($0.portType == .bluetoothA2DP ||
$0.portType == .bluetoothHFP ||
$0.portType == .bluetoothLE) ? true : nil
}.isEmpty
}
I was able to detect whether a bluetooth headset (HFP) device was currently connected using the following:
NSArray *arrayInputs = [[AVAudioSession sharedInstance] availableInputs];
for (AVAudioSessionPortDescription *port in arrayInputs)
{
if ([port.portType isEqualToString:AVAudioSessionPortBluetoothHFP])
{
bHas = YES;
break;
}
}
However, your AVAudioSession category must be set as AVAudioSessionCategoryPlayAndRecord in order for this to work. If it isn't, the port will not show up in the list even if the HFP device is connected.
You can detect it with routeChangeNotification:
func activateHeadPhonesStatus(){
NotificationCenter.default.addObserver(self, selector: #selector(audioRouteChangeListener(_:)), name: AVAudioSession.routeChangeNotification, object: nil)
}
#objc func audioRouteChangeListener(_ notification:Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue:reasonValue) else {
return
}
if reason == .newDeviceAvailable {
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSession.Port.bluetoothA2DP {
print("Bluetooth Headphone Connected")
break
}
}
}

Is it OK to use dot-syntax getters within an NSManagedObject's custom class?

Generally speaking... in an NSManagedObject class, inside one of the setters for a given float dynamic property, is it OK to use the dot-syntax getters for other float dynamic properties of the same NSManagedObject within that setter? Or do I need to use KVC-compliant CoreData accessors any time I access a value, even if it's from a different method than the actual getter for the value being accessed? I would assume that calling the dot-syntax in this way would cause my other custom accessor to fire, which is OK with me, since inside that there are the proper KVC primitive accessors. But I've seemed to run into weird issues where the dot-syntax either simply fails, or seems to have unpredictable results, and I'm not sure if it's because of the fact I'm using the dot-syntax in an unsafe way, or if there's some other bug I haven't figured out yet.
Here's a code sample of something I'm talking about:
- (void)illTakeYouToTheWoodshed {
float h = self.SSx.floatValue/self.yourMomsCurrentWeightInTons.floatValue;
[self willChangeValueForKey:#"SSy"];
[self setPrimitiveValue:#(h) forKey:#"SSy"];
[self didChangeValueForKey:#"SSy"];
[self diagonal]; //makes sure nd gets set
}
- (void)setSSx:(NSNumber *)value{
[self willChangeValueForKey:#"SSx"];
[self setPrimitiveValue:value forKey:#"SSx"];
[self didChangeValueForKey:#"SSx"];
if(self.WorH==syanara || self.WorH == dude_wtf) {
if(self.SSy.floatValue != 0.0) {
[self doThatFunkyDance];
[self diagonal];
} else if (self.youBetcha.floatValue != 0.0) {
[self whatTheFrakDoesThisEvenDo];
}
} else if (self.WorH==fooBarTastic) {
if(self.yourMomsCurrentWeightInTons.floatValue != 0.0) {
[self illTakeYouToTheWoodshed];
}
} else {
NSLog(#"Escaped with salad not having been tossed.");
}
}

substitute to switch statement in objective c

I am loading a tableview with the contents from web service using json frameworks in asynchronous connection. The data is in the json object form
{"id":1,"firstName":"A","lastName":"B","email":"abc#yahoo.com","salary": {"monthly":$5000,"annual":$60000}}
I am loading tableview using switch statement in cellForRowAtIndexPath:
dictionaryData = [responseString JSONValue];
switch (indexPath.row)
{
case 0:
cell.textLabel.text = [NSString stringWithFormat:#"%# : %# %#",#"Name",[dictionaryData valueForKey:#"firstName"],[dictionaryData valueForKey:#"lastName"]];
break;
case 1:
cell.textLabel.text = [NSString stringWithFormat:#"%# : %#",#"Email",[dictionaryData valueForKey:#"email"]];
break;
case 2:
cell.textLabel.text = [NSString stringWithFormat:#"%# : %#",#"Monthly Salary",[[dictionaryData valueForKey:#"salary"]valueForKey:#"monthly"]];;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
break;
case 3:
cell.textLabel.text = [NSString stringWithFormat:#"%# : %#",#"Annual Salary",[[dictionaryData valueForKey:#"salary"]valueForKey:#"annual"]];
break;
default:
break;
}
This is for normal data, but when i have more fields like phone number, address, department number, etc , then writing too many cases will make the method very large.Can someone help me how i can do this without switch.
You are going about this the wrong way. You want to create arrays and index them using indexpath.row. So you ll have only one line of assigning cell.textLabel.text. One solution is:
Create an NSArray of objects beforehand containing your #"Name",#"E-Mail" etc. like:
NSArray *arr1=[NSArray arrayWithObjects:#"Name",#"Email",....,nil];
Then when you get the NSDictionary,store it in an array and enumerate through it like
NSMutableArray *arr2=[dictionaryData allValues];
for(id obj in arr2)
{
if([obj isKindOfClass:[NSString class]])
[arr2 addObject:obj];
else if([obj isKindOfClass:[NSDictionary class]])
{
[arr2 addObject:[obj valueForKey:#"Monthly"]];
[arr2 addObject:[obj valueForKey:#"Annual"]];
}
}
Then just use indexpath.row in cell.textLabel.text
cell.textLabel.text=[NSString stringWithFormat:#"%# : %#",[arr1 objectAtIndex:indexPath.row],[arr2 objectAtIndex:indexPath.row]];
Of course there might be a better way specific to your case, but this should help you build that.

do some work in the background and return the result

I'm trying to get the ID from a tag, using a library.
I came up with the following. the loop that's looks for a tag is done in the background and I get a correct result in tagAsString.
-(void) readTag {
NSLog(#"readTag");
unsigned char * tagUID = (unsigned char *) malloc(M1K_UID_SIZE * sizeof(char));
//work to do in the background
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ERR ret;
while ((ret = scanner->IsTagAvailable(tagUID)) != ERR_TAG_AVAILABLE) {
NSLog(#"ret: %d", ret);
}
//main thread
dispatch_async( dispatch_get_main_queue(), ^{
if(ret == ERR_TAG_AVAILABLE) {
NSLog(#"tag available");
NSString *tagAsString = [[[NSString alloc] initWithFormat:#"%x%x%x%x", tagUID[0],tagUID[1],tagUID[2],tagUID[3]] retain];
}
});
});
}
I would like to be able to return that value so I would be able to call:
NSString * myTag = [self readTag];
is that possible ?
Thanks for your help, Michael
It is possible, however the problem with returning a string from that function is that it would need to hold up your calling thread whilst you perform the work in the background - thus losing the benefit of the background thread. (dispatch_sync is what you would use to do that - however I would not recommend it).
When using blocks it is best to restructure your program to fit better with the asynchronous paradigm. When the work is complete it should notify whatever is waiting on the result by sending a message to it with the result. In your example you would put this in the block of code you dispatch on the main queue.
#interface TagManager
- (void)fetchTag;
- (void)tagFetched:(NSString *)tag;
#end
#implementation TagManager
- (void)fetchTag {
// The following method does all its work in the background
[someObj readTagWithObserver:self];
// return now and at some point someObj will call tagFetched to let us know the work is complete
}
- (void)tagFetched:(NSString *)tag {
// The tag read has finished and we can now continue
}
#end
Then your readTag function would be modified as so:
- (void)readTagWithObserver:(id)observer {
...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...
dispatch_async(dispatch_get_main_queue(), ^{
if (tag is ok) {
[observer tagFetched:tag];
}
});
});
}
The main idea is that you need to split your processing up into two stages
requesting that some work is done (fetchTag in my example)
process the result when it finishes (tagFetched: in my example)

thread safe in iOS development

From wikipedia explanation about thread-safety,thread safe codes can be run in multithreads.
For iOS 3.x, UIKit is not thread safety, since 4.0, UIKIt is thread safety.
In our implementations, we can use synchronized to build thread safety codes. My questions about thread safety are:
1). How to detect thread safety coding issue with instruments tools or other ways ?
2). Any good practices to write thread safety codes for iOS development ?
since 4.0, UIKIt is thread safety.
Basically, UIKit is not thread-safe. Only drawing to a graphics context in UIKit is thread-safe since 4.0.
1) Hmm, I also want to know about that :-)
2) How about Concurrency Programming Guide?
To make a non thread-safe object thread safe, consider using a proxy (see the code below). I use it for example for NSDateFormatter, which is not a thread safe class, when parsing data in a background thread.
/**
#brief
Proxy that delegates all messages to the specified object
*/
#interface BMProxy : NSProxy {
NSObject *object;
BOOL threadSafe;
}
#property(atomic, assign) BOOL threadSafe;
- (id)initWithObject:(NSObject *)theObject;
- (id)initWithObject:(NSObject *)theObject threadSafe:(BOOL)threadSafe;
#end
#implementation BMProxy
#synthesize threadSafe;
- (id)initWithObject:(NSObject *)theObject {
object = [theObject retain];
return self;
}
- (id)initWithObject:(NSObject *)theObject threadSafe:(BOOL)b {
if ((self = [self initWithObject:theObject])) {
self.threadSafe = b;
}
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [object methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if (self.threadSafe) {
#synchronized(object) {
[anInvocation setTarget:object];
[anInvocation invoke];
}
} else {
[anInvocation setTarget:object];
[anInvocation invoke];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector {
BOOL responds = [super respondsToSelector:aSelector];
if (!responds) {
responds = [object respondsToSelector:aSelector];
}
return responds;
}
- (void)dealloc {
[object release];
[super dealloc];
}
#end

Resources