Where does the main-loop go when creating an iOS app? - objective-c

I am writing an iOS app for the iPhone in Xcode and I have created some classes as well as their methods inside their respective .h and .m files (that's two classes so basically I have two pairs of .h & .m files)
I now I want to start writing my main loop that will be executed whenever the user hits on the play button, but where exactly do I do that ?
Do I do that in ViewController.m ? e.g. inside this method :
- (IBAction)playPressed:(UIButton *)sender
{
// main loop executed in here ?
// or simply message to the main loop to start executing is *sent* from here ?
}
I read about a similar question in here and someone was suggesting AppDelegate. Now would that be AppDelegate.m or AppDelegate.h ? And if that's the case do I just start writing code or do I include everything inside something like :
int main(int argc, char **argv)
{
....
}
in the Appdelegate file?
I tried to simply start instantiating classes and declaring generic methods (not belonging to any particular class that is..) in a game.m file I created and I get a initializer element is not a compile-time constant warning as soon as I try instantiating anything
Any help? Coming from c++ it would really help me to clarify once and for all in which file exactly to write my main loop and whether I should wrap it in some kind of an int main() function..
thanks!
PS :
Just in case it makes any difference, my ViewController will only consist of a play button that would start the execution of my main loop whenever its pressed, and a stop button that would terminate the execution of the main loop
I have created their respective methods in ViewController.m :
- (IBAction)playPressed:(UIButton *)sender
{
//
}
- (IBAction)stopPressed:(UIButton *)sender
{
// ??
}
which are for the time being empty :)

The programming methodoly on iOS is different from the C++ methodoly.
In C++ , indeed , you would have to make an infinite loop and get the touches , draw everything , etc at each frame.
Until the player presses "exit" and you break the loop.
On iOS , things are done differently:
You already have a main.m file in which you have a main function.
That starts up the app delegate. That app delegate tells you when the app finished launching , goes to background , comes in foreground , etc.
When the app finished launching , you go to your first actual screen.
There , you ADD subviews. You don't draw them at each frame. That is done automatically for you once you have added the view to a parent view.
The programming on iOS is based on events. You don't have to check for touches and see if the
touch location is on a button and then call the method of that button.
Instead , you set a callback method for the button and it's called automatically for you once the button is pressed.
Of course , you first need to alloc the button and add it to a parent view.
Once you get used to this event based programming model , you will for sure like it.
At the start it may seam very different and maybe it doesn't make sense to you , but don't worry.
Comming from a C++ background is surely a good start.
Cheers,
George
EDIT: In that case , I can give more specific info:
So , you go from the AppDelegate in your first screen. Let's call it MainAppScreen.
Now , you need to add those 2 buttons and set selectors ( callback methods ) for them. I can see you already did that.
Now :
- (IBAction)playPressed:(UIButton *)sender
{
running = TRUE;
[self performSelectorInBackground:#selector(myLoop) withObject:nil];
}
- (IBAction)stopPressed:(UIButton *)sender
{
running = FALSE;
}
- (void) myLoop
{
while(running)
{
//this is your loop. You can code in here.
}
}
Where running is an instance variable in the MainAppScreen class.
Hope this helps.
Cheers!

Every iOS app, as well as every executable file has an entry point - this is the main(). You can't have more than one entry points of an executable.And if you look closely into the project you will see that there is an automatically generated main.m file in the Supporting Files group in Xcode's navigator, which looks like this:
#import <UIKit/UIKit.h>
#import "MyAppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
}
}
What you want to do is not clear enough, but it's a good start reading about the structure and the lifecycle of iOS apps, objective-c syntax, get familiar with the UIKit and at least some of the frameworks Apple provide.

