Check if URL has changed in WebView? - ios

I'm trying to check if the URL has changed in the webView. For example if I were to initially load a page like a Wordpress Sign In page, and I wanted to know when it changed and got redirected to the login page. I tried using this resource enter link description here but the answer seems to be incomplete and does not work.
func validateUrl (stringURL : NSString) -> Bool {
var urlRegEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
let predicate = NSPredicate(format:"SELF MATCHES %#", argumentArray:[urlRegEx])
var urlTest = NSPredicate.predicateWithSubstitutionVariables(predicate)
return predicate.evaluateWithObject(stringURL)
}
urlTest is never called so i'm not sure the purpose of it.
if (validateUrl(stringURL: "http://google.com")) {
//will return true
print("Do Stuff");
}
else {
print("OTHER STUFf")
//If it is false then do stuff here.
}
And then to call this function
func webView(WebViewNews: UIWebView!, shouldStartLoadWithRequest request: NSURLRequest!, navigationType: UIWebViewNavigationType) -> Bool {
if (validateUrl(request.URL().absoluteString())) {
//if will return true
print("Do Stuff");
}
}
I added a return function at the end of my code, but the example does not include a return. I have very little experience in Webview, so any advice or help would be appreciated.

Use the webViewDidFinishLoad function of the UIWebViewDelegate to get the current URL loaded in your webview. Everytime the webview loads a URL the function webViewDidFinishLoad is called.
class YourClass: UIViewController, UIWebViewDelegate {
let initialURL = URL(string: "https://www.google.com.pe/")
#IBOutlet weak var webView:UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.loadRequest( URLRequest(url: initialURL) )
}
func webViewDidFinishLoad(_ webView: UIWebView) {
if webView.request != URLRequest(url: initialURL!) {
//DO YOUR STUFF HERE
}
}
}

Related

Instagram IOS API Implementation

I am trying to get the authentication token to be able to grab a user's info. I registered my app in the instagram api page and everything seems to work except that I am not able to retrieve an authentication token or anything information. (I think it might be because of the redirect url i just made a dummy url) I can login to my instagram account and authorize my app to retrieve information but I dont get anything printed on my console so Im assuming im not being able to retrieve anything.
the code:
import UIKit
import WebKit
class ViewController3: UIViewController, UIWebViewDelegate {
#IBOutlet weak var WebView1: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let authURL = String(format: "%#?client_id=%#&redirect_uri=%#&response_type=token&scope=%#&DEBUG=True", arguments: [API.INSTAGRAM_AUTHURL,API.INSTAGRAM_CLIENT_ID,API.INSTAGRAM_REDIRECT_URI, API.INSTAGRAM_SCOPE])
let urlRequest = URLRequest.init(url: URL.init(string: authURL)!)
WebView1.load(urlRequest)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var tts = segue.destination as! Manage_Ad_VC
tts.S_Media_R = "Instagram"
}
func WebView1(_ WebView1: UIWebView, shouldStartLoadWith request:URLRequest, navigationType: UIWebViewNavigationType) -> Bool{
return checkRequestForCallbackURL(request: request)
}
func checkRequestForCallbackURL(request: URLRequest) -> Bool {
print("Instagram authentication token ==")
let requestURLString = (request.url?.absoluteString)! as String
if requestURLString.hasPrefix(API.INSTAGRAM_REDIRECT_URI) {
let range: Range<String.Index> = requestURLString.range(of: "#access_token=")!
handleAuth(authToken: requestURLString.substring(from: range.upperBound))
return false;
}
return true
}
func handleAuth(authToken: String) {
print("Instagram authentication token ==", authToken)
}
}
struct API {
static let INSTAGRAM_AUTHURL = "https://api.instagram.com/oauth/authorize/"
static let INSTAGRAM_CLIENT_ID = "myclientidgoeshere"
static let INSTAGRAM_CLIENTSERCRET = " myclientsercretgoeshere "
static let INSTAGRAM_REDIRECT_URI = "http://www.dummyurl.com/just_a_made_up_dummy_url"
static let INSTAGRAM_ACCESS_TOKEN = ""
static let INSTAGRAM_SCOPE = "follower_list+public_content" /* add whatever scope you need https://www.instagram.com/developer/authorization/ */
}
enter image description here
U have used WKWebView and UIWebViewDelegate. It is not WKWebView delegate. This view has been inherited from UIView - not from uiwebview - that's why delegates methods does not work. Try to use WKNavigationDelegate with its methods.
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.url, url.host == "URL FOR OAUTH GOES HERE" {
if url.absoluteString.range(of: "access_token") != nil {
let urlParts = url.absoluteString.components(separatedBy: "=")
let code = urlParts[1]
let userInfoURL = "https://api.instagram.com/v1/users/self/?access_token=" + code
//Make request with the userInfoURL to retrieve the user Info.
}
}
return true
}
We need "Access token" to go further.But it seems empty in your case.Try to run local server either through node/jupiter/MAMP or anything and check your localhost page is popping up.
Once your localhost is up and running.Please paste the below link in the browser by replacing the client_id to yours.
https://www.instagram.com/oauth/authorize/?client_id=Your_Client_Id&redirect_uri=http://localhost:8000&response_type=token&scope=public_content
Make sure you are giving the same redirect uri while you registered.
Please follow this link for further clarification
Click on Authorize in the page to be displayed in the browser.And check the URL in the browser, your access token would be passed for the next page.Copy this token and use it in the code

