How to reference a URL object inside a function from ViewController.swift? - swift

I am trying to pass a web page to another WebView from ViewController.swift. The URL is initiated by another class.
So far I was able to create the webView, create the segue, and load a dummy web page inside that controller upon tapping on the button from ViewController.swift. When I tap the button, the correct web address gets printed on the console with print(wikiURL) call.
I tried to call the variable by first declaring a global variable inside ViewController, then modifying it inside the function, then trying to reach from the webView controller, by setting up variables.
This is the function I am calling the segue from:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "WikiViewController") as? WikiViewController {
guard let capital = view.annotation as? Capital else { return }
let wikiURL = capital.url
print(wikiURL)
navigationController?.pushViewController(vc, animated: true)
}
And this is my webView controller:
class WikiViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
title = webView.title
let request = URLRequest(url: URL(string: "")!)
webView.load(request)
}
}
I should be able to replace URL(string: "")!) with wikiURL, then load it inside the webView.
Thank you for any assistance.

In WikiViewController, instead of creating url of type Capital?, it must be of type URL?, i.e.
var url: URL?
Use this url to create the URLRequest for WKWebView.
class WikiViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
var url: URL?
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
title = webView.title
if let url = self.url {
let request = URLRequest(url: url)
webView.load(request)
}
}
}
Usage:
When creating instance of WikiViewController in mapView(_:,annotationView:, calloutAccessoryControlTapped:) method, set the property vc.url as wikiURL.
if let vc = storyboard?.instantiateViewController(withIdentifier: "WikiViewController") as? WikiViewController {
guard let capital = view.annotation as? Capital else { return }
let wikiURL = capital.url
print(wikiURL)
vc.url = wikiURL //here......
navigationController?.pushViewController(vc, animated: true)
}
Note: I've assumed that wikiURL is of type URL?. In case it is String, create a URL instance using that String and then set it as vc.url.

Related

Testing tableview.reloadData()

while using a MockTableView this code still not calling reloadData() from the mock,
please i wanna know what is wrong here.
following this book: Test-Driven IOS Development with Swift 4 - Third Edition
page 164, i was as an exercise
full code repo - on github
ItemListViewController.swift
import UIKit
class ItemListViewController: UIViewController, ItemManagerSettable {
#IBOutlet var tableView: UITableView!
#IBOutlet var dataProvider: (UITableViewDataSource & UITableViewDelegate &
ItemManagerSettable)!
var itemManager: ItemManager?
override func viewDidLoad() {
super.viewDidLoad()
itemManager = ItemManager()
dataProvider.itemManager = itemManager
tableView.dataSource = dataProvider
tableView.delegate = dataProvider
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
#IBAction func addItem(_ sender: UIBarButtonItem) {
if let nextViewController =
storyboard?.instantiateViewController(
withIdentifier: "InputViewController")
as? InputViewController {
nextViewController.itemManager = itemManager
present(nextViewController, animated: true, completion: nil)
}
}
}
ItemListViewControllerTest.swift
import XCTest
#testable import ToDo
class ItemListViewControllerTest: XCTestCase {
var sut: ItemListViewController!
var addButton: UIBarButtonItem!
var action: Selector!
override func setUpWithError() throws {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier:
"ItemListViewController")
sut = vc as? ItemListViewController
addButton = sut.navigationItem.rightBarButtonItem
action = addButton.action
UIApplication.shared.keyWindow?.rootViewController = sut
sut.loadViewIfNeeded()
}
override func tearDownWithError() throws {}
func testItemListVC_ReloadTableViewWhenAddNewTodoItem() {
let mockTableView = MocktableView()
sut.tableView = mockTableView
guard let addButton = sut.navigationItem.rightBarButtonItem else{
XCTFail()
return
}
guard let action = addButton.action else{
XCTFail()
return
}
sut.performSelector(onMainThread: action, with: addButton, waitUntilDone: true)
guard let inputViewController = sut.presentedViewController as?
InputViewController else{
XCTFail()
return
}
inputViewController.titleTextField.text = "Test Title"
inputViewController.save()
XCTAssertTrue(mockTableView.calledReloadData)
}
}
extension ItemListViewControllerTest{
class MocktableView: UITableView{
var calledReloadData: Bool = false
override func reloadData() {
calledReloadData = true
super.reloadData()
}
}
}
You inject a MockTableview Then you call loadViewIfNeeded(). But because this view controller is storyboard-based and the table view is an outlet, the actual table view is loaded at this time. This replaces your MockTableview.
One solution is:
Call loadViewIfNeeded() first
Inject the MockTableview to replace the actual table view
Call viewDidLoad() directly. Even though loadViewIfNeeded() already called it, we need to repeat it now that we have a different tableview in place.
Another possible solution:
Avoid MockTableview completely. Continue to use a real table view. You can test whether it reloads data by checking whether the number of rows matches the changed data.
Yet another solution:
Avoid storyboards. You can do this with plain XIBs (but these lack table view prototype cells) or programmatically.
By the way, I see all your tearDownWithError() implementations are empty. Be sure to tear down everything you set up. Otherwise you will end up with multiple instances of your system under test alive at the same time. I explain there here: https://qualitycoding.org/xctestcase-teardown/