You don't have a main in iOS apps (well, technically you do have a main, but you don't have to worry about writing it). That's all handled for you. The runloop is all done for you too. All you have to do is create your button and then tell it (via addTarget method) which method to run when it gets pressed.
Update:
This is pseudo(ish) code for what you'd need to do....
[startButton addTarget:#selector(startPressed:)];
[stopButton addTarget:#selector(stopPressed:)];
-(void)startPressed {
backgroundThread = [[NSThread alloc] initWithWhateverYouWantToRun];
[backgroundThread start];
}
-(void)stopPressed {
[backgroundThread stop];
}
In your background thread, if you want to update the UI, you would call sendMessageOnMainThread (or something similar - can't remember the exact details at the moment!)

Related

"[GCController controllers]" does not contain any controllers that were connected prior to application launch

"[GCController controllers]" does not contain any controllers that were connected prior to application launch
TLDR;
I am trying to implement gamepad input on macOS using the Game Controller Framework. When invoked in my code, [GameController controllers] always returns an empty list until new controllers are connected. It never reflects gamepads connected to macOS prior to application launch, except if you disconnect them and reconnect them while the app is running. Does anyone know what I need to do to make controllers populate with pre-launch connections?
Full question
Now that Apple has added support for Xbox and Playstation controllers to the GameController framework, I'm trying to use it for gamepad input on a C++ game engine I'm developing. I'm using the framework instead of IOKit in order to "future-proof" my games to support additional controller types in the future, as well as to simplify my own input handling code.
Like many other game engines, I've foregone using NSApplicationMain() and nib files in favor of implementing my own event loop and setting up my game window programmatically. While my "Windows style" event loop appears to be working correctly, I've discovered that [GCController controllers] does not. The array it returns is always empty at launch, and will only ever reflect controllers that are connected while the game is running. Disconnecting a pre-connected controller does not trigger my GCControllerDidDisconnectNotification callback.
Here is a simplified version of my event loop:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
// Create application
[NSApplication sharedApplication];
// Set up custom app delegate
CustomAppDelegate * delegate = [[CustomAppDelegate alloc] init];
[NSApp setDelegate:delegate];
// Activate and launch app
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
[NSApp activateIgnoringOtherApps:YES]; // Strictly speaking, not necessary
[NSApp finishLaunching]; // NSMenu is set up at this point in applicationWillFinishLaunching:.
// Initialize game engine (window is created here)
GenericEngineCode_Init(); // <-- Where I want to call [GCController controllers]
NSEvent *e;
do
{
do
{
// Pump messages
e = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: nil
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e)
{
[NSApp sendEvent: e];
[NSApp updateWindows];
}
} while (e);
} while (GenericEngineCode_Run()); // Steps the engine, returns false when quitting.
GenericEngineCode_Cleanup();
}
return 0;
}
I've confirmed that even when using [NSApp run] instead of [NSApp finishLaunching], the behavior is the same. As best as I can tell, the problem is that there's something NSApplicationMain() does that I'm not doing, but that function is a black box -- I can't identify what I need to do to get controllers to populate correctly. Does anyone know what I'm missing?
The closest thing I could find to an explanation of this problem is this answer, which suggests that my app isn't getting didBecomeActive notifications, or that at the least, the private _GCControllerManager isn't getting a CBApplicationDidBecomeActive message. I'm not a professional macOS developer, though: I don't know if this actually applies to my situation, or how I'd go about correcting the problem if it does.
After a huge amount of time searching, I found the answer on my own. It turns out that my code wasn't the problem -- the problem was that my Info.plist file was having its CFBundleIdentifier value stripped out due to a problem with my build system. It appears that the Game Controller Framework needs the bundle identifier to correctly populate [GCController controllers] at launch. While a missing CFBundleIdentifier would have been a problem anyway, as a Windows person it didn't occur to me that the identifier might be used for things besides the App Store, so I let it slide until now.
If someone else has this problem, make sure that CFBundleIdentifier isn't missing or empty in Info.plist in your assembled app bundle. In my case with Premake, I had to manually set PRODUCT_BUNDLE_IDENTIFIER with xcodebuildsettings so that $(PRODUCT_BUNDLE_IDENTIFIER) would get properly replaced in Info.plist.

Create a "Command Line Tool" project from xcode, and setup and run NSApplication. The cursor in this application can't change

I make a little tool built on "Command Line Tool" project.
Everything works well except the Cursor in this tool.
Here is the launch code:
int main(int argc, const char * argv[]) {
NSApplication * app = [NSApplication sharedApplication];
MyDelegate * delegate = [[MyDelegate alloc] init];
app.delegate = delegate;
[app run];
return 0;
}
And in MyDelegate, I create window, view, etc. However I override the resetCursorRects function in the View, but nothing happened.
If the same code run in an Application project, everything will be OK.
All the thing I tried include:
Make a subclass from NSApplication and override run function to handle uesr event.
Make a NSTrackingArea in the view to update cursor.
Perform selector(run) on mainThread and wait until done.
However they didn't work at all.
Now all the reason I guessed is the function NSApplicationMain(argc, argv) is not equal with [app run].
What's the difference? Can any one help me?
PS: If I use NSApplicationMain(argc, argv), xcode will give me an error that I must have a bundle and Info.plist. I don't want create an application, because it's to fat.
A program which is not a bundled app is, by default, a background-only process. A bundled app is, by default, a foreground-capable process. (One can set the LSUIElement key in an app's Info.plist to change that.)
Only the active app can set the cursor. A background-only or UI element (a.k.a. accessory) process can't become the active app.
If you want your program to be able to be activated and control the cursor, you can use [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular] to make it a foreground-capable app process. Note that this will make it appear in the Dock and Command-Tab application switcher (but with a generic-executable icon).
Of course, it's just easier and simpler to make your program a full-fledged bundled app.

afnetworking async background task handle response when viewcontroller changes

My application allows a user to create an item for listing on an eCommerce site. The user goes through a number of screens adding images and information until they need to create the item on the store.
The final upload screen has an UIViewController using AFNetworking has two services to call that:
1) Calls an image upload webservice and returns some ID's. On success it calls (2).
2) Calls another service using these returned ID's as part of the request.
This process is started when the user hits the Submit button.
What I would like to happen is the following:
The users clicks Submit and the process starts in the background
The current storyboard scene returns to the start screen to allow the user to create another item whilst the previous is still running.
As the code for the service calls and handling the responses from them are in the UIViewController once the scene changes the UIViewController will no longer be running on the stack so what will happen to the service response etc?
If I create a separate class to do the work I'll loose the object reference when the scene changes. If the method is still processing would it be garbage collected?
Should I be sticking this on a background thread using Grand Central Dispatch?
For more detail, here's an example.
I usually have a class named NetWrapper which manages whole network related thing.
.h
#interface NetWrapper : NSObject
+ (instancetype)shared;
#pragma mark - APIs
- (void)requestVersion;
#end
.m
static NetWrapper *_netWrapper;
#implementation NetWrapper
+ (instancetype)shared
{
if(_netWrapper == nil)
{
_netWrapper = [[NetWrapper alloc] init];
}
return _netWrapper;
}
#pragma mark - APIs
- (void)requestVersion
{
// do something
}
If you have a singleton class like this, you can alway have the same instance with
[NetWrapper shared]
and invoke instance method like below.
[[NetWrapper shared] requestVersion];

