I have a basic iOS app developed in Swift. I'm using a WkWebView to load my website and require to get an inputs value before the page is changed so that I can save it into NSUserDefaults.
I can use the following to simply populate the inputs value:
webView.evaluateJavaScript("document.getElementById('myInput').value = 'Test';") { (result, error) in
if error != nil {
print(result)
}
Although how can I get the value of an input from a WKWebView?
Try adding a java script to capture the changes in webview and send the data asynchronously to wkconfiguration delegate to collect the data.
Adding JS to wkwebkit means lot of control, even you can add jquery as well.
This is just a hint where you can create your ideas.
-
(void) addJSScriptToWebConfig
{
NSString *JS = ...;
WKWebViewConfiguration *webViewConfig = [[SBWKWebViewConfiguration sharedInstance] configuration];
WKUserScript *script3 = [[WKUserScript alloc] initWithSource:JS injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[webViewConfig.userContentController addUserScript:script3];
[webViewConfig.userContentController addScriptMessageHandler:self
name:#"callbackFunc"];
Related
I am working on an ancient iOS app for a program that runs off VB6. It is passing strings over in a non unicode format including Hebrew characters which once they are parsed are displayed as "àáðø ãøåøé"
I am assuming it is being encoded in Windows Hebrew.
I can't seem to find anything in the apple documentation that explains how to handle this case. And most searches bring up solutions in Swift, no Obj-C. I tried this:
NSString *hebrewPickup = [pickupText stringByApplyingTransform:NSStringTransformLatinToHebrew reverse:false];
But that just gave me this:
"ðø ַ̃øַ̊øֵ"
I am stumped.
EDIT: Based on JosefZ's comment I have tried to encode back using CP1252, but the issue is that CP1255 is not in the list of NSStringEncodings. But seems like it would solve my issue.
NSData *pickupdata = [pickupText dataUsingEncoding:NSWindowsCP1252StringEncoding];
NSString *convPick = [[NSString alloc] initWithData:pickupdata encoding:NSWindowsCP1254StringEncoding];
NSString *hebrewPickup = [convPick stringByApplyingTransform:NSStringTransformLatinToHebrew reverse:false];
Ok, if any poor soul ends up here, this is how I ended up fixing it. I needed to add some Swift into my Obj-C code. (If only I could magically just rebuild the whole project in Swift instead.)
Here is the info on that: https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c
Making use of this Swift Package: https://github.com/Cosmo/ISO8859
I added the following code to a new swift file.
#objc class ConvertString: NSObject {
#objc func convertToHebrew(str:String) -> NSString {
let strData = str.data(using: .windowsCP1252);
let bytes: Data = strData!;
if let test = String(bytes, iso8859Encoding: ISO8859.part8) {
return test as NSString;
}
let test = "";
return test as NSString;
}
}
Then in the Obj-C project I was able to call it like so:
ConvertString *stringConverter = [ConvertString new];
NSString *pickupTextFixed = [stringConverter convertToHebrewWithStr:pickupText];
NSString *deliverTextFixed = [stringConverter convertToHebrewWithStr:deliverText];
I am trying to show a website through a WKWebView.
My problem is that I don't want to show the navigationBar of the WebSite.
It looks like I have to use webview.evaluateJavaScript but is there an other way of doing it. If NO, can you provide an example with webview.evaluateJavaScript
Thanks
Looking a bit online, I found that on the particular website you can check for navigator.userAgent.
Then I could use the following that helped me remove the content that I want
webView.evaluateJavaScript("navigator.userAgent") { [weak webView] (result, error) in
if let webView = webView, let userAgent = result as? String {
webView.customUserAgent = userAgent + "/_app_"
}
}
I have a web view which request one web page, now that web has some action events which may reply to IOS app as HTML or json. So how would the app will come to know the response type sent. So that the response is handled within the app.
Reading of static HTML content in webview using its delegate is what i have tried, when it is dynamic then how can one handle.
code logic:-
ON load of controller, request page with URL(something) in web view, user will interact
check response type if JSON then 4 else 3
load web page with different URL
Deserialize the JSON Data and store in native DB
Method 1
First, You need to set and handle the UIWebView delegate methods in your UIViewController
Then, in webView: shouldStartLoadWithRequest: navigationType: method, use the following
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSError *error;
NSString *responseString = [NSString stringWithContentsOfURL:request.URL
encoding:NSASCIIStringEncoding
error:&error];
//Parse the string here to confirm if it's JSON or HTML
//In case of JSON, stop the loading of UIWebview
if(json) {
retrun NO;
}
return YES;
}
Note: This will take a performance hit in case of HTML content as the response will be loaded 2 times. Once in stringWithContentsOfURL method and second time when the web view loads.
Method 2
To avoid the double loading you can let the web view load, irrespective of the content type. And then get the loaded content in webViewDidFinishLoad method.
For this you may need to perform some changes on the server end as well.
Suppose your HTML page with JSON is structured as :
<html>
<body>
<div id="json" style="display: none">{"jsonvalue":"{{result}}"}</div>
</body>
</html>
Then in webViewDidFinishLoad
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *res = [webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('json').innerHTML"];
//Use the JSON string as required
}
After the long brainstorming as i am new to IOS, I figure out the solution for swift as I could not find the solution in swift language. Here are the few steps for solutions-
First drag and drop the webView to create the connection of webview
#IBOutlet weak var webViewlLoad: UIWebView!
Now load your webView by calling
webViewlLoad.loadRequest(URL)
Now wait for some time and check the webview for getting the data by the following code
let time = dispatch_time(dispatch_time_t(DISPATCH_TIME_NOW), 5 * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) {
//put your code which should be executed with a delay here
let doc = self.webViewlLoad.stringByEvaluatingJavaScriptFromString("document.documentElement.outerHTML")!
print("document JSON = \(doc)")
let startIndexJSON = doc.rangeOfString("{")?.startIndex
let endIndexJSON = doc.rangeOfString("}}")?.startIndex
var finalJson = doc.substringWithRange( Range<String.Index>(start:startIndexJSON!, end: endIndexJSON!.advancedBy(2)))
print("\n\n finalJson JSON = \(finalJson)")
}
In above code i wait for 5 seconds , then the webView load the json. I read it by substring the result.
In WWDC session Introduction to Search APIs. They show a search result of Airbnb app with a call button. From what I saw I think the result was created with CSSearchableItemAttributeSet not from Web Markup api.
I tried setting ItemContentType of CSSearchableItemAttributeSet to kUTTypeItem, kUTTypeMessage, kUTTypeEmailMessage of course with phoneNumbers value. None of them seems to work. All detail I put are appear correctly, except for the call button.
CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(__bridge NSString *)kUTTypeItem];
attributeSet.title = #"Call me back";
attributeSet.contentDescription = #"Firstname Lastname\n14:36 - 30 January 2014";
attributeSet.phoneNumbers = #[#"+66827364538"];
attributeSet.accountHandles = #[#"+66827364538"];
If I were to use kUTTypeContent. The call button appears but all details are not. Just name of contact that I put in when create CSPerson object.
CSPerson *person = [[CSPerson alloc] initWithDisplayName:#"Theptai Intathep"
handles:#[#"+66827364538"]
handleIdentifier:CNContactPhoneNumbersKey];
attributeSet.authors = #[person];
Try this:
attributeSet.supportsPhoneCall = #(YES);
I'm implementing an AVAssetResourceLoaderDelegate, and I'm having a bit of trouble getting to to behave correctly. My goal is to intercept any requests made by the AVPlayer, make the request myself, write the data out to a file, then respond to the AVPlayer with the file data.
The issue I'm seeing: I can intercept the first request, which is only asking for two bytes, and respond to it. After that, I'm not getting any more requests hitting my AVAssetResourceLoaderDelegate.
When I intercept the very first AVAssetResourceLoadingRequest from the AVPlayer it looks like this:
<AVAssetResourceLoadingRequest: 0x17ff9e40,
URL request = <NSMutableURLRequest: 0x17f445a0> { URL: fakeHttp://blah.com/blah/blah.mp3 },
request ID = 1,
content information request = <AVAssetResourceLoadingContentInformationRequest: 0x17ff9f30,
content type = "(null)",
content length = 0,
byte range access supported = NO,
disk caching permitted = NO>,
data request = <AVAssetResourceLoadingDataRequest: 0x17e0d220,
requested offset = 0,
requested length = 2,
current offset = 0>>
As you can see, this is only a request for the first two bytes of data. I'm taking the fakeHttp protocol in the URL, replacing it with just http, and making the request myself.
Then, here's how I'm responding to the request once I have some data:
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest {
//Make the remote URL request here if needed, omitted
CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)([self.response MIMEType]), NULL);
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
loadingRequest.contentInformationRequest.contentType = CFBridgingRelease(contentType);
loadingRequest.contentInformationRequest.contentLength = [self.response expectedContentLength];
//Where responseData is the appropriate NSData to respond with
[loadingRequest.dataRequest respondWithData:responseData];
[loadingRequest finishLoading];
return YES;
}
I've stepped through this and verified that everything in the contentInformationRequest is filled in correctly, and that the data I'm sending is NSData with the appropriate length (in this case, two bytes).
No more requests get sent to my delegate, and the player does not play (presumably because it only has two bytes of data, and hasn't requested any more).
Does anyone have experience with this to point me toward an area where I may be doing something wrong? I'm running iOS 7.
Edit: Here's what my completed request looks like, after I call finishedLoading:
<AVAssetResourceLoadingRequest: 0x16785680,
URL request = <NSMutableURLRequest: 0x166f4e90> { URL: fakeHttp://blah.com/blah/blah.mp3 },
request ID = 1,
content information request = <AVAssetResourceLoadingContentInformationRequest: 0x1788ee20,
content type = "public.mp3",
content length = 7695463,
byte range access supported = YES,
disk caching permitted = NO>,
data request = <AVAssetResourceLoadingDataRequest: 0x1788ee60,
requested offset = 0,
requested length = 2,
current offset = 2>>
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
loadingRequest.contentInformationRequest.contentType = #"public.aac-audio";
loadingRequest.contentInformationRequest.contentLength = [self.fileData length];
loadingRequest.contentInformationRequest.byteRangeAccessSupported = YES;
NSData *requestedData = [self.fileData subdataWithRange:NSMakeRange((NSUInteger)loadingRequest.dataRequest.requestedOffset,
(NSUInteger)loadingRequest.dataRequest.requestedLength)];
[loadingRequest.dataRequest respondWithData:requestedData];
[loadingRequest finishLoading];
return YES;
}
This implementation works for me. It always asks for the first two bytes and then for the whole data. If you don't get another callback it means that there was something wrong with the first response you have made. I guess the problem is that you are using MIME content type instead of UTI.
Circling back to answer my own question in case anyone was curious.
The issue boiled down to threading. Though it's not explicitly documented anywhere, AVAssetResourceLoaderDelegate does some weird stuff with threads.
Essentially, my issue was that I was creating the AVPlayerItem and AVAssetResourceLoaderDelegate on the main thread, but responding to delegate calls on a background thread (since they were the result of network calls). Apparently, AVAssetResourceLoader just completely ignores responses coming in on a different thread than it was expecting.
I solved this by just doing everything, including AVPlayerItem creation, on the same thread.