I try to load webView with existing website.
I added buttons on a navigation bar which have url links:
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
var myHTMLString = ""
guard let myURL = URL(string: domainURLString) else {
print("Error: \(domainURLString) doesn't seem to be a valid URL")
return
}
do {
myHTMLString = try String(contentsOf: myURL, encoding: .utf8)
} catch let error {
print("Error: \(error)")
}
webView.frame = view.bounds
webView.scalesPageToFit = true
createBottomNavigationBar()
webView.loadHTMLString(myHTMLString as String, baseURL: nil)
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
print("===============================start===============================")
let myURLString:String = (request.url?.absoluteString)!
var myHTMLString = ""
if navigationType == UIWebViewNavigationType.linkClicked {
print("link clicked")
webView.stopLoading()
var myHTMLString = ""
let myURL = URL(string: myURLString)
do {
myHTMLString = try String(contentsOf: myURL!, encoding: .utf8)
} catch let error {
print("Error: \(error)")
return true
}
webView.loadHTMLString(myHTMLString as String, baseURL: nil)
print("first")
print(request.url)
}else if(navigationType == UIWebViewNavigationType.other){
print("This is for Navigation Button click")
//webView.loadHTMLString(myHTMLString as String, baseURL: nil)
}
else{
print("else")
}
return true
However, When I uncomment
webView.loadHTMLString(myHTMLString as String, baseURL: nil)
in this statement:
}else if(navigationType == UIWebViewNavigationType.other){
the app loops forever.
The statement is for when one of the navigation buttons click fires. It redirects to internal url like "http://yahoo.com/mail".
That would be the expected behaviour based on your code. The line
webView.loadHTMLString(myHTMLString as String, baseURL: nil)
in viewDidLoad will try to load the HTML. This in turn will call the delegate method
webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool
Inside the delegate method, when your code hits the else if case, it will again call
webView.loadHTMLString(myHTMLString as String, baseURL: nil)
which in turn will call the delegate method again. It's a cyclical loop. The code is working as it should. You need to change the logic.
Related
I've been looking for the answer everywhere and could't find any.. Is there any way I can access the value of searchBar.text in another file? I have the delegate set in my SearchVC but I also have a custom tableView cell in another file.
I need the value of the SearchBar of my SearchVC to use in FirstDefinitionVC for decoding the word from the searchBar and use it for finding the audio URL.
All works fine while I call the function inside the searchBarSearchButtonClicked method but I can find no way to pass that String into FirstDefintionVC.
The relevant searchVC code :
var word = ""
`
extension SearchVC: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// { (data: [WordData], [Definitions])
word = searchBar.text!
wordManager.performRequest(word: word) { data in
self.wordData = data
self.searchButtonPressed = true
// print(data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
fetchAudio(word: word) { data in //this one works fine
DispatchQueue.main.async {
self.wordData = data
}
}
}
func fetchAudio(word: String, comp: #escaping ([WordData])-> Void) {
let wordURL = "https://api.dictionaryapi.dev/api/v2/entries/en/"
let urlString = "\(wordURL)\(word)"
if let url = URL(string: urlString) {
let dataTask = URLSession.shared.dataTask(with: url, completionHandler: {
(data,response,error) in
guard let data = data, error == nil else {
print("Error occured while accessing data with URL")
return
}
do {
let decoded = try JSONDecoder().decode([WordData].self, from: data)
comp(decoded)
if let sound = decoded[0].phonetics[0].audio,
let sound2 = decoded[0].phonetics[1].audio {
print("sound = \(sound)")
let nonEmpty = (sound != "") ? sound : sound2 //write switch cases or another ternary with more urls to choose from if both are empty
self.audioUrl = URL(string: nonEmpty)
// url = URL(string: sound2)
do {
try AVAudioSession.sharedInstance().setMode(.default)
try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
self.player = AVPlayer(url: self.audioUrl!)
guard let player = self.player else { return }
player.play()
} catch let error {
print(error.localizedDescription)
}
}
//comp(decoded, entries.self)
} catch {
print("Error occured while decoding JSON into Swift structure \(error)")
}
})
dataTask.resume()
}
}
I need to call the searchBar.text value in another file inside this IBAction of class FirstDefinitionVC:
`
#IBAction func pronunciationButton(_ sender: UIButton) {
searchVC.fetchAudio(word: searchVC.word) { data in
self.wordData = data
}
}
This was one of my approaches to this, I tried to create a global model Word with an initializer also and it didn't work. Is there any way around it?
Im getting this error when i press any link.
Completion handler passed to
-[webView:decidePolicyForNavigationAction:decisionHandler:] was called more than once
The link open in safari but the app chrash.
Any suggestions ?
import UIKit
import WebKit
class Polering: UIViewController, WKNavigationDelegate {
// Connect the webView from the StoryBoard.
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Get the path of the index.html
guard let htmlPath = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "www") else {
return
}
// Create an URL to load it in the webView.
let url = URL(fileURLWithPath: htmlPath)
let request = URLRequest(url: url)
webView.navigationDelegate = self
webView.load(request)
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void) {
// Check for links.
if navigationAction.navigationType == .linkActivated {
// Make sure the URL is set.
guard let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}
// Check for the scheme component.
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
if components?.scheme == "http" || components?.scheme == "https" {
if navigationAction.targetFrame == nil {
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
} else {
decisionHandler(.allow)
}
// Open the link in the external browser.
UIApplication.shared.open(url)
// Cancel the decisionHandler because we managed the navigationAction.
decisionHandler(.cancel)
return
} else {
decisionHandler(.allow)
}
} else {
decisionHandler(.allow)
}
}
}
Because decisionHandler is being called 2 times. It must be called once.
if navigationAction.targetFrame == nil {
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
} else {
// =======> First time here
decisionHandler(.allow)
}
// Open the link in the external browser.
UIApplication.shared.open(url)
// Cancel the decisionHandler because we managed the navigationAction.
// =======> Second time here
decisionHandler(.cancel)
you can revise the logic fix duplicate calls to decisionHandler.
If logic is correct, just comment the decisionHandler line in else part.
if navigationAction.targetFrame == nil {
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
}
UIApplication.shared.open(url)
decisionHandler(.cancel)
I'm using Swift 5 and have integrated MessageKit into my app. Here's the expected functionality:
User selects the image via picker
uploadImage function is initiated
Image is uploaded and displayed
What's actually happening:
User selects the image via picker
Nothing happens from there.
I'm not getting a single error message. I've put in print statements to see if it's even entering the uploadImage() function but it's not kicking off. I've change the uipickerimagecontroller code to exact code in other places of my app that is working and even that's not kicking off the function. I know my code isn't pretty but I'm still learning (please don't judge lol). Can anyone help:
Variable setup
private var isSendingPhoto = true {
didSet {
DispatchQueue.main.async {
self.messageInputBar.leftStackViewItems.forEach { item in
item.inputBarAccessoryView?.isUserInteractionEnabled = !self.isSendingPhoto
}
}
}
}
ViewDidLoad
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messageInputBar.delegate = self as? InputBarAccessoryViewDelegate
messagesCollectionView.messagesDisplayDelegate = self
title = "MaybeDrinks"
// 1
let cameraItem = InputBarButtonItem(type: .system)
cameraItem.tintColor = .primary
cameraItem.image = #imageLiteral(resourceName: "camera")
// 2
cameraItem.addTarget(
self,
action: #selector(cameraButtonPressed),
for: .primaryActionTriggered
)
cameraItem.setSize(CGSize(width: 60, height: 30), animated: false)
messageInputBar.leftStackView.alignment = .center
messageInputBar.setLeftStackViewWidthConstant(to: 50, animated: false)
messageInputBar.setStackViewItems([cameraItem], forStack: .left, animated: false) // 3
}
private func sendPhoto(_ image: UIImage) {
isSendingPhoto = true
uploadImage(image) { [weak self] url in
guard let `self` = self else {
return
}
self.isSendingPhoto = false
guard let url = url else {
return
}
var message = Message(messageuser: self.sender, image: image)
message.downloadURL = url
self.save(message)
self.messagesCollectionView.scrollToBottom()
}
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true, completion: nil)
// 1
if let asset = info[.phAsset] as? PHAsset {
let size = CGSize(width: 500, height: 500)
PHImageManager.default().requestImage(
for: asset,
targetSize: size,
contentMode: .aspectFit,
options: nil) { result, info in
print("I'm in image picker")
guard let image = result else {
return
}
self.sendPhoto(image)
}
// 2
} else if let image = info[.originalImage] as? UIImage {
sendPhoto(image)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// MARK: - Actions
#objc private func cameraButtonPressed() {
let picker = UIImagePickerController()
picker.delegate = self as? UIImagePickerControllerDelegate & UINavigationControllerDelegate
picker.allowsEditing = true
if UIImagePickerController.isSourceTypeAvailable(.camera) {
picker.sourceType = .camera
} else {
picker.sourceType = .photoLibrary
}
present(picker, animated: true, completion: nil)
}
Upload Image function
private func uploadImage(_ image: UIImage, completion: #escaping (URL?) -> Void) {
print("im in upload")
// STEP 1. Declare URL, Request and Params
let url = URL(string: "https://localhost/messagepost.php")!
// declaring reqeust with further configs
var request = URLRequest(url: url)
// POST - safest method of passing data to the server
request.httpMethod = "POST"
// values to be sent to the server under keys (e.g. ID, TYPE)
let params = ["sender_id": user_id, "uuid": uuid, "sender": me, "recipient_id": rid, "recipient": recipient, "puuid": puuid]
// body
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// Compressing image and converting image to 'Data' type
guard let scaledImage = image.scaledToSafeUploadSize,
let data = scaledImage.jpegData(compressionQuality: 0.4) else {
return
}
// assigning full body to the request to be sent to the server
request.httpBody = createBodyWithParams(params, filePathKey: "file", imageDataKey: data, boundary: boundary)
print(request.httpBody as Any, "\(puuid).jpg")
URLSession.shared.dataTask(with: request) { (data, response, error) in
DispatchQueue.main.async {
// error occured
if error != nil {
Helper().showAlert(title: "Server Error", message: error!.localizedDescription, in: self)
return
}
do {
// save mode of casting any data
guard let data = data else {
Helper().showAlert(title: "Data Error", message: error!.localizedDescription, in: self)
return
}
// fetching JSON generated by the server - php file
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
// save method of accessing json constant
guard let parsedJSON = json else {
return
}
// uploaded successfully
if parsedJSON["status"] as! String == "200" {
let newurl = parsedJSON["path"]
print("did you upload", newurl as Any)
print("did you upload", parsedJSON["message"] as Any)
self.isSendingPhoto = true
guard let url = newurl else {
return
}
//uploadImage(image)
var message = Message(messageuser: self.sender, image: image)
message.downloadURL = url as? URL
self.save(message)
self.messagesCollectionView.scrollToBottom()
} else {
// show the error message in AlertView
if parsedJSON["message"] != nil {
let message = parsedJSON["message"] as! String
Helper().showAlert(title: "Error", message: message, in: self)
print("where am i", parsedJSON["message"] as Any)
}
}
} catch {
Helper().showAlert(title: "JSON Error", message: error.localizedDescription, in: self)
print("where am i 2")
}
}
}.resume()
let imageName = [UUID().uuidString, String(Date().timeIntervalSince1970)].joined()
}
private func save(_ message: Message) {
self.messagesCollectionView.scrollToBottom()
}
Make sure your view controller declares conformance to both UIImagePickerControllerDelegate and UINavigationControllerDelegate in either the class declaration:
class DirectMessageViewController: MessagesViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
[...]
}
or in one or more extensions:
extension DirectMessageViewController: UIImagePickerControllerDelegate {
[...]
}
extension DirectMessageViewController: UINavigationControllerDelegate {
[...]
}
I am developing an App for the iPhone using Xwebview which enables me to download a page then interact with the javascript on the downloaded page.
All works, but if the internet connection drops, a default local page is loaded, informing the user there is no internet connection. The page displays a retry button that, when pressed checks, the internet connection: if the connection is made the app tries to connect again to the external page and load the page into the webview.
I cannot get this to work: the code downloads the page (I can see this in my session data) but I can't get that page to load back into the webview.
override func viewDidLoad() {
super.viewDidLoad()
login()
}
func login()
{
// *********** Get stored hashkey **************
let hashcode = getHashcode()
// ********** Check network connection *********
let netConnection = Connection.isConnectedToNetwork()
print("net connection: ", netConnection)
if netConnection == true
{
if hashcode != "00000"
{
print("local key found", hashcode)
// We dont have local key
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
//webview.loadRequest(NSURLRequest(URL: NSURL(string: "about:blank")!))
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let url:NSURL = NSURL(string: serverLocation + onlineLoginApi)!
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData
let paramString = "/?username=username&password=password"
request.HTTPBody = paramString.dataUsingEncoding(NSUTF8StringEncoding)
let task = session.downloadTaskWithRequest(request) {
(
let location, let response, let error) in
guard let _:NSURL = location, let _:NSURLResponse = response where error == nil else {
print("error")
return
}
let urlContents = try! NSString(contentsOfURL: location!, encoding: NSUTF8StringEncoding)
guard let _:NSString = urlContents else {
print("error")
return
}
print(urlContents)
}
task.resume()
// you must tell webview to load response
webview.loadRequest(request)
}
else{
print("local key found", hashcode)
// ********* Found local key go to site pass key over ************
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let req = NSMutableURLRequest(URL: NSURL(string:serverLocation + onlineLoginApi + "?hashcode=\(hashcode)")!)
req.HTTPMethod = "POST"
req.HTTPBody = "/?hashcode=\(hashcode)".dataUsingEncoding(NSUTF8StringEncoding)
NSURLSession.sharedSession().dataTaskWithRequest(req)
{ data, response, error in
if error != nil
{
//Your HTTP request failed.
print(error!.localizedDescription)
} else {
//Your HTTP request succeeded
print(String(data: data!, encoding: NSUTF8StringEncoding))
}
}.resume()
webview.loadRequest(req)
}
}
else{
// No connection to internet
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
view.addSubview(webview)
webview.loadPlugin(jsapi(), namespace: "jsapi")
let root = NSBundle.mainBundle().resourceURL!
let url = root.URLByAppendingPathComponent("/www/error-no-connection.html")
webview.loadFileURL(url, allowingReadAccessToURL: root)
print("No internet connection")
}
}
class jsapi: NSObject {
// Reconnect button on interface
func retryConnection()
{
print("Reconnect clicked")
dispatch_async(dispatch_get_main_queue())
{
let netConnections = Connection.isConnectedToNetwork()
if netConnections == true {
let netalert = UIAlertView(title: "Internet on line", message: nil, delegate: nil, cancelButtonTitle: "OK")
netalert.show()
let url = self.serverLocation + self.onlineLoginApi
let hashcode = ViewController().getHashcode()
if(hashcode != "00000") {
let url = url + "?hashcode=\(hashcode)"
print("url: ", url)
}
ViewController().loadPagelive(url)
}
else{
let netalert = UIAlertView(title: "Internet off line", message: nil, delegate: nil, cancelButtonTitle: "OK")
netalert.show()
}
}
print("retryConnect end")
}
}
You try to perform the loadPagelive(url) on a new instance of your ViewController, not on the current one shown on the screen, that's why you don't see any update.
You should create a delegate or a completion block in order to execute code on you ViewController instance loaded on the screen: every time you do ViewController(), a new object is created.
You can try using the delegate pattern, which is simple to achieve. I will try to focus on the important part and create something that can be used with your existing code:
class ViewController: UIViewController {
let jsapi = jsapi() // You can use only 1 instance
override func viewDidLoad() {
super.viewDidLoad()
// Set your ViewController as a delegate, so the jsapi can update it
jsapi.viewController = self
login()
}
func loadPagelive(_ url: URL) {
// Load page, probably you already have it
}
}
class jsapi: NSObject {
weak var viewController: ViewController?
func retryConnection() {
// We check if the delegate is set, otherwise it won't work
guard viewController = viewController else {
print("Error: delegate not available")
}
[... your code ...]
// We call the original (and hopefully unique) instance of ViewController
viewController.loadPagelive(url)
}
}
I have wrote some code to load all html from url and parse it to remove header. So I got the rest of the html under header to show.
However, after clicking event in the body html, the screen shows full html from the URL.
Is there any solution for this? or Did I make a mistake to approach this problem?
The code I made is below
import UIKit
import Fuzi
class ViewController: UIViewController {
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
let myURLString = "http://yahoo.com"
var myHTMLString = ""
guard let myURL = URL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
return
}
do {
myHTMLString = try String(contentsOf: myURL, encoding: .utf8)
} catch let error {
print("Error: \(error)")
}
do {
// if encoding is omitted, it defaults to NSUTF8StringEncoding
let doc = try! HTMLDocument(string: myHTMLString, encoding: String.Encoding.utf8)
let fullHtml:String = (doc.firstChild(xpath: "//*")?.rawXML)!
if let header = doc.firstChild(xpath: "//body/div/header") {
let headerString:String = header.rawXML
let withoutHeader = fullHtml.replacingOccurrences(of: headerString, with: "")
webView.loadHTMLString(withoutHeader as String, baseURL: nil)
}
} catch let error{
print(error)
}
}
Try this, it works with UIWebViewNavigationType.linkClicked. You can modify it to use with other UIWebViewNavigationType.
import UIKit
import Fuzi
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
let myURLString = "http://yahoo.com"
var myHTMLString = ""
guard let myURL = URL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
return
}
do {
myHTMLString = try String(contentsOf: myURL, encoding: .utf8)
} catch let error {
print("Error: \(error)")
}
do {
// if encoding is omitted, it defaults to NSUTF8StringEncoding
let doc = try! HTMLDocument(string: myHTMLString, encoding: String.Encoding.utf8)
let fullHtml:String = (doc.firstChild(xpath: "//*")?.rawXML)!
if let header = doc.firstChild(xpath: "//body/div/header") {
let headerString:String = header.rawXML
let withoutHeader = fullHtml.replacingOccurrences(of: headerString, with: "")
webView.loadHTMLString(withoutHeader as String, baseURL: nil)
}
} catch let error{
print(error)
}
}
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if navigationType == .linkClicked
{
let myURLString = webView.request?.url.absoluteString
webView.stopLoading()
var myHTMLString = ""
let myURL = URL(string: myURLString)
do {
myHTMLString = try String(contentsOf: myURL, encoding: .utf8)
} catch let error {
print("Error: \(error)")
}
do {
// if encoding is omitted, it defaults to NSUTF8StringEncoding
let doc = try! HTMLDocument(string: myHTMLString, encoding: String.Encoding.utf8)
let fullHtml:String = (doc.firstChild(xpath: "//*")?.rawXML)!
if let header = doc.firstChild(xpath: "//body/div/header") {
let headerString:String = header.rawXML
let withoutHeader = fullHtml.replacingOccurrences(of: headerString, with: "")
webView.loadHTMLString(withoutHeader as String, baseURL: nil)
}
} catch let error{
print(error)
}
}
return true
}
I found an answer!
Thanks to #Sherman for the hint to solve this problem.
I had to replace a line of code below
if navigationType == .linkClicked
to
if navigationType == UIWebViewNavigationType.linkClicked
Then It works!