Must AVAudioPlayer be instantiated from a ViewController class? - objective-c

I'm hoping someone can help as I'm new to iOS / objective C and very puzzled. I'm trying to play a simple sound using AVAudioPlayer as follows:
NSString *path = [[NSBundle mainBundle] pathForResource:#"soundFile" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: path];
self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:NULL];
[self.player play];
I am using ARC so I also have in my .h file, the following reference to my player so that ARC does not deallocate my player prematurely:
#property (nonatomic, strong) AVAudioPlayer *player;
This code works just fine and plays my sound PROVIDED that I run this code from a ViewController or my application's AppDelegate.
However if I cut and paste this very same code, plus all the necessary #includes and the #property and add them into another class in the same application but which is not a ViewController, and call the code there then no error is raised but no sound is played.
It is exactly the same code just called on a different class??
Why would it not work?
I have looked and looked for a similar post but nowhere have I seem exactly this scenario addressed. Many thanks if you can help me- would be much appreciated.
To clarify the issue-- here is how I call this code on another class say a class I have named Audio Tester, I would write in AppDelate say
#import "AppDelegate.h"
#import "AudioTester.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
AudioTester * tester = [[AudioTester alloc]init];
[tester playAudio];
}
where AudioTester playAudio is defined as
#import "AudioTester.h"
#implementation AudioTester
-(void) playAudio {
NSString *path = [[NSBundle mainBundle] pathForResource:#"soundFile" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: path];
self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:NULL];
[self.player play];
}
#end
with AudioTester.h as follows
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#interface AudioTester : NSObject
#property (nonatomic, strong) AVAudioPlayer *player;
-(void) playAudio;
#end
Stepping through this code, it gets called just fine but it does not play sound?
If you can help that would be much appreciated. I'm totally stumped.

a little conceptual explanation about your code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
AudioTester * tester = [[AudioTester alloc]init];
[tester playAudio];
}
1.
if you use ARC then the instance won't be not kept alive after the scope runs out, therefore the tester object will be immediately released, so in your case the object is deallocated before it could do anything – that is the reason why you can't hear any noise or sound.
if you want to keep your tester instance alive independently from your current scope where you inited in, you need to create like e.g. a property which is outside of the scope; you could put that into a class extension for instance:
#interface AppDelegate ()
// ...
#property (nonatomic, strong, nullable) AVAudioPlayer * tester;
// ...
#end
2.
we don't put anything like this into the AppDelegate.m file, the app delegate class basically handles the app-related events globally like launch, terminate, etc... briefly, the global and major events of the app's life cycle in runtime.
you can read more about its purpose in the official docs.
3.
you may use the –applicationDidFinishLaunching: method deliberately for initing your app, but I feel necessary to mention you may want to put everything inside the method –application:didFinishLaunchingWithOptions: instead.
you can read more about the initial procedure as well in the same documentation.
TL;DR
the answer to your original concern: NO, a class can be inited and instantiated in any other instance of any type of classes in general, but you need to worry about keeping the object alive as long as you want to use it.

Related

React Native and Objective C delegates

