Swift 3 webView local HTML - ios

I need to load local HTML, CSS and JS into an iOS app to make my boss happy. I'm using Xcode 8.3.1 and Swift 3. I have created a new project with a WebView placed in my Main.storyboard
Other StackOverflow resources helped me get this far:
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let urlpath = Bundle.main.path(forResource: "index", ofType: "html");
let requesturl = URL(string: urlpath!)
let request = URLRequest(url: requesturl!)
webView.mainFrame.load(request)
}
I get this error:
Value of type 'UIWebView' has no member 'mainFrame'
When I remove mainFrame it says I'm missing a mimeType. Can anyone help? I'm obviously a noob to Swift.

mainFrame is from the class WebView which is only for macOS.
Since you are using UIWebView, you need to use webView.loadRequest.
You also have a problem with your URL. Either use URL(fileURLWithPath:) to create the URL from the path string or, better yet, use Bundle.main.url(forResource:withExtension:) and get the URL directly.
You might also want to use WKWebView instead of UIWebView. It has a lot more features and it may be more useful for your needs.

Swift 5
let path = Bundle.main.path(forResource: "temp", ofType: "html")!
//reading
var text = try! String.init(contentsOfFile: path, encoding: String.Encoding.utf8)
text = text.replacingOccurrences(of: "${contents}", with: contents)
// load string
let bundleURL = URL.init(string: path)
webView.loadHTMLString(text, baseURL: bundleURL)
done!

Related

Text file to array in swift

I'm trying to create a dictionary app on IOS. I have a text file (words_alpha.txt) in my Bundle, and I want to read all the words/lines, and place them into an arrray. String = ["word1", "word2", "word3"]. Here is my current code I got from bit.ly/39IC642
let path = Bundle.main.path(forResource: "words_alpha", ofType: "txt") // file path for file "words_alpha.txt"
let string = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)
I am getting this error: Cannot use instance member 'path' within property initializer; property initializers run before 'self' is available
I am fairly new to using Swift and coding in general, please be detailed with your answers. Thank you!
If you are writing an iOS app, you can move such initialization into viewDidLoad():
var wordArray: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
//...
let url = Bundle.main.url(forResource: "words_alpha", withExtension: "txt")! // file URL for file "words_alpha.txt"
do {
let string = try String(contentsOf: url, encoding: .utf8)
wordArray = string.components(separatedBy: CharacterSet.newlines)
} catch {
print(error)
}
}
If your words_alpha.txt does contain multiple words per line, you may need some other way.

WKWebView does load resources from local document folder

