why MPMoviePlayerViewController having different behavior with the same URL? - objective-c

I implemented MPMovieViewController in my app but it has a problem i.e.when i play video in player then first time it is failed to play but next time it will play successfully with the same URL. I am not able to understand this different behavior with the same URL. I copied code here which i used in my app.
NSURL *fileURL = [NSURL URLWithString:#"http://nordenmovil.com/urrea/InstalaciondelavaboURREAbaja.mp4"];
MPMoviePlayerViewController * controller = [[MPMoviePlayerViewController alloc]initWithContentURL:fileURL];
controller.moviePlayer.movieSourceType= MPMovieSourceTypeFile;
[controller.moviePlayer setControlStyle:MPMovieControlStyleDefault];
[controller.moviePlayer prepareToPlay];
[controller.moviePlayer play];
[self presentMoviePlayerViewControllerAnimated:controller];

I suggest to handle the error for see what happens with your URL video (this is a tip that I founded long time ago in this answer)
Add this for capture the end of play:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleMPMoviePlayerPlaybackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
and this for see what happens:
- (void)handleMPMoviePlayerPlaybackDidFinish:(NSNotification *)notification
{
NSDictionary *notificationUserInfo = [notification userInfo];
NSNumber *resultValue = [notificationUserInfo objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
MPMovieFinishReason reason = [resultValue intValue];
if (reason == MPMovieFinishReasonPlaybackError)
{
NSError *mediaPlayerError = [notificationUserInfo objectForKey:#"error"];
if (mediaPlayerError)
{
NSLog(#"playback failed with error description: %#", [mediaPlayerError localizedDescription]);
}
else
{
NSLog(#"playback failed without any given reason");
}
}
}
Don't forget to remove the notification handler:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
With this workaround you should know the error with your stream, I hope this helps!

Related

Where to from AppDelegate? how to go back to previous view and access instance

I have an instance in a view called "PayView" and at the meantime I am doing openURL to open a separate app and to pass some data to it. This second app process the information I send and gives the response back.
At Appdelegate.m I have this handleOpenUrl which receives the response sent by the second app. Once I receive the response back I would like to go back to my "PayView" and use the response received from the second app along with already existing values I have in the instance.
My problem is as soon as the response from the second app reaches the appdelegate and reached the section "return yes", my main app goes back to this "PayView" and does nothing.
So how do I use the response object I received from appdelegate at my PayView along with already existing instance values?
I thought about using a global variable to store the "payView" instance/object and initiate a new instance from appdelegate for "Payview" and use the global along with the response json from appdelegate. However, I found many forums advising against using global.
So, ignoring the global and crating a new instance for "payview" causes loss of all previously stored data.
I am not an experienced iOS programmer and just work momentarily on someone else code.So, hope I explained my issue/question.
It would be great if I could get some valuable inputs :)
My appdelegate.m look like this,
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
if (!url) {
return NO;
}
// Unencode the URL's query string
NSString *queryString = [[url query] stringByRemovingPercentEncoding];
// Extract the JSON string from the query string
queryString = [queryString stringByReplacingOccurrencesOfString:#"response=" withString:#""];
// Convert the JSON string to an NSData object for serialization
NSData *queryData = [queryString dataUsingEncoding:NSUTF8StringEncoding];
// Serialize the JSON data into a dictionary
NSDictionary* jsonObject = [NSJSONSerialization JSONObjectWithData:queryData options:0 error:nil];
NSString *response = [jsonObject objectForKey:#"Result"]; //Get the response
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"Result" message:response delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alert1 show];
//PayView *pdv = [[PayViewController alloc] init];
//[pdv UseTransactionResult:jsonObject] ;
return YES;
}
and this is how I am calling open url from PayView
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:[NSString stringWithFormat:#"mysecondapp://v2/data/?request=%#",requestEncodedString]] options:#{} completionHandler:^(BOOL success) {
if (success)
{
NSLog(#"Opened url");
}
}];
Edit 1:
Hi #ekscrypto, thanks for your valuable input. It is really helpful. I just have one problem with this. It is working just fine when I do the following in my PayView
Receiver:
[[NSNotificationCenter defaultCenter] addObserverForName:#"ActionIsComplete" object:nil queue:nil usingBlock:^(NSNotification *note){
       
        //Completed Action
        NSString *response = [note.userInfo objectForKey:#"Result"]; //Get the response
        NSLog(response);
        [self dismissViewControllerAnimated:YES completion:nil];
   
    }];
 
However, when I try to do the same in the following  method I get error "unrecognized selector sent to instance"
Receiver:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ReceiveActionIsComplete:) name:#"ActionIsComplete" object:nil];
-(void)ReceiveActionIsComplete:(NSNotification *) notification
{
NSDictionary *jsonObject = (NSDictionary *)notification.userinfo;
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"ActionIsComplete" object:nil];
Status = [jsonObject objectForKey:#"Status"];
Result = [jsonObject objectForKey:#"Result"];
NSLog([NSString stringWithFormat:#"%#%#",#"Status is: ", Status]);
NSLog([NSString stringWithFormat:#"%#%#",#"Result is: ", Result]);
}
in both cases my sender at Appdelegate looks like this.
Sender:
[[NSNotificationCenter defaultCenter] postNotificationName:#"ActionIsComplete" object:nil userInfo:jsonObject];
FYI: I tried sending the object in object instead of userInfo as well.
So what I am doing wrong? could you please help me.
Conclusion:
Sender: (AppDelegate.m)
[[NSNotificationCenter defaultCenter] postNotificationName:#"ActionIsComplete" object:nil userInfo:jsonObject];
Receiver: (PayView.m)
under - (void)viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(ReceiveActionIsComplete:) name:#"ActionIsComplete" object:nil];
and the function to receive the results
-(void)ReceiveActionIsComplete:(NSNotification *) notification
{
NSDictionary *jsonObject = (NSDictionary *)notification.userInfo;
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"ActionIsComplete" object:nil];
Status = [jsonObject objectForKey:#"Status"];
Result = [jsonObject objectForKey:#"Result"];
NSLog([NSString stringWithFormat:#"%#%#",#"Status is: ", Status]);
NSLog([NSString stringWithFormat:#"%#%#",#"Result is: ", Result]);
}
You could register a NotificationCenter observer in your PayView, and post a notification from your AppDelegate when the response is received. Via the notification object you can forward any information you need.
Let's assume that you define a struct with the information you want to pass along:
struct PaymentConfirmation {
let amount: Float
let confirmationNumber: String
}
In your PayView:
class PayView: UIView {
...
static let paymentProcessedNotification = NSNotification.Name("PayView.paymentProcessed")
let paymentProcessedObserver: NSObjectProtocol?
override func viewDidLoad() {
super.viewDidLoad()
paymentProcessedObserver = NotificationCenter.default.addObserver(
forName: PayView.paymentProcessedNotification,
object: nil,
queue: .main) { [unowned self] (notification) in
guard let confirmation = notification.object as PaymentConfirmation else { return }
self.paymentProcessed(confirmation)
}
}
deinit {
NotificationCenter.default.removeObserver(paymentProcessedObserver)
}
func paymentProcessed(_ confirmation: PaymentConfirmation) {
// do stuff
}
Then in your AppDelegate:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
// do stuff required with the callback URL
let confirmation = PaymentConfirmation(
amount: 3.0,
confirmationNumber: "Pay830da08.8380zSomething")
NotificationCenter.default.post(
name: PayView.paymentProcessedNotification,
object: confirmation)
}
For more information, check out https://medium.com/ios-os-x-development/broadcasting-with-nsnotification-center-8bc0ccd2f5c3

Can not register token with GCM

I copy some google example for GCM(https://github.com/googlesamples/google-services/blob/master/ios/gcm/GcmExample/)
// [START_EXCLUDE]
_registrationKey = #"onRegistrationCompleted";
_messageKey = #"onMessageReceived";
// Configure the Google context: parses the GoogleService-Info.plist, and initializes
// the services that have entries in the file
NSError* configureError;
[[GGLContext sharedInstance] configureWithError:&configureError];
NSAssert(!configureError, #"Error configuring Google services: %#", configureError);
_gcmSenderID = [[[GGLContext sharedInstance] configuration] gcmSenderID];
// Register for remote notifications
UIUserNotificationType allNotificationTypes =
(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
UIUserNotificationSettings *settings =
[UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
// [END register_for_remote_notifications]
// [START start_gcm_service]
GCMConfig *gcmConfig = [GCMConfig defaultConfig];
gcmConfig.receiverDelegate = self;
[[GCMService sharedInstance] startWithConfig:gcmConfig];
// [END start_gcm_service]
__weak typeof(self) weakSelf = self;
// Handler for registration token request
_registrationHandler = ^(NSString *registrationToken, NSError *error){
if (registrationToken != nil) {
weakSelf.registrationToken = registrationToken;
NSLog(#"Registration Token: %#", registrationToken);
[weakSelf subscribeToTopic];
NSDictionary *userInfo = #{#"registrationToken":registrationToken};
[[NSNotificationCenter defaultCenter] postNotificationName:weakSelf.registrationKey
object:nil
userInfo:userInfo];
} else {
NSLog(#"Registration to GCM failed with error: %#", error.localizedDescription);
NSDictionary *userInfo = #{#"error":error.localizedDescription};
[[NSNotificationCenter defaultCenter] postNotificationName:weakSelf.registrationKey
object:nil
userInfo:userInfo];
}
};
it's launchOptions code for GCM.
but when i run it, catch some error, like this:
I think i am already fixed it. modify Other Link Flag value to "$(inherited)"

in-app purchase methods(mkstorekit? )

I have an app at the appstore , and i would like to add it in-app purchase, a basic one ,in order to purchase more levels .
I know that the apple sdk is very hard to implement,
I know the mkstorekit is an easy one, but i cant find a guide from scratch to use it .
Whats the best way to do so, any other method ?any good tutorial ?
Thanks a lot.
First you need to initialize MKStoreKit. A sample initializiation code that you can add to your application:didFinishLaunchingWithOptions: is below In Objective-C
[[MKStoreKit sharedKit] startProductRequest];
[[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitProductsAvailableNotification
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification *note) {
NSLog(#"Products available: %#", [[MKStoreKit sharedKit] availableProducts]);
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitProductPurchasedNotification
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification *note) {
NSLog(#"Purchased/Subscribed to product with id: %#", [note object]);
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitRestoredPurchasesNotification
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification *note) {
NSLog(#"Restored Purchases");
}];
[[NSNotificationCenter defaultCenter] addObserverForName:kMKStoreKitRestoringPurchasesFailedNotification
object:nil
queue:[[NSOperationQueue alloc] init]
usingBlock:^(NSNotification *note) {
NSLog(#"Failed restoring purchases with error: %#", [note object]);
}];
You can check if a product was previously purchased using -isProductPurchased as shown below.
if([MKStoreManager isProductPurchased:productIdentifier]) {
//unlock it
}
You can check for a product's expiry date using -expiryDateForProduct as shown below.
if([MKStoreManager expiryDateForProduct:productIdentifier]) {
//unlock it
}
To purchase a feature or to subscribe to a auto-renewing subscription, just call
[[MKStoreKit sharedKit] initiatePaymentRequestForProductWithIdentifier:productIdentifier];
You can also find tutorials of mkstorekit here and here.

Playing two videos in sequence withou any gap or pause between them in iOS 5

In my app, I should play firstvideo at once, and then play second video, which will on repeat. So, at first I used MPMoviePlayerController, but there already was a black screen between two videos. So, I read somewhere, that in my case I should use AVFoundation. That's whe I use AVQueuePLayer with two AVPLayerItem, which I create with my NSURL. But issue is, that after first video finished, there is a long pause. Nearly for 0.5 sec, before second vide o starts. How remove it? What way to use? So, I really appreciate any help! I need it very much, as soon as possible. Thanks!
Here all my methods which I use:
- (void) configureAVPlayer {
self.queuePlayer.actionAtItemEnd = AVPlayerActionAtItemEndPause;
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.queuePlayer];
self.isFirstVideoFinished = NO;
[self playVideos];
}
- (void) playVideos {
[self setPlayerItemsForQueue];
[self.videoHolder setPlayer:self.queuePlayer];
}
- (void) setPlayerItemsForQueue {
NSURL *fileURL = [[NSBundle mainBundle]
URLForResource:[self getStringForMode:self.currentMode] withExtension:#"mp4"];
NSMutableString *secondFile = [self getStringForMode:self.currentMode];
[secondFile appendString:#"Second"];
NSURL *secondFileUrl = [[NSBundle mainBundle] URLForResource:secondFile withExtension:#"mp4"];
AVAsset *firstAsset = [AVAsset assetWithURL:fileURL];
AVPlayerItem *firstVideoItem = [AVPlayerItem playerItemWithAsset:firstAsset];//[AVPlayerItem playerItemWithURL:fileURL];
AVAsset *secondAsset = [AVAsset assetWithURL:secondFileUrl];
AVPlayerItem *secondVideoItem = [AVPlayerItem playerItemWithAsset:secondAsset];//[AVPlayerItem playerItemWithURL:secondFileUrl];
self.queuePlayer = [AVQueuePlayer queuePlayerWithItems: [NSArray arrayWithObjects:firstVideoItem, secondVideoItem,nil]];
for (AVPlayerItem *playerItem in self.queuePlayer.items) {
[playerItem addObserver: self forKeyPath: #"status" options:0 context:NULL];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
dispatch_async(dispatch_get_main_queue(), ^{
if ([(AVPlayerItem *)object status] == AVPlayerItemStatusReadyToPlay) {
[self.queuePlayer play];
}
});
}
- (void) playerItemDidReachEnd:(NSNotification*) notification {
NSMutableString *secondFile = [self getStringForMode:self.currentMode];
[secondFile appendString:#"Second"];
NSURL *secondFileUrl = [[NSBundle mainBundle] URLForResource:secondFile withExtension:#"mp4"];
AVPlayerItem *playerItem = [[ AVPlayerItem alloc ] initWithURL:secondFileUrl];
if ([self.queuePlayer canInsertItem:playerItem afterItem:[notification object]]) {
[self.queuePlayer insertItem: playerItem afterItem: nil ];
}
}
Yes, multiple AVPlayer objects are your best option.
I solved this problem with a main timer function which monitored the current playback time of firstVideoItem, and when it was 0.01 seconds from the end, I would flip the players on the alpha channel, so secondVideoItem was visible, then play secondVideoItem.
I also used [secondVideoItem seekToTime:CMTimeMakeWithSeconds(1,1)] before issuing the play command to make sure the second video was initialised and ready to play. This helped a lot.

NSFileHandle readInBackgroundAndNotify does not work

Hi I'm using NSFileHandle's readInBackgroundAndNotify method to get notifications when a log file has been updated.
I have the following code:
- (void)startReading
{
NSString *logPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Library/Logs/MyTestApp.log"];
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:logPath];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:#selector(getData:)
name:NSFileHandleReadCompletionNotification
object:fh];
[fh readInBackgroundAndNotify];
}
- (void) getData: (NSNotification *)aNotification
{
NSLog(#"notification received");
}
However the selector is never called and the notification is not received.
Add an NSLog to startReading to make sure that's getting called.
Log fh. My guess is that it's nil (most probably because you haven't created MyTestApp.log yet).