I am attempting to load a lunch menu PDF into a web view for a high school app that I am updating. Currently, it can load a PDF into the web view and display it just fine, but I want to speed up the monthly update process by having my app receive the link through Parse (Which I can update much quicker than updating the link in the app itself with Apple's 7 day review period), and then load the PDF. Currently, with what I have put together, my app will not load the PDF. Here's the entire view:
import UIKit
class AlaCarte_ViewController: UIViewController {
#IBOutlet weak var webviewAlaCarte: UIWebView!
var urlpath = String()
func loadAddressUrl(){
let requestURL = NSURL (string:urlpath)
let request = NSURLRequest(URL: requestURL!)
webviewAlaCarte.loadRequest(request)
alaCarteUpdate()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
clearPDFBackground(self.webviewAlaCarte)
}
func clearPDFBackground(webView: UIWebView) {
var view :UIView?
view = webView as UIView
while view != nil {
if NSStringFromClass(view?.dynamicType) == "UIWebPDFView" {
view?.backgroundColor = UIColor.clearColor()
}
view = view?.subviews.first as! UIView?
}
}
func alaCarteUpdate() {
var query = PFQuery(className: "AlaCarte")
query.getObjectInBackgroundWithId("rT7MpEFySU") {(AlaCarte: PFObject!, error: NSError!)-> Void in
if error == nil && AlaCarte != nil {
println(AlaCarte)
} else {
println(error)
}
let AlaCarteLink = AlaCarte["webaddress"] as! String
self.urlpath = AlaCarteLink
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadAddressUrl()
}
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 prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
The link is stored in my Parse app as "webaddress" and does not contain end quotations. Adding them does not help. Any ideas?
It looks to me like you're not telling the web view to load the URL once it's retrieved from Parse.
Try adding the following lines after self.urlpath = AlaCarteLink in alaCarteUpdate().
let requestURL = NSURL (string:self.urlpath)
let request = NSURLRequest(URL: requestURL!)
self.webviewAlaCarte.loadRequest(request)
I think it would also be a good idea to add a function that specifically loads a url string into your web view, so you can call it from both inside alaCarteUpdate(), and loadAddressUrl(), and avoid the duplicate 3x lines. I've assumed that you're loading the URL in loadAddressURL() so that you can show a local/cached document while retrieving the latest from Parse.
Related
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
I want the http task to run, setting the globally defined variable jwt. Then and only then do I want to run the seque that passes the jwt to my next activity.
I know that they are running out of order because the print statements are out of order. Capture is at the bottom. I redacted the actual jwt but where the black box is, is the jwt string.
Main view controller
import UIKit
var jwt = ""
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func submitLogin() {
let url = URL(string: "http://example.com")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options:.allowFragments) as? [String:Any] {
jwt = json["jwt"] as! String
print("Checkpoint 1 " + jwt)
}
} catch let err{
print(err.localizedDescription)
}
}
task.resume()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loginSubmit" {
if let toViewController = segue.destination as? HomeActivity {
print("Checkpoint 2 " + jwt)
toViewController.token = jwt
}
}
}
}
Second View Controller
import UIKit
class HomeActivity: UIViewController {
var token:String!
override func viewDidLoad() {
super.viewDidLoad()
renderInbox()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func renderInbox() {
print("Printing token" + token)
}
}
P.S I have only been doing Swift for less than a week. So any general tips or the like would be welcome. Such as how to pass the jwt to the seque without using a global variable. Or what that is after checkpoint 1 in the output picture.
P.P.S Sorry about the code being in and out of the Stack Overflow code blocks. If you know how to fix it let me know and I'll try
It sounds like you have a single button that triggers both a segue and a URLSession request. Since the request is asynchronous, the segue will always start before the request completes. The button triggers both at the same time, and there is no way that I know of to tell the segue to wait.
If you want the segue to start after the request, you'll need to remove the existing segue from the button (just click it and delete in Interface Builder), create a manual segue (with an identifier), and then present the segue manually using performSegue.
To create a manual segue in Interface Builder, control-drag from the ViewController icon (yellow circle with a white square inside) in the top bar of your source ViewController to anywhere in your destination ViewController.
A menu will pop up and let you select what kind of transition you want. Once it's created, go to the Attributes inspector and set a descriptive identifier.
Now you add code in the URLSession's callback to manually invoke your new segue. Add this line right after "Checkpoint 1":
performSegue(withIdentifier: "Your Identifier Here", sender: self)
Now your segue should happen only after the request is complete. This is also nice because if the request fails or you don't like the response, you can skip performing the segue at all.
No need to use segue here.
In the call back of the task, you can use presentViewController method to present the new view controller (or pushViewController if you have a navigation controller).
I'm trying to make an app with a button which launch a webview.
I've followed many tutorial and read differents topic about the subject but I cant get it working : I'm getting this message when I test my code :
"Cannot call value of non-function type UIWebView!"
Here's the steps I did until now
Adding a button in the principal view Controller
Creating an another view Controller named 'WebViewController'
Adding a segue to link the button to WebViewController
Creating a new Cocoa Touch Class file 'WebViewController'
Setting the WebViewController custom class with the WebViewController class
Adding a webView in the WebViewController ViewController named 'myWebView'
Here's the WebViewController class (in which I got the error when I run the project)
import UIKit
class WebViewController: UIViewController{
#IBOutlet weak var myWebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//define url
let url = NSURL (string: "http://www.my-url.com")
//request
let req = NSURLRequest(url: url as! URL)
//load request into the webview
myWebview(req as URLRequest) //error happens here :
}
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.
}
*/
}
Here's a screenshot (picture talks more than long text, right =)
Thanks !
You can use SFSafariViewController:
import SafariServices
let url = URL(string: "https://www.google.com")
let safariVC: SFSafariViewController = SFSafariViewController(url: url)
self.present(safariVC, animated: true, completion: nil)
I used swift 3 syntax.
That code opens a Safari Web view and you dont need to create segues and view controlles in storyboard.
Try to use:
let url = NSURL (string: "https://google.com")
let request = NSURLRequest(url: url as! URL)
self. myWebView.loadRequest(request as URLRequest)
This code works for me
I am trying to load a pdf using web view using swift. It can load only one page of the pdf, cannot scroll down more than one page. What can i do?
import UIKit
class ViewController: UIViewController,UIWebViewDelegate {
#IBOutlet var webViews: UIWebView!
var path = ""
override func viewDidLoad() {
super.viewDidLoad()
path = NSBundle.mainBundle().pathForResource("ibook", ofType: "pdf")!
let url = NSURL.fileURLWithPath(path)
/*webViews.scalesPageToFit = true
webViews.scrollView.scrollEnabled = true
webViews.userInteractionEnabled = true*/
webViews.delegate = self
self.webViews.loadRequest(NSURLRequest(URL: url!
))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func webViewDidStartLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = true
println("webViewDidStartLoad")
}
func webViewDidFinishLoad(webView : UIWebView) {
//UIApplication.sharedApplication().networkActivityIndicatorVisible = [enter image description here][1]false
webViews.scalesPageToFit = true
webViews.scrollView.scrollEnabled = true
webViews.userInteractionEnabled = true
println("webViewDidFinishLoad")
}
}
I've bumped into the similar problem while trying to display external pdf (not the bundled one), but I suppose you can use the same fix.
In your webViewDidFinishLoad, check if the url is actually a pdf one. Because in my case I know what I'm expecting, I used simple dumb checking. If url links to a pdf, you need to reload the web view to show it correctly and hence be able to scroll.
Here is a bit simplified code in objective C. It should be quite similar in Swift. Try something like this:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
static BOOL isPdfReloaded = NO;
if (!isPdfReloaded && [webView.request.URL.absoluteString containsString:#".pdf"])
{
[webView reload];
isPdfReloaded = YES;
}
else
{
isPdfReloaded = NO;
}
}
The Best solution is to migrate from UIWebView to WkWebView
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.