In my Swift iOS app, I want to download some dynamic HTML pages from a remote server, save them in the document directory, and display those pages from document directory.
I was using this to load the page:
var appWebView:WKWebView?
...
appWebView!.loadRequest(NSURLRequest(URL: NSURL(fileURLWithPath: htmlPath)))
Everything works on the simulator, but when I moved to real phones, it just showed a blank page. I then connected to the app using Safari, and found it complained with "Failed to load resource".
I then tried to first read the content of the page at htmlPath, then use
appWebView!.loadHTMLString()
to load the page. It works when the HTML page is simple. But if the HTML references something else, i.e. a JavaScript file also in the document directory (with an absolute path like <script src="file:////var/mobile/Containers/Data/Application/762035C9-2BF2-4CDD-B5B1-574A0E2B0728/Documents/xxxxx.js">), it will fail to load.
Does anyone know why this happens, and how to resolve the issue?
More info:
XCode version: 7.3.1
Deployment Target: 8.1 (I tried to use 9.3 too, but that didn't help.)
This is a simplified version of what I have used to load local files in a project of mine (iOS 10, Swift 3). I have just updated my code (7.5.2017) after testing it out again on iOS 10.3.1 and iPhone 7+ as requested by Raghuram and Fox5150 in the comments.
I just created a completely new project and this is the folder structure:
Update 19.04.2018: Added a new feature to download a .zip with HTML, CSS, JS files, unzip it in /Documents/ (Alamofire + Zip) and then load those files into the webView. You can find it in the GitHub sample project as well. Again, feel free to fork & star! :)
Update 08.02.2018: finally added a GitHub sample project, which also includes a local JavaScript file. Feel free to fork & star! :)
Version 1 with webView.loadFileURL()
ViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let webView = WKWebView()
let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
let htmlUrl = URL(fileURLWithPath: htmlPath!, isDirectory: false)
webView.loadFileURL(htmlUrl, allowingReadAccessTo: htmlUrl)
webView.navigationDelegate = self
view = webView
}
}
Version 2 with webView.loadHTMLString()
ViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let webView = WKWebView()
let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
let folderPath = Bundle.main.bundlePath
let baseUrl = URL(fileURLWithPath: folderPath, isDirectory: true)
do {
let htmlString = try NSString(contentsOfFile: htmlPath!, encoding: String.Encoding.utf8.rawValue)
webView.loadHTMLString(htmlString as String, baseURL: baseUrl)
} catch {
// catch error
}
webView.navigationDelegate = self
view = webView
}
}
Gotchas to look out for:
Make sure that your local html/js/css files are in Project -> Target -> Build Phases -> Copy Bundle Resources
Make sure that your html files don't reference relative paths e.g. css/styles.css because iOS will flatten your file structure and styles.css will be on the same level as index.html so write <link rel="stylesheet" type="text/css" href="styles.css"> instead
Given the 2 versions and the gotchas here are my html/css files from the project:
web/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Offline WebKit</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<h1 id="webkit-h1">Offline WebKit!</h1>
</body>
</html>
web/css/styles.css
#webkit-h1 {
font-size: 80px;
color: lightblue;
}
If somebody wants a GitHub sample project, tell me in the comments section and I'll upload it.
Swift 4 Method
This method allows WKWebView to properly read your hierarchy of directories and sub-directories for linked CSS/JS files. You do NOT need to change your HTML, CSS or JS code.
Updated for Xcode 9.3
Step 1
Import the folder of local web files anywhere into your project. Make sure that you:
☑️ Copy items if needed
☑️ Create folder references (not "Create groups")
☑️ Add to targets
Step 2
Go to the View Controller with the WKWebView and add the following code to the viewDidLoad method:
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
index – the name of the file to load (without the .html extension)
website – the name of your web folder (index.html should be at the root of this directory)
Conclusion
The overall code should look something like this:
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.uiDelegate = self
webView.navigationDelegate = self
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "Website")!
webView.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webView.load(request)
}
}
If any of you have further questions about this method or the code, I'll do my best to answer. :)
This solution helped me:
[configuration.preferences setValue:#YES forKey:#"allowFileAccessFromFileURLs"];
This works well (Swift 3, Xcode 8):
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
if let url = Bundle.main.url(forResource: "file", withExtension: "txt")
{
do
{
let contents = try String(contentsOfFile: url.path)
webView.loadHTMLString(contents, baseURL: url.deletingLastPathComponent())
}
catch
{
print("Could not load the HTML string.")
}
}
}
}
This works nicely with file URL or remote URL, and whether file is in the bundle or in documents:
if url.isFileURL {
webView.loadFileURL(url, allowingReadAccessTo: url)
} else {
let request = URLRequest(url: url)
webView.load(request)
}
Constructing the URLs this way allowed me to load resources from the document directory with WKWebView:
guard let docDir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) else {
return
}
let resourceURL = docDir.appendingPathComponent("/Path/To/Your/Resource")
self.wkWebView.loadFileURL(resourceURL, allowingReadAccessTo: docDir)
The files must be in the document directory.
I implemented the following to retrieve a document:
let documentDirUrl = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileNameWithExtension = "IMG_0002.PNG"
let indexFileUrl = documentDirUrl.appendingPathComponent(fileNameWithExtension)
if FileManager.default.fileExists(atPath: indexFileUrl.path) {
webView.loadFileURL(indexFileUrl, allowingReadAccessTo: documentDirUrl)
}
Check that ticket: iOS: How to load local files (not in the bundle) in a WKWebView?
var nsurl = URL(fileURLWithPath: URL(fileURLWithPath: URL(fileURLWithPath: documentsDirectory()).appendingPathComponent(user_appli).absoluteString).appendingPathComponent("index.html").absoluteString) //locally
var readAccessToURL: URL? = nsurl.deletingLastPathComponent?.deletingLastPathComponent
if let anURL = readAccessToURL {
webView?.loadFileURL(nsurl, allowingReadAccessTo: anURL)
}