How do I loop a sound, while some condition, using the AVAudioPlayer framework here?

EDIT : trying to implement Adam B's answer now...
I have a crashSound.wav file that I have put in my Supporting Files folder of my xCode project
I'm now trying to make it play inside a while loop, but the documentation isn't very clear as to how exactly I can do that. I know I have to create a delegate object somewhere (I guess my Game class) and get notifications as to whether stopButtonPressed is false and whether the file has finsihed playing so that it can loop and play again while the stopButtonPressed condition is false.. and I know I shouldn't be doing that by calling the [crashSound play] method but I'm not sure how to do it.. Any help?
#interface Game()
{
// code...
AVAudioPlayer *crashSound;
// code...
}
#end
#implementation Game
- (id) init
{
// code...
NSURL *crashSoundFile = [[NSURL alloc] initWithString:#"crashSound" ];
crashSound = [[AVAudioPlayer alloc] initWithContentsOfURL:crashSoundFile error:NULL];
// code...
}
-(void) play // this is my "main" method that will be called once the playButton is pressed
{
while(!self.stopButonPressed)
{
[crashSound play];
}
}
#end
You're structuring your code wrong for how most multimedia libraries would work (including AVPlayer, for example).
Instead of having a while loop, you would start the sound playing, and check if your condition is true each time it completes. You may need to trigger the "next" thing based on a timer or in that callback when the sound completes, instead of having the loop.
See this question and good answer for an example of how to setup AVPlayer and the notification function playerItemDidReachEnd:: Looping a video with AVFoundation AVPlayer?

App crashes in main after adding delegate protocol; no error code

In my app, a graph in one view can be dragged to a second view so that the new graph replaces the second view (like a copy/paste effect with a drag and drop feature). The app works if the delegate protocol is taken out so that the second view handles the change in function itself. When the protocol is added, the app crashes in the main file at
return UIApplicationMain(argc, argv, nil, NSStringFromClass([Load_CreatorAppDelegate class]));.
There isn't any error output other than the standard (lldb). Even when I take out the call to the delegate (keeping in the code), the app crashes. I know that it has to be related to the protocol code, though, because it worked fine before that.
Here is part of the code for the second view (BeamView):
[self drawSupportsAtLeftPoint:self.beamBottomLeft rightPoint:self.beamBottomRight inContext:context :leftPin :rightPin];
BOOL pt = NO;
if (self.tempLoad) {
//self.loadGraph = [self.dataSource changeToTempLoad:self]; NOTE #1
//if (self.tempPtLoad.x != 0 || self.tempPtLoad.y != 0) pt = YES;
pt = [self changeLoad];
[self drawLoadWithFunction:self.loadGraph inContext:context fromPoint:self.beamTopLeft toPoint:self.beamTopRight withAlpha:0.3 isPointLoad:pt inBlack:YES];
}
else {
self.loadGraph = ^(int x) {return x/15;};
[self drawLoadWithFunction:self.loadGraph inContext:context fromPoint:self.beamTopLeft toPoint:self.beamTopRight withAlpha:1 isPointLoad:pt inBlack:NO];
}
self.tempLoad = NO;
NOTE #1: These lines that are commented out are the ones that call on the delegate. Those two methods and their implementation are the only changes I made.
I'm completely confused, any help would be greatly appreciated. What are possible reasons the app will crash in the main file?
Ok! I feel kind of stupid, but it turned out that the crash didn't have anything to do with delegation (well, kind of). I had deleted outlets in the ViewController.m file without disconnecting them in IB, which was causing the crash.
I'd forgotten that I'd done that, and so it took me a while to think of it--it wasn't until I went back to an older saved version that I saw the differences.