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
Related
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!
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 need open url programmatically when user click to cell, but not need segue to Safari browser. It is need for statistic on site.
This is need to imitation like post request
I do:
UIApplication.shared.openURL(URL(string: url)!)
But this open Safari browser.
If I'm not mistaking, you want to open the URL in the application it self (without navigating to external -Safari- browser). Well, if that's the case, you should use UIWebView:
You can use the UIWebView class to embed web content in your app. To
do so, create a UIWebView object, attach it to a window, and send it a
request to load web content. You can also use this class to move back
and forward in the history of webpages, and you can even set some web
content properties programmatically.
WebViewController:
I suggest to add a UIWebView into a ViewController (called WebViewController) and present the ViewController when needed; ViewController on storyboard should looks like:
DON'T forget to assign a Storyboard ID for it.
And the ViewController:
class WebViewController: UIViewController {
#IBOutlet weak private var webVIew: UIWebView!
var urlString: String?
override func viewDidLoad() {
if let unwrappedUrlString = urlString {
let urlRequest = URLRequest(url: URL(string: unwrappedUrlString)!)
webVIew.loadRequest(urlRequest)
}
}
#IBAction private func donePressed(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
Usage:
Consider that you want present it when the user taps a button in another ViewController:
class ViewController: UIViewController {
#IBAction private func donePressed(_ sender: Any) {
let stoyrboard = UIStoryboard(name: "Main", bundle: nil)
// withIdentifier: the used storyboard ID:
let webViewController = stoyrboard.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
webViewController.urlString = "https://www.google.com/"
}
}
Or specifically for your case (selecting a cell):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// withIdentifier: the used storyboard ID:
let webViewController = stoyrboard.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
webViewController.urlString = "URL GOES HERE..."
}
Hope this helped.
if your question really means that you have some data on an external website that you need to access, so that you can extract a piece of information from it, then you probably don't need to display the rendered webpage at all.
You can extract the html content of a page like this
var text = ""
let url = URL(string: "https://www.bbc.co.uk")
do
{
text = try String(contentsOf: url!, encoding: String.Encoding.utf8)
}
catch
{
print("error \(error.localizedDescription)")
}
if !text.isEmpty
{
parseThisPageSomehow(text)
}
How you parse the text depends on what you need to get out of it, but this approach will give you the data you need.
If you use http rather than https, you will run into the well-documented transport security issue, which will force you to either set up a temporary override for http, or start using https
Transport security has blocked a cleartext HTTP
When you open url that contain PDF file safari ask you if you want to open it on safari or in iBook.
I want to do the same thing ,
in my project i had a collection view contains videos and photos,
i want the user to chose if he want to open the file on the app or to open it with other media player.
For loading into your own app it depends on which class you're using to display content on the exact code you'd use but for opening in another app you'd normally use a share button. Here is example code that will work if you wire up the #IBAction and #IBOutlet to the same bar button in your UI (and place a file at the fileURL that you specify):
import UIKit
class ViewController: UIViewController {
// UIDocumentInteractionController instance is a class property
var docController:UIDocumentInteractionController!
#IBOutlet weak var shareButton: UIBarButtonItem!
// called when bar button item is pressed
#IBAction func shareDoc(sender: AnyObject) {
// present UIDocumentInteractionController
if let barButton = sender as? UIBarButtonItem {
docController.presentOptionsMenuFromBarButtonItem(barButton, animated: true)
}
else {
print("Wrong button type, check that it is a UIBarButton")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// retrieve URL to file in main bundle
if let fileURL = NSBundle.mainBundle().URLForResource("MyImage", withExtension: "jpg") {
// Instantiate the interaction controller
self.docController = UIDocumentInteractionController(URL: fileURL)
}
else {
shareButton.enabled = false
print("File missing! Button has been disabled")
}
}
}
Notes
A UIDocumentInteractionController is used to enable the sharing of documents between your app and other apps installed on a user's device. It is simple to set up as long as you remember three rules:
Always make the UIDocumentInteractionController instance a class
(type) property. If you only retain a reference to the controller
for the life of the method that is triggered by the button press
your app will crash.
Configure the UIDocumentInteractionController
before the button calling the method is pressed so that there is not
a wait in which the app is waiting for the popover to appear. This is important because while
the presentation of the controller happens asynchronously, the instantiation does not. And you may find that there is a noticeable delay to open the popover if
you throw all the code for instantiation and presentation inside a
single method called on the press of a button. (When testing you might see a delay anyway because the share button is likely going to be pressed almost straightaway but in real world use there should be more time for the controller to prepare itself and so the possibility of lag is less likely.)
The third rule is that you must test this on a real device not in the simulator.
More can be found in my blogpost on the subject.
Edit: Using a UIActivityViewController
Code for using UIActivityViewController instead of UIDocumentInteractionController
import UIKit
class ViewController: UIViewController {
// UIDocumentInteractionController instance is a class property
var activityController: UIActivityViewController!
#IBOutlet weak var shareButton: UIBarButtonItem!
// called when bar button item is pressed
#IBAction func shareStuff(sender: AnyObject) {
if let barButton = sender as? UIBarButtonItem {
self.presentViewController(activityController, animated: true, completion: nil)
let presCon = activityController.popoverPresentationController
presCon?.barButtonItem = barButton
}
else {
print("not a bar button!")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// retrieve URL to file in main bundle
if let img = UIImage(named:"MyImage.jpg") {
// Instantiate the interaction controller
activityController = UIActivityViewController(activityItems: [img], applicationActivities: nil)
}
else {
shareButton.enabled = false
print("file missing!")
}
}
}
You can also add custom activities to the UIActivityViewController and here is code for adding an "Open In..." button to a UIActivityViewController so that you can switch to a UIDocumentInteractionController from a UIActivityViewController.
I did the same code for saving a PDF file from a URL (whether it's a local URL in your device storage, or it's a URL from somewhere on the internet)
Here is the Code for Swift 3 :
#IBOutlet weak var pdfWebView: UIWebView!
#IBOutlet weak var shareBtnItem: UIBarButtonItem!
var pdfURL : URL!
var docController : UIDocumentInteractionController!
then in viewDidLoad()
// retrieve URL to file in main bundle`
let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("YOUR_FILE_NAME.pdf")
//Instantiate the interaction controller`
self.docController = UIDocumentInteractionController(url: fileURL)`
and in your barButtonItem tapped method (which I have called openIn(sender)):
#IBAction func openIn(_ sender: UIBarButtonItem)
{
// present UIDocumentInteractionController`
docController.presentOptionsMenu(from: sender, animated: true)
}
FYI: You need a webView in your storyboard if you wish to show the pdf file as well
Hope this helps.
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.