Application doesn't work properly when installed with ipa file - objective-c

I am playing youtube video on iPad via webView using this code.
NSString *htmlString = [NSString stringWithFormat:#"<html>\
<body>\
<div id=\"player\"> </div>\
<script>\
var tag = document.createElement('script');\
tag.src = \"http://www.youtube.com/player_api\";\
var firstScriptTag = document.getElementsByTagName('script')[0];\
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);\
var done = false;\
var player;\
function onYouTubePlayerAPIReady() {\
player = new YT.Player('player', {\
height: '%i',\
width: '%i',\
videoId: '%#',\
events: {\
'onReady': onPlayerReady,\
'onStateChange': onPlayerStateChange\
}\
});\
}\
function onPlayerReady(evt) {\
evt.target.playVideo();\
}\
function onPlayerStateChange(evt) {\
if(evt.data==0)\
{\
window.location=\"http:\\end\";\
}\
}\
function resizePlayer(width,height)\
{\
player.setSize(width, height);\
}\
</script>\
</body>\
</html>",
height,width, videoID];
The problem is that when I'm installing my app via xCode it works fine, but when I'm using ipa file it don't.

The problem you are having could depend on the specific device and iOS versions (there are subtle differences in UIWebView implementations), more than on using an ipa file.
So, you might try and reproduce the environment where the UIWebView fails to interpret correctly your HTML snippet. Also, don't forget to define webView:didFailLoadWithError: and give a look at a way to intercept javascript errors inside of UIWebViews and display them on the console.
Hope this helps.

I have found what caused my problem.
To allow my application to catch a moment when video reaches the end, I wrote Javascript code to redirect page to another URL. Then I implemented method from WebViewProtocol -(BOOL)webView:(UIWebView *) shouldStartLoadWithREquest:(NSURLRequest *) navigationType:(UIWebViewNavigationType)navigationType. The main idea was to close view when it tries to go to some special link, but I've made one mistake that for some unknown reason didn't raised when I launch my app from XCode.
Code with error:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = [[request URL] absoluteString];
if ([url isEqualToString:#"http://youtube.com/end"])
{
[self onCloseVideo];
[self unsubscribe];
return = NO;
}
//here on else I had to return YES but I didn't
}
Code without error:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = [[request URL] absoluteString];
BOOL shouldStartRequest = YES;
if ([url isEqualToString:#"http://youtube.com/end"])
{
[self onCloseVideo];
shouldStartRequest = NO;
}
return shouldStartRequest;
}

Related

JavascriptCore hangs when running a JS with callback

I am trying to run a JS function using JavascriptCore from Objective-C. Here's the JS code:
var FeedParser = require('feedparser');
var run = function(data, options){
var feedparser = new FeedParser(options);
feedparser.on('readable', function() {
if(callback){
callback(this.read());
}
});
feedparser.end(data);
};
module.exports = {
run: run
};
And here's the Objective-C code that invokes the JS:
NSString *path = [[NSBundle mainBundle] pathForResource:#"rss" ofType:#"js"];
NSStringEncoding encoding;
NSError *error = nil;
NSString *js = [NSString stringWithContentsOfFile:path
usedEncoding:&encoding
error:&error];
JSContext *context = [[JSContext alloc] init];
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(#"%#", value);
}];
context[#"callback"] = ^(JSValue *val){
#try{
if([val isString]){
NSLog(#"%#", [val toString]);
} else if([val toDictionary][#"0"]){
NSLog(#"%#", [val toArray]);
} else {
NSLog(#"%#", [val toDictionary]);
}
}
#catch(NSException *e){
}
};
[context evaluateScript:js];
JSValue *parse = context[#"rss"][#"run"];
[parse callWithArguments:#[data, o]];
Note:
Just to be clear, this approach works fine for all other JS functions that immediately return results. I am using browserify to package them all into a single JS. I'm just noticing problems with functions that return asynchronously via callback. (Notice I'm adding callback function to the context.
When I run this on XCode it hangs (not just the app but also the XCode editor itself, and it never recovers until I kill the process from Activity Monitor).
However for some reason it returns the result as soon as I unplug the iPhone from my laptop. (At this point XCode is still in a limbo state until I kill it)
Once detached from XCode, the app runs fine. It just hangs while it's running from XCode invoked run.
Probably you are trying to convert some JavaScript value to NSDictionary via toDictionary call. This method attempts to convert value recursively, so it leads to a hung often. I suspect that any JS value with circular references causes infinite recursion in this case. Try to work with JSValue directly instead.

IOS App Action extension is not closing

I am facing app extension close issues , please tell me if anyone know what wrong am doing.I am using action extension after preform some action inside extension i need to return response back.
Sample Code
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
}
// With Error Case
- (void) completeActionWithError: (NSError *) error {
[self.extensionContext cancelRequestWithError: error];
}
With Success Case working fine but some time is not closing,
With Error Case not working above code.
Please let me know what went wrong.Thanks
When you create an action extension, this is the default method which will close the Action Extension View Controller:
- (IBAction)done {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
[self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];
}
Since this method is already provided, you should just try calling it from your success method.
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
// Call to "done" method
[self done];
}

AVPlayer plays on simulator but doesn't on a real device

I'm implementing a basic audio player in order to play remote audio files. Files are in format mp3.
The code I wrote is working fine on the simulator but doesn't work on a real device. However the same url I use within my app works fine if I load it by using safari (on the same real device) so I'm not really getting the missing point.
Below is my code:
self.musicPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:urlTrack]];
[self.musicPlayer play];
something extremely easy. The music player property is defined as
#property (nonatomic, retain) AVPlayer *musicPlayer;
I also tried using an AVPlayerItem but the result is the same. Here is the code I have used
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:urlTrack]];
self.musicPlayer = [AVPlayer playerWithPlayerItem:playerItem];
[self.musicPlayer play];
Finally I tried to use the code below
self.musicPlayer = [AVPlayer playerWithURL:[NSURL URLWithString:urlTrack]];
NSLog(#"Player created:%d",self.musicPlayer.status);
[self.musicPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"Player created:%d",self.musicPlayer.status);
if (object == self.musicPlayer && [keyPath isEqualToString:#"status"]) {
if (self.musicPlayer.status == AVPlayerStatusReadyToPlay) {
[self.musicPlayer play];
} else if (self.musicPlayer.status == AVPlayerStatusFailed) {
// something went wrong
}
}
}
When the method observeValueForKeyPath is invoked the player status is 1 and the play is exectuted but still not sound.
I tried several files like:
http://www.nimh.nih.gov/audio/neurogenesis.mp3
http://www.robtowns.com/music/blind_willie.mp3
Any idea?
Tnx
Check the spelling of your filename. The device is case-sensitive, the simulator is not...
Also, check if your ringer is off, you won't hear any sound when it's off. To prevent that, use
NSError *_error = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &_error];
right before where you init the player
i have tried all the things but did not work, the only think that work for me AVAudioSession to playBack
here is the code i used
private var audioPlayer: AVAudioPlayer!
guard let path = Bundle.main.path(forResource: name, ofType: "mp3") else {
print("can not find path")
return
}
let url = URL(fileURLWithPath: path)
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
audioPlayer = try AVAudioPlayer(contentsOf: url)
} catch {
print("some thing went wrong: \(error)")
}
audioPlayer!.prepareToPlay()
audioPlayer!.play()
please do not ignore the line in the above code
try AVAudioSession.sharedInstance().setCategory(.playback)

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}