I am quite new to react native and and the bridging mechanism with native code, especially when the framework has delegates. Assume I am trying to bridge the following framework:
#protocol BRPtouchNetworkDelegate;
#class PLNetworkModule;
#interface BRPtouchNetworkManager : NSObject <NSNetServiceBrowserDelegate,NSNetServiceDelegate>
#property(retain, nonatomic) NSMutableArray* registeredPrinterNames;
#property(assign, nonatomic) BOOL isEnableIPv6Search;
- (int)startSearch: (int)searchTime;
- (NSArray*)getPrinterNetInfo;
- (BOOL)setPrinterNames:(NSArray*)strPrinterNames;
- (BOOL)setPrinterName:(NSString*)strPrinterName;
- (id)initWithPrinterNames:(NSArray*)strPrinterNames;
- (id)initWithPrinterName:(NSString*)strPrinterName;
#property (nonatomic, assign) id <BRPtouchNetworkDelegate> delegate;
#end
#protocol BRPtouchNetworkDelegate <NSObject>
-(void) didFinishSearch:(id)sender;
#end
The following is the bridge module I implemented:
RCTBRPtouchNetworkManager.h
#import <React/RCTBridgeModule.h>
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#interface RCTBRPtouchNetworkManager : NSObject <RCTBridgeModule, BRPtouchNetworkDelegate>
#end
RCTBRPtouchNetworkManager.m
#import "RCTBRPtouchNetworkManager.h"
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#import <React/RCTLog.h>
#implementation RCTBRPtouchNetworkManager {
BRPtouchNetworkManager *_networkManager;
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(#"Pretending to create an event %# at %#", name, location); //a dummy method to test the bridge
}
RCT_EXPORT_METHOD(startSearchWithTimeout:(int)time) {
RCTLogInfo(#"Bridge started search with time %d", time);
_networkManager = [[BRPtouchNetworkManager alloc] init];
_networkManager.delegate = self; //I'm setting delegate here
_networkManager.isEnableIPv6Search = NO;
NSString * path = [[NSBundle mainBundle] pathForResource:#"PrinterList" ofType:#"plist"];
if( path )
{
NSDictionary *printerDict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *printerList = [[NSArray alloc] initWithArray:printerDict.allKeys];
[_networkManager setPrinterNames:printerList];
} else {
RCTLogInfo(#"PrinterList path not found");
}
// Start printer search
[_networkManager startSearch: 5.0];
}
- (void)didFinishSearch:(id)sender {
NSLog(#"didFinishedSearch"); //this delegate method is not called
}
#end
I can easily call the dummy method and see the results in the logs. However, the delegate method didFinishSearch() is never called. I call this from javascript as follows:
componentDidMount() {
let networkManager = NativeModules.BRPtouchNetworkManager;
networkManager.startSearchWithTimeout(5.0);
}
I there something I am missing? Am I implementing delegate properly? Is this kind of functionality even possible (can't seem to not since the delegate method was used by iOS community for a long time). Your help is much appreciated.
EDIT
I found that adding the following to my bridge manager file made the delegate to fire (thanks to this post)
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
However, even though this solves the problem, I'd like a more technical understanding on what is going on here since I can't seem to exactly grasp it. Thank you
I know this isn’t an an answer to the post but for the bit where you’ve asked for a more technical understanding - dispatch_get_main_queue(); puts the delegate method responses on to the main thread. Since JS is single threaded any process on the background thread won’t be visible to it.

Playing NSSound in NSObject (Mac App with Objective-C)

I'm making a game that needs to play music. To make my code more manageable, I wanted to make an NSObject that takes care of the sounds (like fading, playing sounds in a playlist, etc). I have this code:
NSSound *music = [[NSSound alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.filename ofType:self.fileExtention] byReference:NO];
[music play];
This code works when I place it in the AppDelegate.m file but is does not work when I place it in the New NSObject Class.
Code in NSObject Class (named Music):
- (void)playMusic:(NSString *)fileName ofType:(NSString *)type
{
NSSound *music = [[NSSound alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.filename ofType:self.fileExtention] byReference:NO];
[music play];
NSLog(#"Works!");
}
I call the method with this code in the AppDelegate.m:
[[[Music alloc] init] playMusic:self.fileName ofType:self.extension];
When this is executed it does log "Works!" which means the code is executed.
So the exact same code works in the AppDelegate but not in a NSObject Class. Does anyone know if playing an NSSound in a NSObject Class is even possible (if not, why?), and if so how to edit the code so that it works? It would make my code look a lot less messy ;)
Try called methods on main thread,
dispatch_async(dispatch_get_main_queue(), ^{
// do work here
});

Can't bind NSArray with NSArrayController

I have an array (_websites) which returns 2 results (i can see the records using NSLog).
What I am trying to do is to display those 2 records in NSTableView that has 3 columns. I make numerous attempts to bind the content of my array with the NSArrayController, without any success.
Here is the .h file
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#interface CombinedViewController : NSViewController <NSTableViewDataSource>
#property (nonatomic,strong) NSManagedObjectContext *mObjContext;
#property AppDelegate *appDelegate;
#property (strong) IBOutlet NSArrayController *combinedRecordsArrayController;
#property (nonatomic,strong)NSArray *websites;
#property (weak) IBOutlet NSTableView *tableView;
#end
the .m file code:
#import "CombinedViewController.h"
#import "Website.h"
#import "Customer.h"
#import "Hosting.h"
#interface CombinedViewController ()
#end
#implementation CombinedViewController
- (void)viewDidLoad {
[super viewDidLoad];
_appDelegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
self.mObjContext = _appDelegate.managedObjectContext;
[self getCombinedResutls];
}
-(NSArray *)getCombinedResutls {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Website" inManagedObjectContext:self.mObjContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [self.mObjContext executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"Error:%#",error);
}
_websites = [_mObjContext executeFetchRequest:fetchRequest error:nil];
for (Website *ws in _websites) {
Customer *cust = ws.customerInfo;
Hosting *host = ws.hostingInfo;
NSLog(#"Website: %#, Customer: %#, Hosting Provider: %#",ws.websiteUrl, cust.customerName, host.hostingProvider);
}
return fetchedObjects;
}
#end
I am trying to learn how to do it both ways, using cocoa binding and programmatically, so any kind solution will be appreciated. Links with some up to date tutorial will be also very welcomed.
I just forgot to mention...I bind the NSArrayController with ContentArray in Controller Content, and then I bind the NSTableView with the NSArrayController, as well as my Table Column,but I am getting empty NSTableView...no error is shown in console whatsoever.
If you use direct iVar access-- _websites-- to write the websites property's iVar, the KVO notification that the binding depends upon never happens.
If you instead use self.websites = or more explicitly [self setWebsites: ..., then you will trigger a KVO notification to the array controller that the value of the websites property has been updated.
Instead, the array controller in the Xib is unarchived and bound to websites before viewDidLoad, so at that point, websites is nil. And subsequently, you never trigger any KVO notification about websites changing value because you explicitly avoid using the websites accessor setWebsites and instead use direct instance variable access. So the AC never knows that websites changes and the table never reflects any value for websites except nil.
In general never use the instance variable to access a property's value unless you have a very good reason to do so and fully understand why you're doing so.

UITableView and delegates

I have an iPad app in progess but I'm having difficulty catching the selection of a row in my table view. I know this is because I haven't defined my delegate properly yet but, after 2 hours on the net, it still isn't making much sense.
What I'm trying to do is pass the selected table row item to a new view that displays info based on the selection - pretty standard.
I set up the tableViewController sub class using the option to create it as a UITableViewController subclass which, unless I am wrong, incorporates the delegates (UITableViewDelegate and UITableViewDataSource) automatically.
In the didSelectRowsAtIndex method I'm trying to create a DetailViewController. I've tried with a nib file and creating one purely in code but the class is never created. I'm missing a step I'm sure of it but I can't find what it is. At some point shouldn't I be defining what function I want to access with the selected row? But where? How?
In what I considered was my best attempt, I created the DetailViewController, set a string variable in the detailViewController to the selected row, and then tried to add the detailViewController view to display. I figured I could then use the viewDidload to call the next function but the view never got displayed on screen.
Some basic guidence would be nice. Or a decent tutorial would be nice. No calls to read the relevant docs please, I've been over it and right now I need a example to pull things together.
Thanks,
Steve
I think you are missing this line in your didSelectRowAtIndexPath: method
[self presentModalViewController:controller animated:YES];
where controller is an object of class DetailViewController
Yeah, maybe paste the code snippet will be easier to figure out what's going on here. And are those delegate(didSelectRowAtIndexPath:) methods being called correctly?
Try this,
this goes in didSelectRowAtIndexPath
MoreInfoTable *moreInfoView = [[MoreInfoTable alloc] initWithStyle:UITableViewStyleGrouped];
//in the MoreInfoTable, make properties like titles etc.
[self.navigationController pushViewController:moreInfoView animated:YES];
[moreInfoView release];
}
here's an example of an MoreInfoTable.h
#interface MoreInfoTable : UITableViewController {
NSMutableArray *moreInfo;
NSURL *getDirections;
NSURL *getWebsite;
NSMutableString *getPhoneNumber;
NSString *address;
NSString *footer;
float lat, lon;
}
-(void)goToWebsite;
-(void)goToMaps;
-(IBAction)addToFavorites:(id)sender;
-(void) callNumber;
#property (nonatomic,retain) NSURL *getDirections;
#property (nonatomic,retain) NSURL *getWebsite;
#property (nonatomic,retain) NSMutableString *getPhoneNumber;
#property (nonatomic,retain) NSString *footer;
#property (nonatomic,retain) NSString *address;
#property (readwrite) float lat;
#property (readwrite) float lon;
#end
now back in the other file in which you declare the table, you can say
MoreInfoTable *moreInfoView = [[MoreInfoTable alloc] initWithStyle:UITableViewStyleGrouped];
//in the MoreInfoTable, make properties like titles etc.
moreInfoView.title = #"TITLE!";
//etc.
[self.navigationController pushViewController:moreInfoView animated:YES];
[moreInfoView release]; //

EXC_BAD_ACCESS iPhone Dev

I am very new to development in Objective C but have a lot of experience in object orientated development in c# ASP. I've been trying to make a simple iPhone app where a PickerView control is accessing a data source.
In my header file (InstaTwitViewController.h) I have declared the following:
#interface InstaTwitViewController : UIViewController
<UIPickerViewDataSource, UIPickerViewDelegate> {
NSArray* activities;
NSArray* feelings;
}
I now try to inititialise my arrays in the viewDidLoad functions in my InstaTwitViewController.m file:
activities = [[NSArray alloc] initWithObjects:#"sleeping", #"eating", #"thinking", nil];
feelings = [[NSArray alloc] initWithObjects:#"psyched", #"sad", #"happy", nil];
but when I run the code on the simulator I get a Thread 1: Program recieved signal "ESC_BAD_ACCESS" error on the first line of code (where I allocate data to the activities array).
I put a break point on the line and it recognises it as an NSArray called activities but says "Invalid Summary" at the end.
Does anyone know why I am getting this error? I have looked at many threads about the ESC_BAD_ACCESS error but none have seemed to have helped me.
.h
#interface InstaTwitViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate> {
NSArray* activities;
NSArray* feelings;
}
#property (nonatomic, retain) NSArray* activities;
#property (nonatomic, retain) NSArray* feelings;
#end
.m
#import "your.h"
#implementation InstaTwitViewController
#synthesize activities, feelings;
- (void)viewDidLoad {
[super viewDidLoad];
[self.activities addObject ~ /* Do your code here */];
}
#end
Becareful you should stay on self.~ if you don't want to lose your value.
By the way, I am Korean~ ^^;