How to Autofill forms in WKWebView Using User Defaults or JavaScript

Hello Swift Developers!.
I am new to swift programming and need little help.
I am developing a very simple app, that should simply load the web using WkWebView and autofill the shipping form it have in on of its page.
I have successfully managed to fetch the page in webView(WkWebView).
First it loads this url https://www.adidas.com/us, after selecting the item in to cart it gets to this delivery page https://www.adidas.com/us/delivery where we have to fill this form. before this, no login information needed.
I am trying to make it done with both UIWebView and WkWebView but to no avail, here's my code, UIWebView part is commented.
class ViewController: UIViewController, WKUIDelegate {
#IBOutlet weak var uiWebView: UIWebView!
var webView: WKWebView!
let url: String = "https://www.adidas.com/us/delivery"
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
//WKWebView programatically so it can run below iOS 11
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
if let safeUrl = URL(string: url){
let request = URLRequest(url: safeUrl)
webView.load(request)
}
// uiWebView.loadRequest(NSURLRequest(url: NSURL(string: url)! as URL) as URLRequest)
// let result = uiWebView.stringByEvaluatingJavaScript(from: "document.title")
// print("result is: \(result!)")
self.webView.evaluateJavaScript("document.getElementById('di-id-cab9a55c-9d253ce3').value = 'Hello'") { (result, error) in
print(result) //This will Print Hello
}
}
// func webViewDidFinishLoad(_ webView: UIWebView) {
//
// let email = defaults.string(forKey: "EMAIL")
// let password = defaults.string(forKey: "Pass")
//
// let fillForm = "document.getElementById('f_707d6a95-3ef9-4b76-a162-9361b4ef7d4d').value = \(password)"
// webView.stringByEvaluatingJavaScript(from: fillForm)
// }
}
And Here's the screenshot of inspect element of First Name field.
After this I would use user default for autofill data. that step I know how to do just stuck here!
Can I have any helpful code snippet or suggestion please? I am really stuck at this point!
Thanks in advance
First: stop using UIWebView since it's deprecated from one side and stop supporting by AppStore from Dec 2020.
You should set navigationDelegate to you WKWebView and set your values to the fields on webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) when the page is loaded e.g.:
override func viewDidLoad() {
super.viewDidLoad()
...
webView?.navigationDelegate = self
...
}
extension ViewController : WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// Set a delay for dynamic pages
//DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let fillForm = "document.getElementsByName('firstName')[0].value = 'My Name'"
webView.evaluateJavaScript(fillForm, completionHandler: nil)
//}
}

Xcode Wkwebview not Loading

I have a Xcode Project with a Webview and a TabBar and with the TabBar I can switch between WebViews.
My Problem is that when I put something in my ShoppingCard under lieferworld.de and switch with the TabBar to my Shopping Card url the Items in there are not Visible. How can I solve this? the ShoppingCard URL ends with .php. Below is the code which is implemented
Here is also a Video were you can see the error:
https://youtu.be/qU3Mu1G7MY0
Viewhome:
import UIKit
import WebKit
class viewHome: UIViewController, WKUIDelegate {
#IBOutlet var webViewHome: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webViewHome = WKWebView(frame: .zero, configuration: webConfiguration)
webViewHome.uiDelegate = self
webViewHome.configuration.preferences.javaScriptEnabled = true
//webViewHome.configuration.preferences.javaEnabled = true
view = webViewHome
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://lieferworld.de")
let request = URLRequest(url: url!)
webViewHome.configuration.preferences.javaScriptEnabled = true
//webViewHome.configuration.preferences.javaEnabled = true
webViewHome.load(request)
}
#IBAction func GoBackHome(_ sender: Any) {
if webViewHome.canGoBack {
webViewHome.goBack()
}
}
#IBAction func GoForwardHome(_ sender: Any) {
if webViewHome.canGoForward {
webViewHome.goForward()
}
}
}
ViewShopping | Shopping Cart Class:
import UIKit
import WebKit
class viewShopping: UIViewController, WKUIDelegate {
#IBOutlet var webViewShopping: WKWebView!
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webViewShopping = WKWebView(frame: .zero, configuration: webConfiguration)
webViewShopping.uiDelegate = self
//webViewShopping.configuration.preferences.javaEnabled = true
webViewShopping.configuration.preferences.javaScriptEnabled = true
view = webViewShopping
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://lieferworld.de/warenkorb.php")
let request = URLRequest(url: url!)
webViewShopping.configuration.preferences.javaScriptEnabled = true
//webViewShopping.configuration.preferences.javaEnabled = true
webViewShopping.load(request)
}
#IBAction func goBackShoppingCart(_ sender: Any) {
if webViewShopping.canGoBack {
webViewShopping.goBack()
}
}
#IBAction func goForwardShoppingCart(_ sender: Any) {
if webViewShopping.canGoForward {
webViewShopping.goForward()
}
}
#IBAction func webViewRefresh(_ sender: Any) {
webViewShopping.reload()
}
}
Your data is not being shared between the two UIViewControllers because your WKWebViews are different instances of each other and don't share the same data. It's as if you opened two different tabs in your web browser. If you're logged in, and the cart data is stored on the server, it should work, if you refresh the cart's page. If it's cookie based, you'll need to ensure those cookies are common among the two web views. Instead, use the tab to redirect to the cart's page instead of creating a new View Controller instance, or put the instance of the WKWebView into a static variable that can be shared between the two tabs. You'll have to do a bit of hiding/showing of that view so that you don't see it loading, but that can be handled via the delegate.
If the data is stored on the server, you can simple just reload the webpage within the viewDidAppear() overridden function in your VC. It really depends on what you're going for, but those are a few suggestions that'll work.

