Swift: How do unit test UIWebView loading? - ios

My app has a basic webview controller to perform some operations. This view in the storyboard is not much besides a wrapper around a UIWebView. The controller itself has various public functions that can be called to load pages in the webview, like so:
class WebViewController: UIViewController, UIWebViewDelegate {
// MARK: Properties
#IBOutlet var webView: UIWebView!
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
loadHomePage()
}
// MARK: Public
public func loadHomePage() {
navigateWebView(to: HOME_PAGE)
}
public func loadSettingsPage() {
navigateWebView(to: SETTINGS_PAGE)
}
public func loadSignOutPage() {
navigateWebView(to: SIGN_OUT_PAGE)
}
// MARK: Private
private func navigateWebView(to url: String) {
let request = URLRequest(url: URL(string: url)!)
webView.loadRequest(request)
}
I'm trying to write unit tests that verify that the proper URL is sent to the loadRequest function of the webview. Note that I don't actually care about loading the URL; this is just a unit test, so all I really want to test is that loadSettingsPage sends a URLRequest with the SETTINGS_PAGE URL to the webview to load, for example.
I tried something like this, with no success:
_ = webViewController.view // Calls viewDidLoad()
XCTAssertEqual(webViewController.webView.request?.url?.absoluteString, HOME_PAGE)
The value of the first part of the assertEqual was nil.
I assume I need to mock out the webView somehow but I'm not sure how to go about that. Any suggestions?
As a follow-up, I'd also like to be able to test when things like webView.reload() and webview.goBack() are called, so any pointers there would be appreciated as well. Thanks!

Related

The isExternalURL function from the IntuneMAMWebViewPolicyDelegate is not called

I have implemented the iOS Intune SDK in my app. The app applies the MAM policies and is working properly. I am trying to implement the Webview content restrictions using the IntuneMAMWebViewPolicyDelegate and isExternalURL as described here
The isExternalURL function is never called.
This is the view containing my WKWebView
import UIKit
import WebKit
import IntuneMAMSwift
class WebviewPage: UIViewController {
let webViewPolicyDelegage = WebViewPolicyClass.init();
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
IntuneMAMPolicyManager.instance().setWebViewPolicyDelegate(webViewPolicyDelegage, forWebViewer: webView!)
let webaddress = "https://www.google.com"
if let url = URL(string: webaddress) {
let urlRequest = URLRequest(url: url)
self.webView.load(urlRequest)
}
}
}
This is my delegate implementation
import IntuneMAMSwift
class WebViewPolicyClass: NSObject, IntuneMAMWebViewPolicyDelegate {
func isExternalURL(_ url: URL) -> Bool {
// TODO: Check if URL is external
return true
}
}
The app MAM policy is configured to require the managed Edge browser.
Any ideas why the delegate method is not called?

Instance member 'webView' cannot be used on type 'MyWebView(UIview)'

