WebView insert local image to html couldn't success? - html

I have a WkWebView in my ViewController, I used webview load local html. The code is this:
guard let path = Bundle.main.url(forResource: "editor", withExtension: "html") else {
return
}
webView.load(URLRequest(url: path))
1: I choose a image from photoLibrary, then I saved it to document's directory .
private func fileName() -> String {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd#HH-mm-ss"
return "/" + formatter.string(from: date) + ".png"
}
func saveImage(_ image: UIImage) {
guard let imageData = UIImageJPEGRepresentation(image, 1.0) else {
return
}
let directory = NSHomeDirectory().appending("/Documents")
let path = directory.appending(fileName())
let fileManager = FileManager.default
do {
try fileManager.createDirectory(atPath: directory, withIntermediateDirectories: true, attributes: nil)
fileManager.createFile(atPath: path, contents: imageData, attributes: nil)
} catch {
SHLog("saveImage: \(error)")
}
}
2: I fetched the image path then I used the method of the webView to insert the image.
func evaluateJs(_ imagePath: String) {
webView.evaluateJavaScript("insertImage('\(imagePath)')") { (response, error) in
}
}
insertImage() is a method from the local js file, it can insert the image to html.
If I used the local imagePath, I couldn't insert image to html. But When I used the real url, it can work. I don't know the reason, anyone could help me. Thanks.

Your issue is likely caused by WKWebViews security policies not allowing you access to the local folders of the application.
Check out the documentation for WKWebView loadFileURL. You should be able to pass either the documents directory or full image URL in the allowingReadAccessTo parameter which should let you access the images you want.
If you have a web url of the image, I would prefer to use the web URL anyway rather than trying to store and load the image locally. Your not using up storage in your documents directory and WKWebView is designed to load content from the web.

In my case, assign baseURL your image file's parent folder firstly. Then insert the whole URL path in your html likes following.
<img src="/var/mobile/Containers/Data/Application/C217F3BD-1A1B-438A-A436-D878411C7849/Documents/48C4B007-A950-46D2-A719-E025482710A0/images/79042B1F-E11C-42D1-8B49-DD3A0016F421.jpg"

Related

Is there a possibility to get the image from internet without calling API in swift?

I got the (data , response , error) already in completion handler from the first call to the api. And have to get the image from the links nested in the json object(which is parsed from the obtained data) . Do I need to create a separate method which will again call the api using the nested link of the json or is there any short hand (frame work) methods to do that. like ,
let image = UIImage(data: imageData) // default method in framework
let image = UIImage(contentsOfFile : fileLocation) // default method in framework
let image = UIImage(url: URL) // expecting a method like this
I'm expecting a method like the above one which gets the image directly from the url without having call to the api repeatedly to get the images from the nested links. Is that possible, so that I can avoid multiple api calls and minimise threads?
Since the intoduction of the Combine framework it has become pretty comfortable to nest network calls.
This is an example with the jsonplaceholder API. The code first loads JSON containing an id and an url, then it loads the image at this URL
import Combine
struct Photo : Decodable, Identifiable {
let id : Int
let url : URL
}
var storage : AnyCancellable?
let baseURL = URL(string: "https://jsonplaceholder.typicode.com/photos/1")!
storage = URLSession.shared.dataTaskPublisher(for: baseURL)
.map(\.data)
.decode(type: Photo.self, decoder: JSONDecoder())
.map(\.url)
.flatMap { url in
URLSession.shared.dataTaskPublisher(for: url)
.map(\.data)
.replaceError(with: Data())
.eraseToAnyPublisher()
}
.receive(on: DispatchQueue.main)
.sink { result in
print(result)
} receiveValue: { imageData in
print("Image successfully loaded containing", imageData)
let image = UIImage(data: imageData)
}
you can display image from url as well without api call. to achieve this you need to convert url to Data
let imageURL = "https://picsum.photos/200/300"
let imageData = Data(contentsOf: URL(string: imageURL)!)
let finalImage = UIImage(data: imageData)
extension UIImageView {
func load(imageURL: URL) {
DispatchQueue.global().async { [weak self] in
let imageData = Data(contentsOf: imageURL)
let finalImage = UIImage(data: imageData)
DispatchQueue.main.async {
//set your image here in main thread
}
}
}
}
please try it and check if it works for you

swift 4 error: load html string to webview

