Transmitting a string via UDP using cocoaasyncsocket - ios4

I've been teaching myself Objective-C over the past few months; I'm building an iPhone app for my company. I started as (and still am) a complete novice, but until now I have had no problems easily finding answers to all my questions at various locations online.
For the final, and most important, piece of my app, I need to send a simple string to an address/port via UDP when a button is pressed. The string, address, and port are all variables pulled from an object passed to my view controller.
I have been digging around for two days looking at solutions and reading examples, but everything seriously reads like Greek to me. I'm not sure what major hunk of knowledge I seem to have missed out on, but I'm at a total loss. I learned about cocoaasyncsocket, and how "simple" it is, and it sounds perfect for what I need, but I just can't seem to wrap my mind around it. I'm really hoping someone here can help break it down for me into simple terms.
Here is a snippet of the code I've been trying, but with no luck. This code is from my viewController, with AsyncUdpSocket.h imported:
-(IBAction)udpButtonTwoPressed:(id)sender {
NSData *myData;
myData = [[NSData alloc] initWithData:([selectedObject
valueForKey:#"udpCommandTwo"])];
AsyncUdpSocket *mySocket;
mySocket = [[AsyncUdpSocket alloc] initWithDelegate:self ];
NSError *error = nil;
if (!([mySocket connectToHost:([selectedObject
valueForKey:#"serverIPAddress"]) onPort:([[selectedObject
valueForKey:#"serverPort"] intValue]) error:&error])) {
NSLog(#"Can't Connect Cause: %#", error);
abort();
}
[mySocket close];
[mySocket release];
[myData release];
}
What am I doing wrong here?

There are two things that stand out from your example.
I don't see you writing anything to the socket. Have a look at writeData:withTimeout:tag.
CocoaAsyncSocket is asynchronous so in your example everything is going out of scope. If you are really wanted to write synchronously there is an example in the reference.
NSString *customRunLoopMode = #"MySyncWrite";
[asyncSocket addRunLoopMode:customRunLoopMode];
[asyncSocket writeData:didBackgroundData withTimeout:TIMEOUT_NONE tag:TAG_BG];
syncWriteComplete = NO;
BOOL runLoopReady = YES;
while (runLoopReady && !syncWriteComplete)
{
runLoopReady = [[NSRunLoop currentRunLoop] runMode:customRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
[asyncSocket removeRunLoopMode:customRunLoopMode];
HTH

Related

GPUImage terminates due to [AVAssetWriter startWriting] Cannot call method when status is 3'

I am having an issue running GPUImage. I have modified SimpleVideoFileFilter program(replaced the filter with a chromakeyfilter) and am using my own video. My program is terminating due to the following error:
[AVAssetWriter startWriting] Cannot call method when status is 3'
I have gone through the forums but not sure why the moviewriter is closing and then someone is writing to it.
I am using iPhone4 running iOS 7.0
Any clues are greatly appreciated. Thanks much!
Check whether your destination file exists already. If it does, remove it.
I was trying to add the file to a directory which did not exist. Example : /Videos/Video.mov , leaving it just /Video.mov worked.
Ok, I have a few ideas for you.
When you say "it just shows a frame and never plays the video" we have a good indication that your entire processing pipeline from start to finish is functional exactly once, then stops working.
That tells us that you are stringing things together correctly, but some of the components don't exist longer than a single frame buffer cycle, and subsequently the whole process stops.
it looks like filter and movieWriter are scoped to the class (I'm assuming they're not properties from the lack of an underscore, _filter and _movieWriter). So they will live on after this method has finished (correct me if I'm wrong...)
I think where you are encountering trouble is your (GPUImageView*)displayView
This should probably be declared as a class property (although it could work as just a variable) and then instantiated through the nib or the viewDidLoad method of the view controller.
As you have it now, this line: GPUImageView* filterView = (GPUImageView*)displayView; is making an assignment for filterView which is not used (and therefore unnecessary). It's not clear if displayView really is an instance of GPUImageView or if it will still be in existence when the current method finishes. (in fact you say it "is a UIView that I have programmatically created")
displayView will have to be a subclass of GPUImageView for this whole thing to work, and it will have to be scoped to the class, and not the method.
Declare it like this:
#property (strong, nonatomic)GPUImageView* displayView;
and then instantiate it and add it to your view hierarchy from within viewDidLoad
movieFile1 = [[GPUImageMovie alloc] initWithURL:movieFileURL1];
movieFile2 = [[GPUImageMovie alloc] initWithURL:movieFileURL2];
movieFile2.runBenchmark = YES;
movieFile2.playAtActualSpeed = NO;
filter = [[GPUImageChromaKeyBlendFilter alloc] init];
[(GPUImageChromaKeyBlendFilter *)filter setColorToReplaceRed:0.0 green:1.0 blue:0.0];
[(GPUImageChromaKeyBlendFilter *)filter setThresholdSensitivity:0.4];
GPUImageView *filterView = (GPUImageView*)displayView;
[filter addTarget:displayView];
[movieFile1 addTarget:filter];
[movieFile2 addTarget:filter];
NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Movie.m4v"];
unlink([pathToMovie UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];
movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(1920.0, 1280.0)];
[filter addTarget:movieWriter];
movieWriter.shouldPassthroughAudio = YES;
movieFile1.audioEncodingTarget = movieWriter;
[movieFile1 enableSynchronizedEncodingUsingMovieWriter:movieWriter];
[movieWriter startRecording];
[movieFile1 startProcessing];
[movieFile2 startProcessing];
[movieWriter setCompletionBlock:^{
[filter removeTarget:movieWriter];
[movieWriter finishRecording];
}];
if (outputPath) {
finalURL = [[stongObj tempFileURL] copy];
DebugLog(#"Start Filter Processing :%#",finalURL);
DebugLog(#"movieUrl :%#",movieUrl);
// [CSUtils removeChuckFilePaths:#[outputPath]];
//Create Image Movie Object
_movieFile = [[GPUImageMovie alloc] initWithURL:outputPath];
//_movieFile = [[GPUImageMovie alloc] initWithURL:[[NSBundle mainBundle] URLForResource:#"videoviewdemo" withExtension:#"mp4"]];
_movieFile.runBenchmark = NO;
_movieFile.playAtActualSpeed = YES;
_movieFile.delegate = self;
//Movie Writer Object
_movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:finalURL size:CGSizeMake([UIScreen mainScreen].bounds.size.height,[UIScreen mainScreen].bounds.size.height)];
//_movieWriter.delegate = self;
//Create Selecetive GPU Image Filter
[stongObj setGpuOutputFilter:selectedVideoFilterType];
//Create Group Filter
groupFilter = [[GPUImageFilterGroup alloc] init];
[groupFilter addTarget:imageOutputFilter];
// Only Single Filter is implemented.
//Apply Initial and Terminal Filter
[(GPUImageFilterGroup *)groupFilter setInitialFilters:[NSArray arrayWithObject:imageOutputFilter]];
[(GPUImageFilterGroup *)groupFilter setTerminalFilter:imageOutputFilter];
//_movieWriter -> groupFilter ->_movieFile
[_movieFile addTarget:groupFilter];
[groupFilter addTarget:_movieWriter];
_movieWriter.shouldPassthroughAudio = YES;
_movieFile.audioEncodingTarget = _movieWriter;
[_movieFile enableSynchronizedEncodingUsingMovieWriter:_movieWriter];
//Start Recording
[_movieWriter startRecording];
//Start Processing
[_movieFile startProcessing];
__weak typeof(self) weekSelf=self;
[_movieWriter setCompletionBlock:^{
__strong typeof(self) stongSelf=weekSelf;
DebugLog(#"Movie Write Completed");
//Finish Recording.
[stongSelf.movieWriter finishRecording];
//Release all object
// [self releaseAllObject];
//remove movieUrl,audioUrl,outputPath
[CSUtils removeChuckFiles:#[movieUrl,audioUrl,outputPath]];
}];
[_movieFile startProcessing]; app get crash in iOS 8 on this line but working fine on iOS 7
#Seasia Creative ,I have no enough reputation to add a comment by that list,I create a new list to answer U.
I check the output URL,console log "/var~~~~/tmpmerge.mp4",so i realize that ,i miss a "/" --->"/var~~~~/tmp/merge.mp4".
If the url is no correct, project runs into the same error.
hope to help some.

Saving a UIManagedDocument - speed improvements

I have the following code which is used on my View Controller whenever its property 'document' (of type UIManagedDocument) is set.
I'm not sure if other people do, but I find the notion of concurrency in Core Data very confusing, the documents have a go at explaining it but it's still hard to get to grips with. For this reason, I was wondering if people had any ideas of how to speed up the code I use for saving the newly set UIDocument if it does not exist. This code does work, if others would like to use it.
My main goal is to try and make the time it takes for the document to save and load much faster. It currently takes about 20 seconds to do this, which is way too long!
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (![[NSFileManager defaultManager] fileExistsAtPath:self.documentDatabase.fileURL.path]) {
[self.documentDatabase saveToURL:self.documentDatabase.fileURL
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (success) {
NSLog(#"Saved %#", self.documentDatabase.localizedName);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self.documentDatabase.managedObjectContext performBlockAndWait:^{
dispatch_async(dispatch_get_main_queue(), ^{
[Book newBookWithTitle:self.documentDatabase.fileURL.lastPathComponent.stringByDeletingPathExtension inManagedObjectContext:self.documentDatabase.managedObjectContext];
[self saveDocumentWithCompletionHandler:^(bool success) {
if (success) {
[self setIsDocumentHidden:NO];
}
}];
NSLog(#"Added default note to %#", self.documentDatabase.fileURL.lastPathComponent);
});
}];
});
} else {
NSLog(#"Error saving %#", self.documentDatabase.fileURL.lastPathComponent);
}
}];
} else {
[self openDocumentWithCompletionHandler:nil];
}
20 seconds sounds a bit extreme, unless you're dealing with a vast database. Have you tried Time Profiling the saving process in Instruments?
Even if the delay is entirely within framework code, sometimes by diving deep into the time profile you can get some hint of what it's doing that is taking so long. For example, its only tangentially related, but by doing this I discovered saving UIManagedDocuments that were synced over iCloud were totally clobbered performance wise by the presence of inverse relationships in the data model.

Using selected NSManagedObject across different controllers

I have an entity called Practice and I use a View Controller called SelectorViewController to select one of the practices, selectedPractice. I then return selectedPractice to a view Controller called RegularViewController where I display some of the selectedPractice attributes. All of this works fine. However the app has a number of other View Controllers which can be reached by modal segues from instances of RegularViewController. As a result, if I leave and then come back to RegularViewController, selectedPractice is reset as null. I would also like to save selectedPractice so that it is available at app initialisation if it has previously been set in SelectorViewController. How do I achieve this by making selectedPractice persistent across the app, and available at runtime?
Regards
Thanks to the post above, which was great, I managed to sort it. Here is my code, which may be very clumsy, but it works.
Firstly, as I loaded the fetchedObjects into a PickerView in SelectorView Controller, I set an attribute "isSelectedPractice" to "NO" with the following code:
for (Practice *fetchedPractice in [self.fetchedResultsController fetchedObjects]) {
[fetchedPractice setValue:#"NO" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
I then identified for the selected Practice:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
Practice *practice = [[self.fetchedResultsController fetchedObjects] objectAtIndex:row];
self.selectedPractice = practice;
NSLog(#"The '%#' practice was selected using the picker", self.selectedPractice.name);
}
as the view Segue'd back to RegularViewController I set the isSelectedPractice attribute for selectedPractice to YES. I kept it this late as I didn't want more than one selection in the PickerView to result in multiple objects with isSelectedPractice YES.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SavedPractice Segue"])
{
[self.selectedPractice setValue:#"YES" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
NSLog(#"Setting SelectedPractice as '%#' in RegularViewController with isSelectedPractice as '%#'",self.selectedPractice.name,self.selectedPractice.isSelectedPractice );
RegularViewController *rvc= segue.destinationViewController;
rvc.delegate = self;
rvc.selectedPractice = self.selectedPractice;
}
else {
NSLog(#"Unidentified Segue Attempted!");
}
}
I then set the following Predicate in the setupFetchedResultsController method of RegularViewController:
request.predicate = [NSPredicate predicateWithFormat:#"isSelectedPractice = %#", #"YES"];
Many thanks for the help
Without seeing your actual project, one way I know will work but might be a little too round a bout would be to add an attribute "isSelectedPractice" to your entity. You could make it a BOOL, but I've had mixed results with BOOL's in Core Data, I prefer to just leave it as a NSString and set it to "yes" or "no". Then when you pull it down, modify it or add it to core Data as a entity with isSelectedPractice set to "yes". Then in your other controllers, do a
if (self.managedObjectContext == nil) {
self.managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
then do a fetch request to get entities with a predicate which is looking for isSelectedPractice equaling "yes". If you need actual code samples on how to do this let me know and I'll edit them in.

How to get multiple placemarks from CLGeocoder

Whatever address i give to the geocoder ([geocoder geocodeAddressString:completionHandler:), it always puts only one object in the placemarks array.
I there any way to get multiple results (like in Maps app) from which the user can select one?
Apple's native geocoding service is provided by the MapKit framework. The important object in this framework is MKLocalSearch, which can geocode addresses and return multiple results.
MKLocalSearch returns back 10 results in mapItems of type MKMapItem. Each MKMapItem contains a MKPlacemark object, which is a subclass of CLPlacemark.
Here's an example using MapKit's MKLocalSearch:
MKLocalSearchRequest* request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = #"Calgary Tower";
request.region = MKCoordinateRegionMakeWithDistance(loc, kSearchMapBoundingBoxDistanceInMetres, kSearchMapBoundingBoxDistanceInMetres);
MKLocalSearch* search = [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
yourArray = response.mapItems; // array of MKMapItems
// .. do you other logic here
}];
I've done some sniffing on the packets and it seems that CLGeocoder doesn't connect to Google's geocoding service, but to Apple's. I've also noticed that I get only one placemark from there every time.
If you want something more sophisticated you should use Google's or other geocoding. I use SVGeocoder (https://github.com/samvermette/SVGeocoder), which has a very similar API to CLGeocoder.

CoreData autosaving and not loading all data after autosave

I have an NSPersistentDocument subclass using NSManagedObject subclasses for my data.
When a new document is opened, I do some initializing of data structures (trivial amount of populating fields). What I've noticed is that the Untitled document gets autosaved, and when the application re-opens, that document gets loaded. If the application quits, the user doesn't (by default) get prompted with the save dialog. If the window closes, the user does.
First question:
I want to call up the save dialog when the user quits the application. I don't want this Untitled document hanging around (under normal circumstances). I either want it saved or trashed.
I attempted to fill out:
- (void)applicationWillTerminate:(NSNotification *)aNotification
In order to trigger the document to be saved. Calling save: on the context at this point gives an error. From what I can tell, this is because the user hasn't yet saved the file on their own. In addition, calling [self close]; or [[self windowForSheet] close]; close the window without saving.
How can I force the save dialog to come up? How can I trash the untitled document?
Second question (no, I can't count):
Since when the application starts, there may or may not be an Untitled document to deal with, I'm trying to keep track of the state in another model. I've already found that the initial data (to which I referred earlier) is present when the Untitled document came up. My other model has some metadata, including a success flag/state for the populated data. Once the populated data is all in place and correct, the state indicates as such. Unfortunately, while my populated data is being loaded when the app starts with a pre-existing Untitled document, the metadata class is not.
Please excuse the roughness of the code, at this point, I'm mucking it up until I can see that it's working how I want before I polish it back off:
- (bool) createGameState {
NSEntityDescription* description = [NSEntityDescription entityForName:[GameState name] inManagedObjectContext:[self managedObjectContext]];
NSFetchRequest* req = [[NSFetchRequest alloc] init];
[req setEntity:description];
NSError *error = nil;
NSArray *array = [[self managedObjectContext] executeFetchRequest:req error:&error];
[req release];
req = nil;
GameState* result = nil;
if (array) {
NSUInteger count = [array count];
if (!count) {
// Create the new GameState.
DebugLog(#"Creating GameState");
result = [NSEntityDescription insertNewObjectForEntityForName:[GameState name] inManagedObjectContext:[self managedObjectContext]];
[result setIsLoaded:[NSNumber numberWithBool:NO]];
} else {
if (count > 1) {
NSLog(#"WARNING: Potentially Corrupt Game State. found: %lu", count);
}
result = [array objectAtIndex:0];
if ([result isLoaded]) {
[self variantLoaded];
} else {
// In this case, we have an aborted set-up. Since the game isn't
// playable, just refuse to create the GameState. This will
// force the user to create a new game.
return NO;
}
}
} else {
DebugLog(#"error: %#", error);
}
[game setState:result];
return result;
}
Note that array is always present, and count is always zero. No, I'm not explicitly calling save: anywhere. I'm relying on the standard auto-save, or the user performing a save.
EDIT:
I installed the Core Data Editor app. It turns out the issue isn't on saving the data, but on loading it. (Note: Due to another issue, the app saves as binary when instructed to save as XML, which causes much head banging.)
I've broken it down to the simplest code, which should pick up all objects of type GameState in an array. It retrieves none, despite there clearly being objects of the appropriate type in the saved file:
NSManagedObjectContext* moc = [self managedObjectContext];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"GameState" inManagedObjectContext:moc];
NSFetchRequest* req = [[NSFetchRequest alloc] init];
[req setEntity:entity];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:req error:&error];
Array is not null, but [array count] is 0.
At this point, I'm guessing it's something simple that I'm overlooking.
Second EDIT:
I added -com.apple.CoreData.SQLDebug 5 and saved as SQLite. The call to executeFetchRequest does not generate any debug logs. I do see the INSERT INTO ZGAMESTATE entry show up in the logs. It seems that executeFetchRequest is not getting passed to the backend.
Third EDIT (this one burns):
I created a new xcode project, using core data (as I had with the other). I copied just this one function (stubbing where necessary) and plopped a call to it in windowControllerDidLoadNib. In this new project, the code above works.
Found the problem.
I errantly was loading objects in Document's - (id) init call. Moved to windowControllerDidLoadNib (which is what I did in the test version) and it worked fine.

Resources