I use GCDWebServer for create a simple server where users can download file from document, from a button I show an UIAlertView where there is the IP Address of iphone, then on click I star the server, the problem is there after start of server the UI is blocked, the uialertview not dismiss.
This is my code
if (alertView.tag == 999) {
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
self.webServer = [[GCDWebServer alloc] init];
[self.webServer addHandlerForBasePath:#"/" localPath:documentsDir indexFilename:nil cacheAge:3600];
[self.webServer runWithPort:8080];
// I try this 2 solution but no one work
//[self starsServer]
//[self performSelector:#selector(startServer) onThread:[NSThread mainThread] withObject:nil waitUntilDone:NO];
}
- (void) startServer {
[self.webServer start];
}
where is the error?
Do not use -runWithPort: as it will block the thread. This method should only be used on Mac command line tools.
Just call -start after creating the server. This will not block the current thread but start the server in the background. Then you can use Xcode debugger to find out what is happening afterwards that hangs the UI (most likely the current function being executed is not returning).
Related
I just built my first app and now I started testing it on my phone. It looks great when I first launch the app after building it, the launch images appears and then my json data is loaded via NSURL and displays properly on the app. But when I close down the app, update the data via php and mysql and re open it the launch image does not appear and my app is not updated. Is it possible to have the app launch like it did when I first launched it, always have launch image and also get the new data?
Here is my code if it helps.
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadJSON];
}
- (void)loadJSON
{
Reachability *networkReachability = [Reachability reachabilityForInternetConnection];
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];
if (networkStatus == NotReachable) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Error" message: #"Connection Failed" delegate: self cancelButtonTitle:#"Refresh" otherButtonTitles:nil]; [alert show]; [alert release];
});
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:#"http://url.com/GetData.php"];
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:nil];
NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSArray *firstItemArray = array[0];
NSString *yesNoString = firstItemArray[0];
NSString *dateString = firstItemArray[1];
NSString *timeString = firstItemArray[2];
NSString *homeString = firstItemArray[3];
NSString *awayString = firstItemArray[4];
NSString *lastUpdatedString = firstItemArray[5];
dispatch_async(dispatch_get_main_queue(), ^{
self.YesOrNo.text = yesNoString;
self.date.text = [#"For " stringByAppendingString:dateString];
self.time.text = timeString;
self.home.text = homeString;
self.away.text = awayString;
self.lastUpdated.text = lastUpdatedString;
self.lastUpdatedText.text = #"Last Updated";
self.vs.text = #"vs";
});
});
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[self loadJSON];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
[_YesOrNo release];
[_date release];
[_time release];
[_vs release];
[_home release];
[_away release];
[_lastUpdatedText release];
[_lastUpdated release];
[super dealloc];
}
#end
If someone can point me in the right direction, that would be great and appreciated, thanks.
When you hit the round home button when running an app, it just puts it into the background, where it continues to run in a type of catatonic state. When you tap its icon again, it just wakes up, and doesn't re-launch. If you'd like to have your app completely quit when the user hits the home button, use the info.plist option "UIApplicationExitsOnSuspend" also known as "Application does not run in background." Set this to YES in your info.plist, and you'll get a fresh start every time. You can access this by clicking on your project in Xcode in the Project Navigator mode, and select the "info" tab at the top middle.
As it is your first app, you should try reading about the various devices and os versions.
Read about various application states, also the methods that are present in the AppDelegateClass, that get called when the app enters into various states,try reading about them.
So what has happened in your case is the device that you are using is Multitasking one. So when you press the home button or the sleep button the game goes to background, and is not killed. So next time when you tap on the application icon on your device, it brings it back to the foreground and does not relaunch it hence your Viewdidload method won't be called and your changes won't get reflected.
So, now to terminate your app, you can go through this link
http://www.gottabemobile.com/2013/02/10/how-to-close-apps-on-iphone/
Hope this helps.
I have a read-only text field that I use as a log display. I have a operation that removes all the files in app's document directory. I want to insert a line of log when I remove each file. But the text field is only updated when the whole operation got finished. How do I fix this?
Here is my code:
NSFileManager *fm = [[[NSFileManager alloc] init] autorelease];
NSError *error = nil;
for (NSString *fileName in array) {
NSString *filePath = [DOCUMENT_PATH_VALUE stringByAppendingFormat:#"/%#", fileName];
[fm removeItemAtPath:filePath error:&error];
if (!error) {
NSString *log = [NSString stringWithFormat:#"removed success: %#", fileName];
[self logThis:log];
}else{
NSString *log = [NSString stringWithFormat:#"remove failed: %#, %#", fileName, [error localizedDescription] ];
[self logThis:log];
-(void)logThis:(NSString*) text{
NSRange range = NSMakeRange([updateLogTextView.text length], [text length]);
updateLogTextView.text = [updateLogTextView.text stringByAppendingFormat:#"%#\n", text];
[updateLogTextView scrollRangeToVisible:range];
}
You need to move your long-running operation into a background thread/queue and make sure that your UI-updating code is always executed on the main thread.
Example:
- (void)processFiles:(NSArray *)array
{
//You need to create your own autorelease pool in background threads:
NSAutoreleasePool *pool = [NSAutoreleasePool new];
//...
NSString *log = ...;
[self performSelectorOnMainThread:#selector(logThis:) withObject:log waitUntilDone:NO];
//...
[pool release];
}
You would then start your operation using:
[self performSelectorInBackground:#selector(processFiles:) withObject:files];
Using Grand Central Dispatch with blocks would be another option.
Refer to the Threading Programming Guide and the Concurrency Programming Guide for more in-depth information.
You need to look into Threading and making that call Asynchronous. You are currently blocking your main thread (assuming it's called from there). You need to separate the two so a separate thread does the heavy operation while your main thread is updated with the new text in the UI.
I've just started ios dev but sounds like you need to redraw that UIText I'm not sure how though I'd assume that when your log writes that you could dealloc your original UIText object the realloc it and it should redraw with the new log message. Only problem is that it will most likely update very very fast so redrawing wouldnt be worth it unless ios has a sleep function?
Something like:
Alloc and init text
Then in for loop after each write or new message in log dealloc your text and realloc and init your text
This should redraw the UIText object and update the text, if it doesnt expect a memory write error/app crash
Here is my code.. why it isn't gonna add object to array when on device?
Maybe array can't be read from file? I write file to documents directory, but what may be wrong? THanks...
if (contactsToProcess==nil)
{
contactsToProcess=[[NSMutableArray alloc] init];
NSLog(#"Array with objs initialized");
}
contactsToProcess=[NSKeyedUnarchiver unarchiveObjectWithFile:fPath()];
[contactsToProcess addObject:cont];
NSLog(#"Object added to file:");
for(Contact *cn in contactsToProcess)
{
NSLog(#"%#", cn.fName);
}
[NSKeyedArchiver archiveRootObject:contactsToProcess toFile:fPath()];
fPath() does:
NSString* PathforResources(NSString* filename)
{
NSString *leftpart=[filename stringByDeletingPathExtension];
NSString *extension=[filename pathExtension];
return [[NSBundle mainBundle] pathForResource:leftpart ofType:extension];
}
NSString* DocumentsDirectory()
{
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDirectory, YES);
return [paths objectAtIndex:0];
}
NSString* fPath()
{
return [DocumentsDirectory() stringByAppendingPathComponent:#"quickdial.xml"];
}
I suppose you should change your code into:
contactsToProcess=[NSKeyedUnarchiver unarchiveObjectWithFile:fPath()];
if (contactsToProcess==nil)
{
contactsToProcess=[[NSMutableArray alloc] init];
NSLog(#"Array with objs initialized");
}
...
then everything should work. Some explanation: the code as you posted will only work as you expect if the file quickdial.xml is already present; it will be specifically unable to create the file with a proper content (or to create it at all, I don't know what is the exact effect of archiving a nil pointer). Indeed, when the file does not exist, after executing you original code:
if (contactsToProcess==nil)
{
contactsToProcess=[[NSMutableArray alloc] init];
NSLog(#"Array with objs initialized");
}
contactsToProcess=[NSKeyedUnarchiver unarchiveObjectWithFile:fPath()];
contactsToProcess will be nil; this means that all successive operation on it will have no effect and that you are archiving a nil pointer. I.e, when the file does not exist, it will not be created with the array as you expect. This is what happens on your device.
My hypothesis is that you ran the program in a different state on your simulator, and that your array got archived into the file successfully. Then you changed the program to the current state; on the simulator it keeps working because the file is there and contains an archived array. On the device the file is not created or just contains nil, so it does not work as you expect.
I would suggest removing the app from the device (tap on the app icon until you enter the dashboard editing mode, then tap on the delete button); then apply the change I suggested above and re-run.
What is the path returned by fPath()?
On Simulator you have access to the full file system (at least as far as your logged in Mac OS X user can access), on device only to the sand-boxed home directory.
Also check for unmatched lower-/UPPER-case since the device file system is case sensitive, Mac OS X is not by default.
I'm working on an app that writes data out to a .plist from a view and then pulls it in to populate some UILabels in another view. I've done this several times in the past and it has worked fine on both the device and simulator. This time it's working fine on the simulator but not on the device. I've been through looking at all the connections and code but just can't find why it's not functioning. My suspicion is that for some reason the data is not at the end of the file path, but if it isn't I can't understand why this is the case.
I've Also tried cleaning the targets through the menu and deleting and reinstalling the app on the testing device in the hope that this may rectify the situation, but no joy.
Can anyone offer some insight into why I'm having this problem and how to rectify it? It's why it works on the simulator but not the phone that is confusing me, especially when I've used the same code in the past to make fully functioning apps. I'm using xCode 4 and testing on an iphone 4:
The code is as follows. First I check to see if there is data at the end of a data file path like this:
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirctory = [paths objectAtIndex:0];
return [documentsDirctory stringByAppendingPathComponent:memberDetails];
}
Then in viewWillAppear I get the data and use it to populate UIlabels in the View. a Few of the labels are visible or invisible based on the data they contain. The viewWillAppear is as follows:
-(void)viewWillAppear:(BOOL)animated
{
// Check data file path and if it exists, load data from there.
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
///All of the following handles the reloading of data from the plist.
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
memName.text = [array objectAtIndex:0];
memAddress.text = [array objectAtIndex:1];
memOccupation.text = [array objectAtIndex:2];
sherex.text = [array objectAtIndex:3];
if ([[array objectAtIndex:3] isEqualToString:#"winstonwinston"]) {
cvName.hidden = NO;
memName.hidden = NO;
cvAddress.hidden = NO;
memAddress.hidden = NO;
cvOccupation.hidden = NO;
memOccupation.hidden = NO;
sherex.hidden = YES;
altText.hidden = YES;
}
else {
cvName.hidden = YES;
memName.hidden = YES;
cvAddress.hidden = YES;
memAddress.hidden = YES;
cvOccupation.hidden = YES;
memOccupation.hidden = YES;
sherex.hidden = YES;
altText.hidden = NO;
}
[array release];
}
[super viewWillAppear:animated];
}
One thing perhaps worth pointing out is that the if/else statement doesn't seem to be working on the device either. In other words everything in the view is visible. To me this would seem to suggest that the problem might not be the data since if should be able to determine whether or not there is an item called 'winstonwintson' in existence or not and display the hidden/showing elements of the view accordingly.
If anyone has some insight please help! I'm totally stuck. Thanks for taking the time to read this.
If it involves files and works on the simulator but not on the device it's usually one of two problems.
you use filenames that work ok on a case insensitive file system (the simulator) but not on the device (case sensitive filesystem).
The only way I can imagine how this affects you is when you try to copy an initial dataset to the documents directory. For example if you try to copy from your bundle, check the path that is returned by [[NSBundle mainBundle] pathForResource:#"foo" ofType:#"bar"]; If the path is nil you have to check your filenames again, they have to be exactly the same.
you write to directories that are not writeable on the device.
One very unlikely possibility is that memberDetails contains something like /../foo and you try to write in a non writeable directory. On the simulator you can write your files wherever you want, on the device such accesses will fail.
So the two questions you should try to answer are:
how do you save the data in the documents directory?
what is the content of memberDetails? (how does the returned path from dataFilePath look?)
EDIT:
NSLog is always a great help in debugging such problems, just print out your paths and check if they are ok.
Something like this:
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:#"foo" ofType:#"bar"];
NSLog(#"Source: %#", sourcePath);
if (![[NSFileManager...
or you could use a if condition
if (sourcePath == nil) {
NSLog(#"Something is wrong with the sourcePath, because sourcePath == nil!");
}
EDIT2:
Since neither of those view configuring parts are called most likely if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) is never true. This means the file does not exist.
Delete app from phone, clean project, re-build and run. It is sometimes helpful to turn off backgrounding, to do that add:
<key>UIApplicationExitsOnSuspend</key><true/>
to your app plist.
I'm trying to load two standard-issue style singletons: http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html when my iPhone app is loaded. Here's my code:
- (void) applicationDidFinishLaunching:(UIApplication *)application {
// first, restore user prefs
[AppState loadState];
// then, initialize the camera
[[CameraModule sharedCameraModule] initCamera];
}
My "camera module" has code that checks a property of the AppState singleton. But I think what's happening is a race condition where the camera module singleton is trying to access the AppState property while it's in the middle of being initialized (so the property is nil, and it's re-initializing AppState). I'd really like to keep these separate, instead of just throwing one (or both) into the App Delegate. Has anyone else seen something like this? What kind of workaround did you use, or what would you suggest?
Thanks in advance!
Here's the loadState method:
+ (void)loadState {
#synchronized([AppState class]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *file = [documentsDirectory stringByAppendingPathComponent:#"prefs.archive"];
Boolean saveFileExists = [[NSFileManager defaultManager] fileExistsAtPath:file];
if(saveFileExists) {
sharedAppState = [[NSKeyedUnarchiver unarchiveObjectWithFile:file] retain];
} else {
[AppState sharedAppState];
}
}
}
The line that calls AppState in CameraModule:
- (void)initCamera {
...
if([AppState sharedAppState].captureSession != nil) {
...
}
}
The weirdest thing was happening... I was accessing a property of AppState in my RootViewController, and the order of app execution was the culprit.
RootViewController is loaded in my MainWindow.xib file, and apparently the XIB loading/shenanegans happens BEFORE anything inside applicationDidFinishLaunching so loadState was being called late in the game.
The solution was to move [AppState loadState] to my RootViewController's init method.