I tried to load a html string in my webview with the code :
let htmlFile = Bundle.main.path(forResource: "code", ofType: "html")
let html = try! String(contentsOfFile: htmlFile!, encoding: String.Encoding.utf8)
self.weview.loadHTMLString(html, baseURL: nil)
I run the app, everything works fine, but if i press on a website link, i get the error in my AppDelegate: Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)
Can someone give me a code for swift 4, where I can load a html string and interact with it? Would be really nice.
First thing to check - go to Project Settings -> Build Phases -> Copy Bundle Resources to make sure your directory with your html-files is on the list there.
Second, load the file the way listed below. You don't have to load the content of the file.
let path = Bundle.main.path(forResource: "code", ofType: "html")!
let uri = URL(string: path)!
let request = URLRequest(url: uri)
webView.loadRequest(request)
Also, make sure forResource param contains full path to the html file starting from your project directory
Try This :
let link = "https://google.co.in"
do {
let htmlStr = try String(contentsOf : URL(string:link)!)
let separateHtmlStr = htmlStr.components(separatedBy:"") // any html tag you want to interact with
let newURL = URL(string: link)
yourWebView.loadHTMLString(separateHtmlStr, baseURL: newURL) // in case if you have changed anything
}catch{
print("Exception")
}
}

iOS Swift Syntax for Mathjax

I wish to use MathJax to display a formatted math equation in a Web View. I've seen various discussions on this topic, but they are mostly in Objective-C (I'm using Swift).
So far, I've dragged and dropped the "MathJax-master" folder into my Xcode project (i want the MathJax engine stored locally so no internet connection will be required for the app to work). I next want to display ...maybe: "Hello world, x = y^2" in the Web View.
I found the following tutorial, but (again) I think it's in Objective-C, and I'm having trouble translating it into Swift syntax: http://new2objectivec.blogspot.com.au/2012/02/tutorial-how-to-setup-mathjax-locally.html
If anyone could please point me to the correct Swift syntax, it would be very much appreciated.
Following code worked for me:
//Function below concatenates an HTML file String, stores it locally, then calls it to display in a UIWebView using NSURLRequest with MathJax typesetting:
func formatWebViewText(){
//Specify the text that will go in the HTML file:
let htmlString: NSString = "<head><script type=\"text/x-mathjax-config\">MathJax.Hub.Config({tex2jax: {inlineMath: [ ['$','$'] ],},\"HTML-CSS\": { linebreaks: { automatic: true, width: \"container\" } } } )</script><script src='" +
String(NSBundle.mainBundle().pathForResource("MathJax", ofType: "js", inDirectory: "MathJax-masterSlim")!) + //reference to local MathJax files
"?config=TeX-AMS-MML_HTMLorMML'></script></head>" +
"<body>" + questionMessage + "</body></html>" // "questionMessage" contains the text to be typeset/processed by MathJax
//Give HTML file a name:
let htmlFileName: NSString = "htmlTempFile.html"
//Create a reference to the temporary directory that will store the HTML file:
let tempDirectory: NSString = NSTemporaryDirectory()
//Append reference temporary directory to include HTML file name:
let htmlFilePath: NSString = tempDirectory.stringByAppendingPathComponent(htmlFileName as String)
//Convert appended temporary directory NSString to an NSURL object:
htmlUrl = NSURL(fileURLWithPath: htmlFilePath as String)
//Below, HTML string is written to a file:
do { try htmlString.writeToFile(htmlFilePath as String, atomically: true, encoding: NSUTF8StringEncoding) }
catch { }
//HTML file URL is fetched and displayed in UIWebView:
QuestionWebView.loadRequest(NSURLRequest(URL: htmlUrl!))
}
Swift 3
Here's your answer updated to Swift 3 and using a WKWebView (available in WebKit.framework and currently recommended by Apple over UIWebView and WebView):
guard let path = Bundle.main.path(forResource: "MathJax", ofType: "js", inDirectory: "MathJax-master") else { return }
let htmlString = "<head><script type=\"text/x-mathjax-config\">MathJax.Hub.Config({tex2jax: {inlineMath: [ ['$','$'] ],},\"HTML-CSS\": { linebreaks: { automatic: true, width: \"container\" } } } )</script><script src='" + path + //reference to local MathJax files
"?config=TeX-AMS-MML_HTMLorMML'></script></head>" +
"<body>" + enteredLatex + "</body></html>"
let filePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("tempHTML.html")
do {
try htmlString.write(toFile: filePath.path, atomically: true, encoding: String.Encoding.utf8)
} catch { return }
webView.load(URLRequest(url: filePath))

