Xcode - Privacy - Camera Usage Description won't ask the permission for the camera - ios

My app developed in Xcode with Objective-C should ask for the camera permission in order to reply to a SIP video call.
Although i wrote the Privacy - Camera Usage Description Key into my info.plist file the app only asks for the microphone permission and not for the camera permission and when I start a video call it crash.
This is my info.plist file:
My info.plist file
Also, If I go to app settings in iOS Settings there is no camera permission switch.
Update with code as requested
To make a video call i use the ABTO VoIP SDK.
AppDelegate.h
#import <UIKit/UIKit.h>
#import <AbtoSipClientWrapper/AbtoSipPhoneObserver.h>
#import "Global.h"
#include "GenDefs.h"
#define kNotifKey #"key"
#define kNotifKey_IncomingCall #"icall"
#define kNotifKey_IncomingMsg #"imsg"
#define kNotifCall_SessionId #"sid"
#define kNotifCall_RemoteInfo #"rinfo"
#define kNotifKey_IncomingIm #"iim"
#interface ImMessage : NSObject {
#public
NSString *from;
NSString *to;
NSString *text;
BOOL isRead;
}
#end
#interface AppDelegate : UIResponder <UIApplicationDelegate, AbtoPhoneInterfaceObserver> {
#public
UINavigationController *navController;
UILocalNotification *lastCallNotification;
BOOL checkIconLaunch;
NSMutableArray *imMessages;
}
+ (AppDelegate *)sharedInstance;
#property (readwrite,nonatomic) AbtoPhoneInterface* phone;
#property (retain, nonatomic) UIWindow *window;
#property (retain, nonatomic) UINavigationController *navController;
#property (strong, nonatomic) UILocalNotification *lastCallNotification;
#property (nonatomic) BOOL checkIconLaunch;
#property (nonatomic) BOOL connected;
- (void)addMessage:(ImMessage *)message;
- (NSMutableArray *)getIMs;
- (void)restoreIms;
- (void)storeIms;
#end
AppDelegate.m
#import "AppDelegate.h"
#interface AppDelegate () {
}
#end
#implementation AppDelegate
#synthesize phone;
+ (AppDelegate *)sharedInstance{
return (AppDelegate *)[UIApplication sharedApplication].delegate;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
phone = [AbtoPhoneInterface new];
[phone initialize:self];
return YES;
}
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return [[UISceneConfiguration alloc] initWithName:#"Default Configuration" sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
- (void)applicationWillTerminate:(UIApplication *)application {
[phone deinitialize];
}
#pragma mark Abto SIP SDK delegate
- (void)onRegistered:(NSInteger)accId {
//[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PHONE_EVENT object:#(PhoneEventsRegSuccess)];
}
- (void)onRegistrationFailed:(NSInteger)accId statusCode:(int)statusCode statusText:(NSString*)statusText {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PHONE_EVENT object:#(PhoneEventsRegFailed)];
}
- (void)onUnRegistered:(NSInteger)accId {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PHONE_EVENT object:#(PhoneEventsUnregSuccess)];
}
- (void)onRemoteAlerting:(NSInteger)accId statusCode:(int)statusCode {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_PHONE_EVENT object:#(PhoneEventsRemoteAlerting)];
}
- (void)onIncomingCall:(NSInteger)callId remoteContact:(NSString *)remoteContact {
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsIncoming) userInfo:#{CALL_ID_ARGUMENT:#(callId), CONTACT_ARGUMENT:remoteContact}];
}
- (void)onCallConnected:(NSInteger)callId remoteContact:(NSString *)remoteContact {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsConnected) userInfo:#{CALL_ID_ARGUMENT:#(callId)}];
}
- (void)onCallDisconnected:(NSInteger)callId remoteContact:(NSString *)remoteContact statusCode:(NSInteger)statusCode message:(NSString *)message {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsDisconnected) userInfo:#{CALL_ID_ARGUMENT:#(callId), STATUS_ARGUMENT:#(statusCode)}];
}
- (void)onCallAlerting:(NSInteger)callId statusCode:(int)statusCode {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsAlerting) userInfo:#{CALL_ID_ARGUMENT:#(callId), STATUS_ARGUMENT:#(statusCode)}];
}
- (void)onPresenceChanged:(NSString *)uri status:(PhoneBuddyStatus)status note:(NSString *)note {
}
- (void)onCallHeld:(NSInteger)callId state:(BOOL)state {
}
- (void)onToneReceived:(NSInteger)callId tone:(NSInteger)tone {
}
- (void)onTextMessageReceived:(NSString *)from to:(NSString *)to body:(NSString *)body {
}
- (void)onTextMessageStatus:(NSString *)address reason:(NSString *)reason status:(BOOL)status {
}
- (void)onTransferStatus:(NSInteger)callId statusCode:(int)statusCode message:(NSString *)message {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsTransfering) userInfo:#{CALL_ID_ARGUMENT:#(callId), STATUS_ARGUMENT:#(statusCode), MESSAGE_ARGUMENT:message}];
}
- (void)onZrtpSas:(NSInteger)callId sas:(NSString *)sas {
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsZrtpSas) userInfo:#{CALL_ID_ARGUMENT:#(callId), MESSAGE_ARGUMENT:sas}];
}
- (void)onZrtpSecureState:(NSInteger)callId secured:(BOOL)secured {
NSLog(#"ZRTP secured = %#", secured ? #"YES" : #"NO");
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsZrtpSecureState) userInfo:#{CALL_ID_ARGUMENT:#(callId), STATUS_ARGUMENT:#(secured)}];
}
- (void)onZrtpError:(NSInteger)callId error:(NSInteger)error subcode:(NSInteger)subcode {
NSLog(#"ZRTP error = %ld(subcode = %ld)", (long)error, (long)subcode);
// [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_CALL_EVENT object:#(CallEventsZrtpSecureState) userInfo:#{CALL_ID_ARGUMENT:#(callId), STATUS_ARGUMENT:#(error)}];
}
#end
VideoCallViewController.h
This is the "scene" presented when a SIP call is coming.
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
NS_ASSUME_NONNULL_BEGIN
#interface VideoCallViewController : UIViewController {
BOOL isRecording;
BOOL enableSpeaker;
BOOL isFrontCamera;
BOOL isVideoMute;
BOOL sendingVideo;
}
#property (weak, nonatomic) IBOutlet UIImageView *imageViewRemoteVideo;
#property (weak, nonatomic) IBOutlet UIButton *buttonPick;
#property (weak, nonatomic) IBOutlet UIButton *buttonHangUp;
#property (weak, nonatomic) IBOutlet UIButton *buttonHangUpLadge;
#property(nonatomic, retain) NSString *number;
#property(nonatomic, assign) NSInteger callId;
#property(nonatomic, assign) BOOL incoming;
- (IBAction)onButtonClick:(UIButton *)sender;
#end
NS_ASSUME_NONNULL_END
VideoCallViewController.m
#import "VideoCallViewController.h"
#import <AVFoundation/AVCaptureOutput.h>
#import <AVFoundation/AVCaptureDevice.h>
#import <AVFoundation/AVCaptureInput.h>
#import <AVFoundation/AVMediaFormat.h>
#import <QuartzCore/QuartzCore.h>
#define ACTION_SPEAKER_ON #"Turn Speaker On"
#define ACTION_SPEAKER_OFF #"Turn Speaker Off"
#define ACTION_OPEN_DTMF #"Send DTMF"
#define ACTION_START_RECORDING #"Start Recording"
#define ACTION_STOP_RECORDING #"Stop Recording"
#define ACTION_SHOW_RTT #"Show RTT"
#define kColorsDarkBlack [NSArray arrayWithObjects: \
(id)[[UIColor colorWithRed:.1f green:.1f blue:.1f alpha:0.7] CGColor], \
(id)[[UIColor colorWithRed:0.f green:0.f blue:0.f alpha:0.7] CGColor], \
nil]
#define kColorsBlue [NSArray arrayWithObjects: \
(id)[[UIColor colorWithRed:.0f green:.0f blue:.5f alpha:0.7] CGColor], \
(id)[[UIColor colorWithRed:0.f green:0.f blue:1.f alpha:0.7] CGColor], \
nil]
#define kColorsLightBlack [NSArray arrayWithObjects: \
(id)[[UIColor colorWithRed:.2f green:.2f blue:.2f alpha:0.7] CGColor], \
(id)[[UIColor colorWithRed:.1f green:.1f blue:.1f alpha:0.7] CGColor], \
(id)[[UIColor colorWithRed:0.f green:0.f blue:0.f alpha:0.7] CGColor], \
nil]
#interface VideoCallViewController ()
#end
#implementation VideoCallViewController {
NSInteger dtmfLen;
}
#synthesize imageViewRemoteVideo;
#synthesize buttonPick;
#synthesize buttonHangUp;
#synthesize buttonHangUpLadge;
- (id)init {
self = [super init];
if (self) {
isRecording = NO;
enableSpeaker = YES;
dtmfLen = 0;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onPhoneNotification:) name:NOTIFICATION_PHONE_EVENT object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onCallNotification:) name:NOTIFICATION_CALL_EVENT object:nil];
[UIDevice currentDevice].proximityMonitoringEnabled = YES;
AbtoPhoneInterface* phone = [AppDelegate sharedInstance].phone;
[phone setRemoteView:self.imageViewRemoteVideo];
// [phone setLocalView:viewLocalVideo];
if (self.incoming) {
// labelStatus.text = #"Incoming video call from";//NSLocalizedString(#"INVIDEOCALL_KEY", #"");
buttonHangUp.hidden = NO;
buttonPick.hidden = NO;
buttonHangUpLadge.hidden = YES;
} else {
self.callId = [phone startCall:self.number withVideo:YES];
// labelStatus.text = #"Dialing";//NSLocalizedString(#"VIDEOCALL_KEY", #"");
// buttonPick.hidden = YES;
// buttonHangUp.hidden = YES;
buttonHangUpLadge.hidden = NO;
}
/* labelRemoteParty.text = self.number;
labelRemoteParty.hidden = NO;
buttonActions.hidden = YES;
stopVideoButton.hidden = YES;
switchCameraButton.hidden = YES; */
// imageViewRemoteVideo.hidden = YES;
// viewLocalVideo.hidden = YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (self.callId == kInvalidCallId) {
[self closeView];
}
}
- (void)onCallNotification:(NSNotification *)notification {
}
- (void)onPhoneNotification:(NSNotification *)notification {
}
- (void)closeView {
/* if (self == [ApplicationDelegate.navController topViewController])
[ApplicationDelegate.navController popViewControllerAnimated:YES];
DLog(#"closeView"); */
}
- (IBAction)onButtonClick:(UIButton *)sender {
AbtoPhoneInterface* phone = [AppDelegate sharedInstance].phone;
if(sender == buttonHangUp || sender== buttonHangUpLadge) {
[phone hangUpCall:self.callId status:486]; /* TODO: 0 - use defalt status */
} else if(sender == buttonPick) {
[phone answerCall:self.callId status:200 withVideo:YES];
}
}
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController {
NSString* vcNumber;
NSInteger vcCallId;
BOOL vcIncoming;
}
- (void)viewDidLoad {
[super viewDidLoad];
AbtoPhoneConfig* config = [AppDelegate sharedInstance].phone.config;
// [config loadFromUserDefaults:SETTINGS_KEY];
config.regUser = #"myUsernameHere";
config.regPassword = #"myPassHere";
config.regDomain = #"sip.antisip.com";
[AppDelegate.sharedInstance.phone finalizeConfiguration];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onCallNotification:) name:NOTIFICATION_CALL_EVENT object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onCallNotification:) name:NOTIFICATION_CALL_EVENT object:nil];
}
- (void)onCallNotification:(NSNotification *)notification {
NSLog(#"Inizializzare CAll view");
NSInteger status = [[notification object] integerValue];
if (status == CallEventsIncoming) {
NSInteger callId = [[notification.userInfo valueForKey:CALL_ID_ARGUMENT] integerValue];
NSString *contact = [notification.userInfo valueForKey:CONTACT_ARGUMENT];
if (callId != kInvalidCallId) {
UIApplication *app = [UIApplication sharedApplication];
AppDelegate *appDelegate = [AppDelegate sharedInstance];
NSString *remotePartyNumber = [AbtoPhoneInterface sipUriUsername:contact];
UIApplicationState state = app.applicationState;
[appDelegate.phone setSpeakerphoneOn:YES];
if ((state == UIApplicationStateBackground) || (state == UIApplicationStateInactive)) {
UILocalNotification *localNotification = [UILocalNotification new];
if (localNotification) {
localNotification.alertBody = [NSString stringWithFormat:#"%# %#", remotePartyNumber, #"calling"];
// localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = ++app.applicationIconBadgeNumber;
localNotification.repeatInterval = 0;
NSDictionary *userInfo = #{ kNotifKey: kNotifKey_IncomingCall,
kNotifCall_SessionId : #(callId),
kNotifCall_RemoteInfo: contact };
localNotification.userInfo = userInfo;
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
// ApplicationDelegate.lastCallNotification = localNotification;
}
} else {
vcNumber = remotePartyNumber;
vcCallId = callId;
vcIncoming = YES;
[self performSegueWithIdentifier:#"segue1" sender:self];
/* VideoCallViewController *nextController = [appDelegate.phone isVideoCall:callId] ? [VideoCallViewController new] : [VideoCallViewController new];
nextController.number = remotePartyNumber;
nextController.callId = callId;
nextController.incoming = YES;
[appDelegate.navController pushViewController:nextController animated:YES]; */
}
}
} else if (status == CallEventsDisconnected) {
// ApplicationDelegate.lastCallNotification = nil;
}
}
- (void)onPhoneNotification:(NSNotification *)notification {
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([[segue identifier] isEqualToString:#"segue1"]) {
VideoCallViewController *nextController = [segue destinationViewController];
nextController.number = vcNumber;
nextController.callId = vcCallId;
nextController.incoming = vcIncoming;
}
}
#end
The call code is fine, because I tested it multiple times. The issue is that the app does not ask for the camera permission.

Related

ReactNative iOS sendEventWithName cause "RCTCallableJSModules is not set"

i've the following situation.
2 identical react-native apps (differs only for bundleId, app icon etc) structured like this:
-> project structure
My goal it's to emit an event from native side to the JS layer through the bridge when a push notification has been received or tapped by the user (assuming that the app is in foreground and app has finished launching).
On the first App the following code works as expected when i trigger a push notification to my simulator with the command xcrun simctl push <device-id> <bundleId> <filename>.apns, the second app crash immediatly with the following error:
Thread 1: "Error when sending event: pushDelivered with body: <the string passed as body>. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in CustomEventsEmitter, even though it's inherited from RCTEventEmitter."
-> xcode view
Here is the code implementation of RCTEventEmitter's sendEventWithName that provoke the assertion.
I don't know if it's a problem with my implementation. In 1 of the 2 apps works like a charm, in the other 💥.
Anyone can help me find the problem in the code ? Probably a problem with the bridge?
i've tried many times to reinstall pods, clean project and rebuild. The code works on the project A and not on the project B.. i cannot figure out the reason
AppDelegate.h
#import <React/RCTBridgeDelegate.h>
#import <React/RCTBridgeModule.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
#import <UserNotifications/UNUserNotificationCenter.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RCTBridgeModule, UNUserNotificationCenterDelegate>
#property (nonatomic, strong) UIWindow *window;
#property (nonatomic, strong) NSDictionary *receivedNotificationUserInfo;
#end
AppDelegate.mm
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
#import <UserNotifications/UserNotifications.h>
#import "CustomEventsEmitter.h"
#implementation AppDelegate
bool hasFinishedLaunching = false;
CustomEventsEmitter *customEventsEmitter = NULL;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
hasFinishedLaunching = true;
customEventsEmitter = [CustomEventsEmitter allocWithZone: nil];
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, #"MyAppName", initProps);
if (#available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
// Define UNUserNotificationCenter
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
return YES;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:#"index"];
#else
return [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
#endif
}
-(void)applicationDidBecomeActive:(UIApplication *)application
{
application.applicationIconBadgeNumber = 0;
}
// The method will be called on the delegate when the user responded to the notification by opening
// the application, dismissing the notification or choosing a UNNotificationAction. The delegate
// must be set before the application returns from applicationDidFinishLaunching:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler {
NSLog(#"didReceiveNotificationResponse response: %#", response);
NSDictionary *userInfo = response.notification.request.content.userInfo;
if (userInfo[#"_od"]){
// if no listeners has been registered yet, store the value
// this is the case when the notification was clicked from closed app
if(![customEventsEmitter hasListeners]) {
// handle this case ...
}
// if listeners has been registered, emit an event
// this is the case when the notification was clicked from foreground app
else {
[self emitPushTappedEvent:userInfo[#"_od"]];
}
}
if (completionHandler != nil) {
completionHandler();
}
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
NSDictionary *userInfo = notification.request.content.userInfo;
NSLog(#"User Info : %#", userInfo);
[self emitPushDeliveredEvent:userInfo[#"_od"]];
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
-(void)emitPushDeliveredEvent:(NSString*)value {
NSLog(#"emitPushDeliveredEvent called");
[customEventsEmitter sendEventWithName:#"pushDelivered" body:value];
}
-(void)emitPushTappedEvent:(NSString*)value {
NSLog(#"emitPushTappedEvent called");
[customEventsEmitter sendEventWithName:#"pushTapped" body:value];
}
#end
And this are the CustomEventsEmitter files:
CustomEventsEmitter.h
#ifndef CustomEventsEmitter_h
#define CustomEventsEmitter_h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface CustomEventsEmitter : RCTEventEmitter <RCTBridgeModule>
- (void)sendEventName:(NSString *)eventName body:(id)body;
- (bool)hasListeners;
#end
#endif
CustomEventsEmitter.m
#import "CustomEventsEmitter.h"
#implementation CustomEventsEmitter
{
bool hasListeners;
}
RCT_EXPORT_MODULE(CustomEventsEmitter);
+ (id)allocWithZone:(NSZone *)zone {
static CustomEventsEmitter *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
- (NSArray<NSString *> *)supportedEvents {
return #[#"pushDelivered", #"pushTapped"];
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
-(bool)hasListeners {
return hasListeners;
}
- (void)sendEventName:(NSString *)eventName body:(id)body {
if (hasListeners) {
NSLog(#"CustomEventsEmitter sendEventName emitting event: %#", eventName);
[self sendEventWithName:eventName body:body];
} else {
NSLog(#"CustomEventsEmitter sendEventName called without listeners: %#", eventName);
}
}
#end
HELP ME UNDERSTAND PLEASEEEE
Oh i've solved it!
It was a mistake of mine.
The AppModule didn't invoke the CustomEventsEmitter's methods correctly..
changing the code like below makes the events be emitted correctly through the RN bridge
-(void)emitPushDeliveredEvent:(NSString*)value {
NSLog(#"emitPushDeliveredEvent called");
[customEventsEmitter sendEventName:#"pushDelivered" body:value];
//[customEventsEmitter sendEventWithName:#"pushDelivered" body:value];
}
-(void)emitPushTappedEvent:(NSString*)value {
NSLog(#"emitPushTappedEvent called");
[customEventsEmitter sendEventName:#"pushTapped" body:value];
//[customEventsEmitter sendEventWithName:#"pushTapped" body:value];
}

Playing videos in ComponentKit

I am trying to get a video to display using ComponentKit. I want it to play on tapping the component. I found this Github Issue, so I know I need to use a CKStatefulViewComponent but I don't know what the suggested way to handle the tap event is using ComponentKit.
Here is the code I have so far:
#import "CDBVideoPlayerComponent.h"
#import <ComponentKit/ComponentKit.h>
#import <ComponentKit/CKStatefulViewComponentController.h>
#import <AVFoundation/AVFoundation.h>
#interface CDBVideoPlayerComponent()
#property (nonatomic, strong) AVPlayer *player;
#end
#implementation CDBVideoPlayerComponent
+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {
CKComponentScope scope(self, url);
CDBVideoPlayerComponent *component = [super newWithSize:size accessibility:{}];
component->_player = [[AVPlayer alloc] initWithURL:url];
return component;
}
#end
#interface CDBVideoPlayerComponentController : CKStatefulViewComponentController
- (void)handleTapForPlayer:(AVPlayer *)player;
#end
#implementation CDBVideoPlayerComponentController
+ (UIView *)newStatefulView:(id)context {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor darkGrayColor];
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
playerLayer.frame = view.bounds;
[view.layer addSublayer:playerLayer];
return view;
}
+ (void)configureStatefulView:(UIView *)statefulView forComponent:(CDBVideoPlayerComponent *)videoComponent {
__block AVPlayerLayer *layer = nil;
for (CALayer *currentLayer in statefulView.layer.sublayers) {
if ([[currentLayer class] isSubclassOfClass:[AVPlayerLayer class]]) {
layer = (AVPlayerLayer*)currentLayer;
break;
}
}
if (layer) {
layer.player = videoComponent.player;
} else {
layer.player = nil;
}
}
- (void)handleTapForPlayer:(AVPlayer *)player {
[player play];
}
#end
So I was able to find a solution, it's not very clean, and I'm not sure if this is how the ComponentKit devs at Facebook intended for how the right way to handle this case is supposed to look like but this solution works:
First, we need to create a seperate view that handles the actual video presentation. This is pulled from Apple's example
#import "CDBVideoPlayerView.h"
#implementation CDBVideoPlayerView
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer*)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
#end
Then, the components and the controller:
#import "CDBVideoPlayerComponent.h"
#import "CDBVideoPlayerView.h"
#import <ComponentKit/CKStatefulViewComponent.h>
#import <ComponentKit/CKStatefulViewComponentController.h>
#import <AVFoundation/AVFoundation.h>
#interface CDBVideoStateComponent : CKStatefulViewComponent
#property (nonatomic, strong) AVPlayer *player;
+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size;
#end
#implementation CDBVideoStateComponent
+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {
CKComponentScope scope(self, url);
CDBVideoStateComponent *component = [super newWithSize:size accessibility:{}];
component->_player = [[AVPlayer alloc] initWithURL:url];
component->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
return component;
}
#end
#interface CDBVideoStateComponentController : CKStatefulViewComponentController
#end
#implementation CDBVideoStateComponentController
+ (UIView *)newStatefulView:(id)context {
CDBVideoPlayerView *view = [[CDBVideoPlayerView alloc] init];
return view;
}
+ (void)configureStatefulView:(CDBVideoPlayerView *)statefulView forComponent:(CDBVideoStateComponent *)videoComponent {
statefulView.player = videoComponent.player;
}
#end
#interface CDBVideoPlayerComponent ()
#property (nonatomic, strong) AVPlayer *player;
#end
#implementation CDBVideoPlayerComponent
+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {
CKComponentScope scope(self, url);
CDBVideoStateComponent *component = [CDBVideoStateComponent newWithVideoURL:url size:size];
CDBVideoPlayerComponent *playerComponent = [super newWithComponent:component
overlay:
[CKButtonComponent
newWithTitles:{}
titleColors:{}
images:{}
backgroundImages:{}
titleFont:{}
selected:NO
enabled:YES
action:#selector(handleButtonPress:)
size:{}
attributes:{}
accessibilityConfiguration:{}
]
];
playerComponent->_player = component.player;
return playerComponent;
}
- (void)handleButtonPress:(id)sender {
if (self.player.status == AVPlayerStatusReadyToPlay) {
if (self.player.timeControlStatus == AVPlayerTimeControlStatusPaused || self.player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
[self.player play];
} else {
[self.player pause];
}
}
}
#end
Edit
I also found, what I think is, a cleaner solution by moving most of the code to the VideoPlayerView
#implementation VideoPlayerView
- (instancetype)init {
if (self = [super init]) {
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)]];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)]];
}
return self;
}
- (void)dealloc {
[self removeObserversForPlayer:self.player];
}
+ (Class)layerClass {
return [AVPlayerLayer class];
}
- (AVPlayer*)player {
return [(AVPlayerLayer *)[self layer] player];
}
- (void)setPlayer:(AVPlayer *)player {
[self removeObserversForPlayer:self.player];
[(AVPlayerLayer *)[self layer] setPlayer:player];
[self addObserverForPlayer:player];
}
- (void)addObserverForPlayer:(AVPlayer *)player {
if (player) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:player.currentItem];
}
}
- (void)removeObserversForPlayer:(AVPlayer *)player {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:player.currentItem];
}
- (void)itemDidFinishPlaying:(NSNotification *)notification {
[self.player seekToTime:kCMTimeZero];
[self.player pause];
}
- (void)handleTap:(id)sender {
if (self.player.status == AVPlayerStatusReadyToPlay) {
if (self.player.timeControlStatus == AVPlayerTimeControlStatusPaused || self.player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
[self.player play];
} else {
[self.player pause];
}
}
}
#end
Updated Components:
#import "VideoPlayerComponent.h"
#import "VideoPlayerView.h"
#import <ComponentKit/CKStatefulViewComponentController.h>
#import <AVFoundation/AVFoundation.h>
#import <ComponentKit/ComponentKit.h>
#interface VideoPlayerComponent ()
#property (nonatomic, strong) AVPlayer *player;
#end
#implementation VideoPlayerComponent
+ (instancetype)newWithVideoURL:(NSURL*)url size:(const CKComponentSize &)size {
CKComponentScope scope(self, url);
VideoPlayerComponent *component = [super newWithSize:size accessibility:{}];
component->_player = [[AVPlayer alloc] initWithURL:url];
component->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
return component;
}
#end
#interface VideoPlayerComponentController : CKStatefulViewComponentController
#end
#implementation VideoPlayerComponentController
+ (UIView *)newStatefulView:(id)context {
VideoPlayerView *view = [[VideoPlayerView alloc] init];
view.backgroundColor = [UIColor grayColor];
return view;
}
+ (void)configureStatefulView:(VideoPlayerView *)statefulView forComponent:(VideoPlayerComponent *)videoComponent {
statefulView.player = videoComponent.player;
}
#end

no known instance method for selector - Obj-C

For some reason I am getting this error in my code and cannot figure it out. I am trying to have a QR Scanner in my app for a class project. Thanks in advance.
ScannerViewController.h:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#protocol ScannerViewControllerDelegate;
#interface ScannerViewController : ViewController <AVCaptureMetadataOutputObjectsDelegate>
#property (nonatomic, weak) id<ScannerViewControllerDelegate> delegate;
#property (assign, nonatomic) BOOL touchToFocusEnabled;
- (BOOL) isCameraAvailable;
- (void) startScanning;
- (void) stopScanning;
- (void) setTorch:(BOOL) aStatus;
#end
#protocol AMScanViewControllerDelegate <NSObject>
#optional
- (void) scanViewController:(ScannerViewController *) aCtler didTapToFocusOnPoint:(CGPoint) aPoint;
- (void) scanViewController:(ScannerViewController *) aCtler didSuccessfullyScan:(NSString *) aScannedValue;
#end
ScannerViewController.m:
#import "ScannerViewController.h"
#interface ScannerViewController ()
#property (strong, nonatomic) AVCaptureDevice* device;
#property (strong, nonatomic) AVCaptureDeviceInput* input;
#property (strong, nonatomic) AVCaptureMetadataOutput* output;
#property (strong, nonatomic) AVCaptureSession* session;
#property (strong, nonatomic) AVCaptureVideoPreviewLayer* preview;
#end
#implementation ScannerViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewWillAppear:(BOOL)animated;
{
[super viewWillAppear:animated];
if(![self isCameraAvailable]) {
[self setupNoCameraView];
}
}
- (void) viewDidAppear:(BOOL)animated;
{
[super viewDidAppear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if([self isCameraAvailable]) {
[self setupScanner];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)evt
{
if(self.touchToFocusEnabled) {
UITouch *touch=[touches anyObject];
CGPoint pt= [touch locationInView:self.view];
[self focus:pt];
}
}
#pragma mark -
#pragma mark NoCamAvailable
- (void) setupNoCameraView;
{
UILabel *labelNoCam = [[UILabel alloc] init];
labelNoCam.text = #"No Camera available";
labelNoCam.textColor = [UIColor blackColor];
[self.view addSubview:labelNoCam];
[labelNoCam sizeToFit];
labelNoCam.center = self.view.center;
}
- (NSUInteger)supportedInterfaceOrientations;
{
return UIInterfaceOrientationMaskLandscape;
}
- (BOOL)shouldAutorotate;
{
return (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation]));
}
- (void)didRotateFromInterfaceOrientation: (UIInterfaceOrientation)fromInterfaceOrientation;
{
if([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft) {
AVCaptureConnection *con = self.preview.connection;
con.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
} else {
AVCaptureConnection *con = self.preview.connection;
con.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
}
}
#pragma mark -
#pragma mark AVFoundationSetup
- (void) setupScanner;
{
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
self.session = [[AVCaptureSession alloc] init];
self.output = [[AVCaptureMetadataOutput alloc] init];
[self.session addOutput:self.output];
[self.session addInput:self.input];
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
self.output.metadataObjectTypes = #[AVMetadataObjectTypeQRCode];
self.preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.preview.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
AVCaptureConnection *con = self.preview.connection;
con.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
[self.view.layer insertSublayer:self.preview atIndex:0];
}
#pragma mark -
#pragma mark Helper Methods
- (BOOL) isCameraAvailable;
{
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
return [videoDevices count] > 0;
}
- (void)startScanning;
{
[self.session startRunning];
}
- (void) stopScanning;
{
[self.session stopRunning];
}
- (void) setTorch:(BOOL) aStatus;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[device lockForConfiguration:nil];
if ( [device hasTorch] ) {
if ( aStatus ) {
[device setTorchMode:AVCaptureTorchModeOn];
} else {
[device setTorchMode:AVCaptureTorchModeOff];
}
}
[device unlockForConfiguration];
}
- (void) focus:(CGPoint) aPoint;
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if([device isFocusPointOfInterestSupported] &&
[device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
CGRect screenRect = [[UIScreen mainScreen] bounds];
double screenWidth = screenRect.size.width;
double screenHeight = screenRect.size.height;
double focus_x = aPoint.x/screenWidth;
double focus_y = aPoint.y/screenHeight;
if([device lockForConfiguration:nil]) {
// Error here ------------------------
if([self.delegate respondsToSelector:#selector(scanViewController:didTapToFocusOnPoint:)]) {
[self.delegate scanViewController:self didTapToFocusOnPoint:aPoint];
}
// ------------------- End
[device setFocusPointOfInterest:CGPointMake(focus_x,focus_y)];
[device setFocusMode:AVCaptureFocusModeAutoFocus];
if ([device isExposureModeSupported:AVCaptureExposureModeAutoExpose]){
[device setExposureMode:AVCaptureExposureModeAutoExpose];
}
[device unlockForConfiguration];
}
}
}
#pragma mark -
#pragma mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection
{
for(AVMetadataObject *current in metadataObjects) {
if([current isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
//Error in this line here ---------------
if([self.delegate respondsToSelector:#selector(scanViewController:didSuccessfullyScan:)]) {
NSString *scannedValue = [((AVMetadataMachineReadableCodeObject *) current) stringValue];
[self.delegate scanViewController:self didSuccessfullyScan:scannedValue];
// ----------------------------End
}
}
}
}
#end
If anyone has a better tutorial than this one please feel free to provide one because they are hard to come by for some reason.
Referenced Tutorial:
http://www.ama-dev.com/iphone-qr-code-library-ios-7/
I'm missing where you tell the compiler that the delegate is an < AMScanViewControllerDelegate>.
Here:
`#protocol AMScanViewControllerDelegate <NSObject>`
Should have been:
`#protocol ScannerViewControllerDelegate <NSObject>`
Since everything else in my code hinted to that

Finally got my leader board up, but score is not showing up

I finally got my leaderboard to show up. Now I just need to implement that my score will pop up.
My score is saved as an NSString in NSUserDefaults under the name score.
Here is some code:
Game_CenterViewController.h
#import <UIKit/UIKit.h>
#import <GameKit/GameKit.h>
#import "GameCenterManager.h"
#class GameCenterManager;
#interface Game_CenterViewController : UIViewController <UIActionSheetDelegate, GKLeaderboardViewControllerDelegate, GKAchievementViewControllerDelegate, GameCenterManagerDelegate> {
GameCenterManager *gameCenterManager;
int64_t currentScore;
NSString *currentLeaderBoard;
}
#property (nonatomic, retain) GameCenterManager *gameCenterManager;
#property (nonatomic, assign) int64_t currentScore;
#property (nonatomic, retain) NSString* currentLeaderBoard;
#end
Game_CenterViewController.m
#import "Game_CenterViewController.h"
#import "AppSpecificValues.h"
#import "GameCenterManager.h"
#implementation Game_CenterViewController
#synthesize gameCenterManager;
#synthesize currentScore;
#synthesize currentLeaderBoard;
- (void)dealloc {
[gameCenterManager release];
[currentLeaderBoard release];
[currentScoreLabel release];
[super dealloc];
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.currentLeaderBoard = thescore101;
self.currentScore = score
if ([GameCenterManager isGameCenterAvailable]) {
self.gameCenterManager = [[[GameCenterManager alloc] init] autorelease];
[self.gameCenterManager setDelegate:self];
[self.gameCenterManager authenticateLocalUser];
} else {
// The current device does not support Game Center.
}
}
- (void)leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController {
[self dismissModalViewControllerAnimated: YES];
[viewController release];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.gameCenterManager = nil;
self.currentLeaderBoard = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
The current score is where I'm trying to put the NSString in.
EDITED: I think it would be better to use int for the currentScore.
Is this what you are looking for?
- (void)submitMyScore:(int64_t)score
{
GKScore *myScoreValue = [[[GKScore alloc] initWithCategory:#"yourCat"] autorelease];
myScoreValue.value = (int)score;
[myScoreValue reportScoreWithCompletionHandler:^(NSError *error){
if(error != nil){
NSLog(#"Score Submission Failed");
} else {
NSLog(#"Score Submitted: %d",(int)score);
}
}];
}
So you should add a IBAction
- (IBAction)buttonPressed
{
[self submitMyScore:currentScore];
}
With this and connecting the SEND MY SCORE button to this IBAction, you will have your score submitted.
I hope this to be useful for you.

Background audio wont play

I'm struggling for couple of days now to sort this thing out and simply just can't find the way. I want to play audio in background when app exits or when i click on link to go to Safari, but i just wont go to background mode. Please help.
FirstViewController.h file :
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVAudioPlayer.h>
#import <AVFoundation/AVFoundation.h>
#interface RygestopFirstViewController : UIViewController <AVAudioPlayerDelegate> {
IBOutlet UIButton *playButton;
IBOutlet UISlider *volumeSlider;
IBOutlet UISlider *progressBar;
IBOutlet UILabel *currentTime;
IBOutlet UILabel *duration;
AVAudioPlayer *player;
UIImage *playBtnBG;
UIImage *pauseBtnBG;
NSTimer *updateTimer;
BOOL inBackground;
}
- (IBAction)playButtonPressed:(UIButton*)sender;
- (IBAction)volumeSliderMoved:(UISlider*)sender;
- (IBAction)progressSliderMoved:(UISlider*)sender;
#property (nonatomic, retain) UIButton *playButton;
#property (nonatomic, retain) UISlider *volumeSlider;
#property (nonatomic, retain) UISlider *progressBar;
#property (nonatomic, retain) UILabel *currentTime;
#property (nonatomic, retain) UILabel *duration;
#property (nonatomic, retain) NSTimer *updateTimer;
#property (nonatomic, assign) AVAudioPlayer *player;
#property (nonatomic, assign) BOOL inBackground;
#end
FirstViewController.m code:
// amount to skip on rewind or fast forward
#define SKIP_TIME 1.0
// amount to play between skips
#define SKIP_INTERVAL .2
#implementation RygestopFirstViewController
#synthesize playButton;
#synthesize volumeSlider;
#synthesize progressBar;
#synthesize currentTime;
#synthesize duration;
#synthesize updateTimer;
#synthesize player;
#synthesize inBackground;
-(void)updateCurrentTimeForPlayer:(AVAudioPlayer *)p
{
currentTime.text = [NSString stringWithFormat:#"%d:%02d", (int)p.currentTime / 60, (int)p.currentTime % 60, nil];
progressBar.value = p.currentTime;
}
- (void)updateCurrentTime
{
[self updateCurrentTimeForPlayer:self.player];
}
- (void)updateViewForPlayerState:(AVAudioPlayer *)p
{
[self updateCurrentTimeForPlayer:p];
if (updateTimer)
[updateTimer invalidate];
if (p.playing)
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:p repeats:YES];
}
else
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
updateTimer = nil;
}
}
- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)p
{
[self updateCurrentTimeForPlayer:p];
if (p.playing)
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
else
{
[playButton setImage:((p.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
}
-(void)updateViewForPlayerInfo:(AVAudioPlayer*)p
{
duration.text = [NSString stringWithFormat:#"%d:%02d", (int)p.duration / 60, (int)p.duration % 60, nil];
progressBar.maximumValue = p.duration;
volumeSlider.value = p.volume;
}
-(void)pausePlaybackForPlayer:(AVAudioPlayer*)p
{
[p pause];
[self updateViewForPlayerState:p];
}
-(void)startPlaybackForPlayer:(AVAudioPlayer*)p
{
if ([p play])
{
[self updateViewForPlayerState:p];
}
else
NSLog(#"Could not play %#\n", p.url);
}
- (IBAction)playButtonPressed:(UIButton *)sender
{
if (player.playing == YES)
[self pausePlaybackForPlayer: player];
else
[self startPlaybackForPlayer: player];
}
- (IBAction)volumeSliderMoved:(UISlider *)sender
{
player.volume = [sender value];
}
- (IBAction)progressSliderMoved:(UISlider *)sender
{
player.currentTime = sender.value;
[self updateCurrentTimeForPlayer:player];
}
- (void)dealloc
{
[super dealloc];
[playButton release];
[volumeSlider release];
[progressBar release];
[currentTime release];
[duration release];
[updateTimer release];
[player release];
[playBtnBG release];
[pauseBtnBG release];
}
#pragma mark AVAudioPlayer delegate methods
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)p successfully:(BOOL)flag
{
if (flag == NO)
NSLog(#"Playback finished unsuccessfully");
[p setCurrentTime:0.];
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
[self updateViewForPlayerState:p];
}
}
- (void)playerDecodeErrorDidOccur:(AVAudioPlayer *)p error:(NSError *)error
{
NSLog(#"ERROR IN DECODE: %#\n", error);
}
// we will only get these notifications if playback was interrupted
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)p
{
NSLog(#"Interruption begin. Updating UI for new state");
// the object has already been paused, we just need to update UI
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
[self updateViewForPlayerState:p];
}
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)p
{
NSLog(#"Interruption ended. Resuming playback");
[self startPlaybackForPlayer:p];
}
#pragma mark background notifications
- (void)registerForBackgroundNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(setInBackgroundFlag)
name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(clearInBackgroundFlag)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)setInBackgroundFlag
{
inBackground = true;
}
- (void)clearInBackgroundFlag
{
inBackground = false;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = NSLocalizedString(#"Play", #"First");
self.tabBarItem.image = [UIImage imageNamed:#"Home"];
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
//Make sure we can recieve remote control events
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
playBtnBG = [[UIImage imageNamed:#"Player.png"] retain];
pauseBtnBG = [[UIImage imageNamed:#"Pause.png"] retain];
[playButton setImage:playBtnBG forState:UIControlStateNormal];
[self registerForBackgroundNotifications];
updateTimer = nil;
duration.adjustsFontSizeToFitWidth = YES;
currentTime.adjustsFontSizeToFitWidth = YES;
progressBar.minimumValue = 0.0;
// Load the the sample file, use mono or stero sample
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:#"Sound1" ofType:#"m4a"]];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
if (self.player)
{
[self updateViewForPlayerInfo:player];
[self updateViewForPlayerState:player];
player.numberOfLoops = 0;
player.delegate = self;
}
[fileURL release];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
And, by the way, i want to run thin in tab bar application, so the background mode must be present always.
Here's what you're looking for: https://devforums.apple.com/message/264397 and set your background mode to 'App plays audio' in your app's .plist file.

Resources