Swift 3 WKWebView 'URLRequest'; did you mean to use 'as' to explicitly convert? ( BUG )

Hello when I use WKWebView codes with Swift 3 gives me this error
'URLRequest'; did you mean to use 'as' to explicitly convert?
I think this is bug I need help or ideas ? My codes under below
Thanks
import UIKit
import WebKit
class SocialsViewController: UIViewController, WKNavigationDelegate {
var webView = WKWebView()
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "https://facebook.com")!
webView.loadRequest(NSURLRequest(url: url))
webView.allowsBackForwardNavigationGestures = true
}
}
Use URL and URLRequest instead:
let url = URL(string: "https://facebook.com")!
webView.load(URLRequest(url: url))
This is quite similar to https://stackoverflow.com/a/37812485/2227743: you either use NSURL and have to downcast it as URL, or you directly use the new Swift 3 structs.
If you follow the error message, your example would become:
let url = NSURL(string: "https://facebook.com")!
webView.load(URLRequest(url: url as URL))
It could be even worse:
let url = NSURL(string: "https://facebook.com")!
webView.load(NSURLRequest(url: url as URL) as URLRequest)
All this works but of course it's much better to start using URL and URLRequest in Swift 3, without using all this downcasting.
Try this one
let myUrl = URL(string: "https://facebook.com")!
webView.loadRequest(URLRequest(url:myUrl));

Why does swift not recognise my resource?

Swift is telling me my file does not exist and unfortunately I cannot find the directory myself. Am I using the URL for resource method incorrectly or do I just need to find a way of removing the optional brackets? When I refer to this object directly it works but I need to find a way of creating a path my team mates can use as well.
override func setUp()
{
super.setUp()
let bundleURL = NSBundle.mainBundle().URLForResource("meetingexample", withExtension: "ics")
let eventsFile = NSURL(fileURLWithPath: ("\(bundleURL)"))
//let eventsFile = NSURL(fileURLWithPath: docFolderURL.URLByAppendingPathComponent("/meetingexample.ics"))
content = try! String(contentsOfURL: eventsFile, encoding: NSUTF8StringEncoding)
}
Print of bundleURL:
Optional(file:/Users/GB112922/Library/Developer/CoreSimulator/Devices/EF6A2594-6B31-4E38-B46D-2F3AAAF25210/data/Containers/Bundle/Application/35E04003-072F-476E-957E-98C70B792539/CallIn.app/meetingexample.ics)`
You have two issues:
"\(bundleURL)" is wrapping the value of the NSURL in the Optional(...) extra text.
URLForResource is already giving you an NSURL. There's no need to create another NSURL from it.
Just use bundleURL. There is no need for the eventsFile variable.
override func setUp()
{
super.setUp()
let bundleURL = NSBundle.mainBundle().URLForResource("meetingexample", withExtension: "ics")
content = try! String(contentsOfURL: bundleURL, encoding: NSUTF8StringEncoding)
}

Get metadata from mp3 file with URL (swift)

I'm new in swift and i would like to get data from a mp3 file located on a server, ( i used an URL, exemple : http://myserver.eu/file.mp3 ).
i used some tuto, like http://geekyviney.blogspot.com/2015/02/extracting-data-like-thumbnail-images.html ( it's working with a local file and i'm trying to make it work with a file from an url)
i got a exception :
Thread 1: EXC_BAD_INSTRUCTION(code=EXC_I1386_INVOP, subcode=0x0
fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)
I suppose that pathForResource is only for local file ? thanks for your help
var filePath = NSBundle.mainBundle().pathForResource("http://servername.eu/filename", ofType: "mp3")
var fileUrl = NSURL(fileURLWithPath: filePath!)
var asset = AVAsset.assetWithURL(fileUrl) as AVAsset
You're right, pathForResource is used to access files locally in the application bundle.
To load data from URLs with http scheme use this syntax
let urlString = "http://servername/filename"
let serverURL = NSURL(string: urlString)!
if let asset = AVAsset.assetWithURL(serverURL) as? AVAsset {
// do something with the asset
}
Swift 3 version:
let urlString = "http://servername/filename"
if let serverURL = URL(string: urlString) {
let asset = AVAsset(url: serverURL)
// do something with the asset
}
Ow and don't forget to add the correct import
import AVFoundation

Resources