Correct way to place and handle .json file in Xcode

I just started to learn Swift and xcode and the first problem that I'm facing is how and where should I place the json file ? And how to use those files? Should I place the .json files within Assets folder ? Since I find it difficult, I would love to hear some tips or examples from you !
You can add an empty file, select syntax coloring as JSON and paste your json text. Even if it is not formatted, you can format it by selecting all the text and pressing Ctrl + I.
How I've done this in September 2019...
1) In Xcode, create an Empty file. Give the file a .json suffix
2) Type in or paste in your JSON
3) Click Editor -> Syntax Coloring -> JSON
4) Inside the file, highlight the JSON, click ctrl + i to indent
5) import SwiftyJSON using Cocoapods
6) In your ViewController, write...
guard let path = Bundle.main.path(forResource: "File", ofType: "json") else { return }
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let json = try JSON(data: data)
} catch {
print(error)
}
N.B. - "File" is the name of the file you created, but excluding the .json suffix
See SwiftyJSON GitHub page for more info - https://github.com/SwiftyJSON/SwiftyJSON
Please review the below image to check where to place the file.
I suggest you to create a group and add the file in that.
After that, review the below could for using that file.
Edit: This is the updated code for Swift 5 if that helps anyone.
let path = Bundle.main.path(forResource: "filename", ofType: "json")
let jsonData = try? NSData(contentsOfFile: path!, options: NSData.ReadingOptions.mappedIfSafe)
var location = "test"
var fileType = "json"
if let path = Bundle.main.path(forResource: location, ofType: fileType) {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = JSON(data: data)
if jsonObj != JSON.null {
}
} catch let error {
print(error.localizedDescription)
}}
As per your requirement, you want to read json from that json file.
I am using SWIFTY JSON Library for that.
Find below the link for that
https://github.com/SwiftyJSON/SwiftyJSON
Add this library to your project.
After adding it, now review the below code:-
let json = JSON(data: jsonData!)
for (index, subjson): (String, JSON) in json{
let value = subjson["key"].stringValue
}

Multiple filters for Safari content blocking Swift

I'm building a simple content-blockin app.
It works, but i want to apply filters (which webs to block and which not) with UISwitches (saved to NSUserDefaults).
Because the content blocking extension uses json it's unclear to me how can i select multiple json files to function simultaneously.
Any ideas how it can be achieved? Multiple extensions? Combining and splitting json files somehow?
I have been in same situation. Answer to this is bit tricky, so bear with me. You cannot write to file in bundle i.e blockerList.json is not writeable. Here is what you need to do,
Enable App groups from TARGETS->YOUR MAIN APP -> Capabilities -> App Groups. And add unique identifier for app groups. Do same with extension. (Use same identifier as group name which you entered for main app)
Create a file in Container directory.
Write rules (json) to that file.
Reload extension once you have written rules.
Read rules from Container directory in content blocker extension.
From your main app create file and write json rules into that file as:
let jsonData = try! JSONSerialization.data(withJSONObject: webFilters, options: JSONSerialization.WritingOptions.prettyPrinted)
//Convert back to string. Usually only do this for debugging
if let JSONString = String(data: jsonData, encoding: String.Encoding.utf8) {
let file = "conbo.json"
if let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "YOUR_GROUP_IDENTIFIER") {
let path = dir.appendingPathComponent(file)
do {
try JSONString.write(to: path, atomically: false, encoding: String.Encoding.utf8)
let id = "YOUR_CONTENT_BLOCKER_BUNDLE_IDENTIFIER"
SFContentBlockerManager.reloadContentBlocker(withIdentifier: id) {error in
guard error == nil else {
print(error ?? "Error")
return
}
print("Reloaded")
}
}
catch {
}
}
}
Now in extension read file from container as:
class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
let file = "conbo.json"
if let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "YOUR_APP_GROUP_IDENTIFIER") {
let path = dir.appendingPathComponent(file)
do {
do {
let attachment = NSItemProvider(contentsOf: path)!
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequest(returningItems: [item], completionHandler: nil)
}
}
}
}
}