Why delegate event is not received swift?

I would like to pass data from EditPostViewController to NewsfeedTableViewController using delegates, but func remove(mediaItem:_) is never called in the adopting class NewsfeedTableViewController. What am I doing wrong?
NewsfeedTableViewController: UITableViewController, EditPostViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//set ourselves as the delegate
let editPostVC = storyboard?.instantiateViewController(withIdentifier: "EditPostViewController") as! EditPostViewController
editPostVC.delegate = self
}
//remove the row so that we can load a new one with the updated data
func remove(mediaItem: Media) {
print("media is received heeeee")
// it does't print anything
}
}
extension NewsfeedTableViewController {
//when edit button is touched, send the corresponding Media to EditPostViewController
func editPost(cell: MediaTableViewCell) {
let editPostVC = storyboard?.instantiateViewController(withIdentifier: "EditPostViewController") as? EditPostViewController
guard let indexPath = tableView.indexPath(for: cell) else {
print("indexpath was not received")
return}
editPostVC?.currentUser = currentUser
editPostVC?.mediaReceived = cell.mediaObject
self.navigationController?.pushViewController(editPostVC!, animated: true)
}
protocol EditPostViewControllerDelegate: class {
func remove(mediaItem: Media)
}
class EditPostViewController: UITableViewController {
weak var delegate: EditPostViewControllerDelegate?
#IBAction func uploadDidTap(_ sender: Any) {
let mediaReceived = Media()
delegate?.remove(mediaItem: mediaReceived)
}
}
The objects instantiating in viewDidLoad(:) and on edit button click event are not the same objects. Make a variable
var editPostVC: EditPostViewController?
instantiate in in viewDidLoad(:) with delegate
editPostVC = storyboard?.instantiateViewController(withIdentifier: "EditPostViewController") as! EditPostViewController
editPostVC.delegate = self
and then present it on click event
navigationController?.pushViewController(editPostVC, animated: true)
or
present(editPostVC, animated: true, completion: nil)
you can pass data from presenter to presented VC before or after presenting the VC.
editPostVC.data = self.data
I suggest having a property in NewsfeedTableViewController
var editPostViewController: EditPostViewController?
and then assigning to that when you instantiate the EditPostViewController.
The idea is that it stops the class being autoreleased when NewsfeedTableViewController.viewDidLoad returns.

WKWebview adjustment to be in the background

I've switched one of my apps to use WKWebview and it's the first time for me, I had a problem which is as you know I can't control it from the storyboard, I have a scrollview inside a sidebar that comes out when you click the button on the upper left, but it seems like it appears in the back of the view, how can I fix this please?
let configuration = WKWebViewConfiguration()
configuration.preferences = preference
webView = WKWebView(frame: view.bounds, configuration: configuration)
view.addSubview(webView)
please note that I already clicked the button in that screeshot but the scroll didn't show]1
Use this,
first import
import WebKit
set delegate of WKWebview
WKScriptMessageHandler , WKNavigationDelegate
declare webview
var webView: WKWebView!
var webConfiguration:WKWebViewConfiguration! = nil
override func loadView() {
webView = WKWebView(frame: .zero, configuration: self.webConfig())
webView.navigationDelegate = self
view = webView
}
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
/*if let url = URL(string: "http://192.168.1.122/arvee/piyush/mobileViewPages/owner/manager.html") {
let request = URLRequest(url: url)
webView.load(request)
}*/
let url = Bundle.main.url(forResource: "AddAppointment/bookApp", withExtension:"html")
let request = URLRequest(url: url!)
webView.load(request)
}
WKWebview Delegate
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
print(error)
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let data = JSON(message.body)
}
func webConfig() -> WKWebViewConfiguration {
//if webConfiguration != false {
webConfiguration = WKWebViewConfiguration()
let userController = WKUserContentController()
userController.add(self, name: "buttonClicked")
userController.add(self, name: "pageLoaded")
webConfiguration.userContentController = userController
//}
return webConfiguration
}