Swift: How do unit test UIWebView loading?

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!

Scroll to bottom of webview programmatically with swift

I have the following webview:
#IBOutlet weak var webView_MyContent: UIWebView!
and load custom html content like so:
self.webView_MyContent.loadHTMLString(html, baseURL: nil)
I would like to scroll to the very bottom of the page programmatically when my content loads. How would this be accomplished in swift?
You can use scrollView property of UIWebView for that.
func webViewDidFinishLoad(_ webView: UIWebView) {
let scrollPoint = CGPoint(x: 0, y: webView.scrollView.contentSize.height - webView.frame.size.height)
webView.scrollView.setContentOffset(scrollPoint, animated: true)//Set false if you doesn't want animation
}
Note: Don't forgot to set delegate of your webView.
Didn't work for me with Swift 4 / WKWebView.
Instead I found the method webView.scrollToEndOfDocument()
To scroll down after loading is finished you can put it into this function:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.scrollToEndOfDocument(self)
}
Don't forget to import WebKit and to make your class the delegate of your WebView:
// ...
import WebKit
// ...
class ViewController: NSViewController, WKNavigationDelegate {
// ...
Advanced: scroll down after finishing AJAX requests
Now in my case I wanted to scroll down a page that used infinite scroll (when nearly reaching the end of the page it starts loading additional content).
This can be done by injecting JavaScript and overwriting the XMLHttpRequest method:
override func viewDidLoad() {
super.viewDidLoad()
// ...
String javascript = String(contentsOfFile: Bundle.main.path(forResource: "script", ofType: "js"))
webView.configuration.userContentController.add(self, name: "injectionHandler")
webView.configuration.userContentController.addUserScript(WKUserScript.init(source: javascript, injectionTime: .atDocumentEnd, forMainFrameOnly: false))
// ...
}
And in the file script.js in your Xcode project you'll put this:
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
this.addEventListener("load", function() {
var message = {"status": this.status, "requestUrl": url, "response": this.responseText, "responseURL": this.responseURL};
webkit.messageHandlers.injectionHandler.postMessage(message);
});
open.apply(this, arguments);
};
To handle this event (and if you want also catch the AJAX response) you have to add this method:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "injectionHandler", let dict = message.body as? Dictionary<String, AnyObject>, let status = dict["status"] as? Int, let response = dict["response"] as? String {
if status == 200 {
webView.scrollToEndOfDocument()
}
}
}
and make your class extend WKScriptMessageHandler:
class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHandler {
// ...

Pass url from UIWebView 1 in ViewController 1 to UIWebView 2 in ViewController 2

I have UIWebView in my first ViewController, and I want if the user clicks on any link inside UIWebView 1 should open new ViewController (ex. InfoViewController) and passes the URL to new UIWebView inside InfoViewController.
EDIT:
My Storyboard
My Code:
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet weak var webview: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = NSURL (string: "https://google.com");
let requestObj = NSURLRequest(URL: url!);
webview.delegate = self;
webview.loadRequest(requestObj);
}
func webview(WebViewNews: UIWebView!, shouldStartLoadWithRequest request: NSURLRequest!, navigationType: UIWebViewNavigationType) -> Bool {
if (navigationType == UIWebViewNavigationType.LinkClicked) {
//Push to new view controller here and pass new url:
let url = request.URL
let infoViewController = self.storyboard?.instantiateViewControllerWithIdentifier("InfoViewController") as! InfoViewController
infoViewController.passURL = url.absoluteString //Add passURL property in your InfoViewController
self.navigationController!.pushViewController(infoViewController, animated: true)
//prevent the current webview from loading the page
return false
}
//webview will load first time normally/other requests will load
return true
}
set the webview delegate to self:
webview.delegate = self
And then you can add this delegate method. This runs whenever it tries to load a url. You can add a if check to make sure it is a link they have clicked and run your code to change screen and prevent the current webview from loading the url by returning false:
//Update: got method name wrong:
func webView(webView: UIWebView!, shouldStartLoadWithRequest request: NSURLRequest!, navigationType: UIWebViewNavigationType) -> Bool {
if (navigationType == UIWebViewNavigationType.LinkClicked {
//Push to new view controller here and pass new url:
let url = request.URL
let infoViewController = self.storyboard?.instantiateViewControllerWithIdentifier("InfoViewController") as InfoViewController
infoViewController.passURL = url.absoluteString //Add passURL property in your InfoViewController
self.navigationController!.pushViewController(infoViewController, animated: true)
//prevent the current webview from loading the page
return false
}
//webview will load first time normally/other requests will load
return true
}
You need to pull the link from your first UIWebview:
iPhone - UIWebview - Get the URL of the link clicked
Open the new ViewController and pass the string.
Direct your second UIWebview to open the URL.

SFSafariViewController: how to provide custom activities?

The WWDC session of Safari View Controller mentioned that apps could provide custom activities through the method func safariViewController(controller: SFSafariViewController, activityItemsForURL URL: NSURL, title: String?) -> [UIActivity] of the delegate SFSafariViewControllerDelegate. I have tried to implement this method, but it is not called after I present the SFSafariViewCntroller. I also implemented another optional method of that delegate, func safariViewControllerDidFinish(_: SFSafariViewController), which does get called. I tried to add the "#objc" keyword to my method (required by some other protocols), but it seems not to change anything.
I am wondering what could go wrong.
Thanks!
Here's the example code for your reference. In your main view:
func safariViewController(controler: SFSafariViewController, activityItemsForURL: NSURL, title: String?) -> [UIActivity] {
//global variable for the url to be shared
webPageUrl = activityItemsForURL.absoluteString
//global variable for the title to be shared
webPageTitle = title!
let wcActivity = WeChatActivity()
let wcMoment = WeChatMoment()
return [wcActivity, wcMoment]
}
Custom activities 1
import UIKit
class WeChatActivity : UIActivity{
override init() {
self.text = ""
}
var text:String?
override func activityType()-> String {
return "WeChat"
}
override func activityImage()-> UIImage?
{
return UIImage(named: "WeChat")!
}
override func activityTitle() -> String
{
return "微信好友"
}
override class func activityCategory() -> UIActivityCategory{
return UIActivityCategory.Action
//you can change to .Share and it'll appear in the share line
}
func getURLFromMessage(message:String)-> NSURL
{
var url = "whatsapp://"
if (message != "")
{
url = "\(url)send?text=\(message)"
}
return NSURL(string: url)!
}
override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
return true;
}
override func performActivity() {
shareToWeChat("ftcweixin://?url=\(webPageUrl)&title=\(webPageTitle)&description=\(webPageDescription)&img=\(webPageImageIcon)&to=chat")
}
}
Custom Activity 2:
import UIKit
class WeChatMoment : UIActivity{
override init() {
self.text = ""
}
var text:String?
override func activityType()-> String {
return "WeChatMoment"
}
override func activityImage()-> UIImage?
{
return UIImage(named: "Moment")!
}
override func activityTitle() -> String
{
return "微信朋友圈"
}
override class func activityCategory() -> UIActivityCategory{
return UIActivityCategory.Action
}
func getURLFromMessage(message:String)-> NSURL
{
var url = "whatsapp://"
if (message != "")
{
url = "\(url)send?text=\(message)"
}
return NSURL(string: url)!
}
override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
return true;
}
override func performActivity() {
shareToWeChat("ftcweixin://?url=\(webPageUrl)&title=\(webPageTitle)&description=\(webPageDescription)&img=\(webPageImageIcon)&to=moment")
}
}
You'll be able to see the two new icons in the action line of the action sheet. You can also change it to appear in the share line, as explained in the code.
One final note, there are pitfalls for WeChat sharing on Safari View, as WeChat doesn't conform to Safari's sharing standard. You can click the WeChat Share icon and WeChat will be able to share. But you can only get the page title and url in Safari View's page, unlike with WKWebView where you can get everything using evaluateJavaScript. So you'll need to get the share image and description (for sharing to friends) from other places.

Resources