Detecting Download in UIWebView

I have a programatically crated UIWebView, and it is used to browse a iPhone-style site stored on my server. In this website, there are a few links to files users can download into my application. Right now, I'm trying to detect this with:
- (BOOL) webView:(UIWebView *) webView shouldStartLoadWithRequest:(NSURLRequest *) request navigationType:(UIWebViewNavigationType) navigationType
{
url = [request URL];
NSString *mimeType = [request valueForHTTPHeaderField:#"Content-Type"];
NSLog(#"Content-type: %#", mimeType);
if(mimeType == #"application/zip" || mimeType == #"application/x-zip" || mimeType == #"application/octet-stream")
{
NSLog(#"Downloading file!");
[NSThread detachNewThreadSelector:#selector(download:) toTarget:self withObject:#"/tmp/file.ipa"];
return NO;
}
return YES;
}
However, when this method is called, the content-type header is almost always (null), so I never am able to download a file.
How would you do this correctly?
You're trying to detect a Content-Type from an NSURLRequest which has not yet been made. You won't know the Content-Type until after the request is made using NSURLConnection. In this case, I'd probably just look at the file extension of the URL path.
----------Swift 4+-------
Example for audio/mp3 detect -
Step 1: Use delegate
class ViewController : WKUIDelegate,WKNavigationDelegate {
Step 2: Setting WebKit
func setWebView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
let myURL = URL(string: "https://www.bossmobi.guru/files/download/type/320/id/197255")//your audio url
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
Step 3: Get audio MIME type from webkit delegate.
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: #escaping (WKNavigationResponsePolicy) -> Void) {
print( #function + "url is \(String(describing: webView.url))" + "Mimetype" + "\(navigationResponse.response.mimeType ?? "NotAvailable")")
if let _ = navigationResponse.response.mimeType?.range(of: "audio/mpeg") {
print("MP3 is audio url \(String(describing: webView.url))")
webView.stopLoading()
}
decisionHandler(.allow)
}
---------ObjC----------
WKWebView setup
NSString *urlString = #"https://www.bossmobi.guru/files/download/type/320/id/197255";
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
WKWebView *_demoWKWebView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:theConfiguration];
_demoWKWebView.navigationDelegate = self;
_demoWKWebView.UIDelegate = self;
NSURL *nsurl=[NSURL URLWithString:urlString];
NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[_demoWKWebView loadRequest:nsrequest];
[self.view addSubview:_demoWKWebView];
WKWebView delegate
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
//NSLog(#"decidePolicyForNavigation---Response %#",webView.URL);
if ([navigationResponse.response.MIMEType isEqualToString:#"audio/mpeg"]) {
NSLog(#"MP3 audio url is %#",webView.URL);
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
So here's the problem: UIWebView doesn't download anything it can't display, and it doesn't know how to display a ZIP file. It will always fail before the Content-Type is filled in.
So, what to do? I don't know if your server-side app runs on more than the iPhone, but you could register a custom URL scheme with links like myapplication://example.com/stuff/yourhexurlgoeshere. You can create a custom URL handler for the myapplication scheme. A couple of seconds of Googling produced this site, which explains how to do it.
This has an additional benefit because if you, say, emailed such a link to another user, they could tap on it in Mail and have it open in your application.