I am creating one SDK to get the url from user and load. So ai m creating my own class with WKWebview. But i am getting few issues about Instance member 'webView' cannot be used on type 'MyWebView(UIview)'
Code :
import Foundation
import WebKit
public class MyWebView: UIView, WKNavigationDelegate {
// initialize the view
var webView: WKWebView!
// load the view
private func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
}
// get the url and load the page
public static func loadUrl(Url: String) {
MyWebView.webView.load(URLRequest(url: URL(string: Url)!))
}
}
In my loadUrl, what ever user sending i need to use that url and load the url. Same in my view controller will look like :
import UIKit
class ViewController: UIViewController {
var webView: MyWebView!
override func loadView() {
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Any help would be great.
Your loadUrl function should not be static, since it needs access to an instance property, webView. Making the function non-static solves the issue.
Also a couple of minor improvements: don't force unwrap the URL init, since with an incorrect input that will crash. Use optional binding to safely unwrap it instead. I'd also suggest renaming the input argument label on loadUrl, since there's no point in having to right out loadUrl(Url:) every time you call the func, loadUrl( reads more naturally.
public func loadUrl(_ urlString: String) {
guard let url = URL(string: urlString) else { return }
webView.load(URLRequest(url: url))
}

How to stream music on app using the API Jukebox with swift?

I want to stream music to my app using this library I found online called Jukebox. Heres a link to their page: https://github.com/teodorpatras/Jukebox
import UIKit
import Jukebox
class ViewController: UIViewController {
var jukebox = Jukebox()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.jukebox = Jukebox(delegate: self, items: [ JukeboxItem(URL: NSURL(string: "http://mixtapemonkey.com/mixtapes/zip/491/Chance%20The%20Rapper%20-%2010Day%20Official%20Final/03%20Nostalgia.mp3")!)
])
}
I get an error where it says 'Jukebox(delegate: self' stating that I cannot convert the value of type 'ViewController' to expected argument type 'JukeboxDelegate?'
#IBAction func Play(sender: UIButton) {
self.jukebox.play()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
where am i going wrong? Thanks
My guess is that you've failed to inherit from the Jukebox delegate in your class declaration. Should be something like this:
class ViewController: UIViewController, JukeboxDelegate {
var jukebox = Jukebox()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.jukebox = Jukebox(delegate: self, items: [ JukeboxItem(URL: NSURL(string: "http://mixtapemonkey.com/mixtapes/zip/491/Chance%20The%20Rapper%20-%2010Day%20Official%20Final/03%20Nostalgia.mp3")!)
])
}
}
Since these delegate methods aren't optional, you'll also have to make your ViewController conform to the delegate by including the delegate methods, listed below.
Defines the five possible states that Jukebox can be in.
public protocol JukeboxDelegate : class {
func jukeboxStateDidChange(jukebox : Jukebox)
func jukeboxPlaybackProgressDidChange(jukebox : Jukebox)
func jukeboxDidLoadItem(jukebox : Jukebox, item : JukeboxItem)
}
The library seems to be written in Swift, and unless you put some extra effort into it, delegate methods can't be optional which is why you're getting the error about your VC not conforming to the methods. You at least have to have the method signatures in your class, and it's up to you whether you want anything to happen when they're called.
i don't know Jukebox - but something doesn't work with your ViewController. Your ViewController is a class not a class instance. You need to first create an instance of this controller.
Maybe something in this direction helps .. but im not good in Swift, too.
let VCJukeBox = UIViewController()?
VCJukeBox.jukebox = Jukebox(delegate: self, items: [ JukeboxItem(URL: NSURL(string: "yoururl.mp3")!)
])
Im not good in swift, too - but maybe this helps somehow.

DELAY issues: how to apply styles immediately or not show loaded webpage until styles are applied?

Technologies Used: XCode 6, iOS8, Swift
I'm loading a webpage in a uiwebview and I'm also appending a new stylesheet to the body of that webpage and overwriting some of its styles. But, there is a delay (maybe 1 second or 2) between when the webpage loads and the styles are applied so you can see the webpage before its restyled. I'm using javascript to append the new styles to the body of the webpage. How can I fix this so that the webpage will only show with the styles are already applied? Here is my code:
import UIKit
class SecondViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var website: UIWebView!
var url = "http://www.fake-website-url.net"
func loadUrl() {
let requestURL = NSURL(string: url)
let request = NSURLRequest(URL: requestURL!)
website.loadRequest(request)
}
override func viewDidLoad() {
super.viewDidLoad()
website.delegate = self
loadUrl()
}
func webViewDidFinishLoad(website: UIWebView) {
var loadStyles = "var script = document.createElement('link');script.type = 'text/css';script.rel = 'stylesheet';script.href = 'http://fake-url.styles.css';document.getElementsByTagName('body')[0].appendChild(script);"
website.stringByEvaluatingJavaScriptFromString(loadStyles)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Note, I'm using Swift.
What I would do is create a property to store the downloaded page. Then override the property setter to add your custom style sheet after the page is saved to that property. Then finally load it into your Web View.
Hope that makes sense.

Determinate finish loading website in webView with Swift in Xcode

i'm trying to create a webapp with swift in xcode, this is my current code:
IBOutlet var webView: UIWebView!
var theBool: Bool = false
var myTimer = NSTimer()
#IBOutlet var progressBar: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "http://stackoverflow.com")
let request = NSURLRequest(URL: url)
webView.loadRequest(request)
}
I've a question, How i can determinate the finish loading of the page in webView?
Do you know a documentation that contains all the definitions of the WebView? I mean .. (start / end load, the current url, title page, etc ..)?
(Sorry for my English).
Through the UIWebView delegate call.
You need to set your webViews delegate to the current controller, and conform to the UIWebViewDelegate protocol. When the webView finished loading the page func webViewDidFinishLoad(_ webView: UIWebView) will get called.
For WKWebview there is also wknavigationdelegate didfinish, but not do the trick as the SO question WKWebView didn't finish loading, when didFinishNavigation is called - Bug in WKWebView? and this answer show.
I also found when the page to load is very complicate and dinamic, UIWebview's webViewDidFinishLoad(- webView: UIWebView) also not works.
And I think use native swift or objective-c to detect when the page is loaded is a bad choice. The more flexable and effective way is to use javascript to call the native swift code when page finishing loading. What's more this also work for a specific dom finishing loading event and very dynamic content.
For how to call swift from javascript refer to this post.
Here is a sample code for dynamic content with anjularjs.
js,
var homeApp = angular.module('home', ['ui.bootstrap', 'ngAnimate']);
homeApp
.directive("printerinfo",
function() {
return {
restrict: "E",
link: function() { //call navite swift when page finishing loading
try {
window.webkit.messageHandlers.testHandler.postMessage("Hello from JavaScript");
} catch(err) {
console.log('The native context does not exist yet');
}
},
templateUrl: "/static/tpls/cloud/app/printerinfo.php",
}
})
swift3,
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.name)
if(message.name == "testHandler") {
//webpage content loaded
print(Date())
print("javascript test call swift")
}
Here I use angularjs to check element ready, you can also refter how-to-call-a-function-after-a-div-is-ready or check-if-a-given-dom-element-is-ready or jquery-ready-equivalent-event-listener-on-elements for more.

Resources