Bad URL in swift 2 - swift

When I want to load a url it goes always into the catch block. How can i solve this? I think it's something with the escaped url.
If i set url in the if block it goes immediately to the else block.
let urls : String = Baseurl+"lat="+latitude+"&lon="+longitude+"&APPID={"+apiKey+"}"
let escapedurl = urls.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
let url: NSURL = NSURL(string: escapedurl!)!
if let url = NSURL(string: escapedurl!) {
do {
let contents = try NSString(contentsOfURL: url, usedEncoding: nil)
print(contents)
} catch {
// contents could not be loaded
print("bad")
}
} else {
// the URL was bad!
print("bads")
}

URLHostAllowedCharacterSet() returns the character set for characters allowed in a host URL subcomponent.
For example, in the URL http://www.example.com/index.html, the host component is www.example.com.
Try to change
let escapedurl = urls.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
into
let escapedurl = urls.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())

Related

Cannot write Data to file in Swift

I have an image that I want it's data to be saved in this fileUri generated by ("react-native-fs"). LibraryDirectoryPath/saved_images/{filename}:
/Users/macbookpro/Library/Developer/CoreSimulator/Devices/9CBD2F1E-7330-418D-81BE-108C064DEA7E/data/Containers/Data/Application/C26348CC-3463-43EF-9B26-B7E31641E2EA/Library/saved_images/6B3A6A3A-8DE3-488B-AF43-A54775545B38.jpg
And below is my implementation:
do {
let url = URL(string: fileUri)
let fileExisted = FileManager().fileExists(atPath: url!.path)
if (fileExisted) {
try decryptedData.write(to: url!)
} else {
let handle = try FileHandle(forWritingTo: url!)
handle.write(data) // data is type Data
handle.closeFile()
}
} catch {
reject("FileError", "Failed to write file", error)
}
I also tried let url = URL(fileURLWithPath: fileUri) with and without file:// prepending to fileUri
do {
let url = URL(fileURLWithPath: fileUri)
let fileExisted = FileManager().fileExists(atPath: url.path)
if (fileExisted) {
try decryptedData.write(to: url)
} else {
let handle = try FileHandle(forWritingTo: url)
handle.write(data)
handle.closeFile()
}
} catch {
reject("FileError", "Failed to write file " + error.localizedDescription, error)
}
it says:
You are using the wrong API.
let url = URL(string: fileUri)
is for strings representing a full – even encoded - URL starting with a scheme like file:// or https://.
On the other hand fileUri is actually a path without a scheme, so you have to use
let url = URL(fileURLWithPath: fileUri)
This returns a non optional URL by adding the file:// scheme.
fileUri should be renamed as filePath.

Swift URL returns nil when the url contains an internationalized domain name (IDN)

I was wondering why this piece of code always exit with 1:
import Foundation
// an idn domain:
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
let urlStr = "https://" + uLabel
guard let url = URL(string: urlStr) else { exit(1) }
exit(0)
Since Apple's browser Safari does support well IDN domains, I was surprised their URL library does not... I tried to urlencode the string beforehand, but it is not helping.
======EDIT======
After fixing the piece above upon Matt's suggestion, I faced another problem during fetching the website data:
import Foundation
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
let scheme = "https"
var comps = URLComponents()
comps.scheme = scheme
comps.host = uLabel
guard let url = comps.url else { exit(1) }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let rawContent = data else { exit(1) }
guard let content = String(data: rawContent, encoding: String.Encoding.utf8) else { exit(1) }
if content.contains("UASG Testbed Landing Page") {
// successfully fetch content of the page
exit(0)
} else {
// error during fetching
exit(1)
}
}
task.resume()
RunLoop.main.run()
The program still exits with 1. It seems the domain is not converted to an A-LABEL as it is on Safari, as the error suggests (the certificate is valid, the error is misleading):
NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “համընդհանուր-ընկալում-թեստ.հայ” which could put your confidential information at risk., NSErrorFailingURLKey=https://%d5%b0%d5%a1%d5%b4%d5%a8%d5%b6%d5%a4%d5%b0%d5%a1%d5%b6%d5%b8%d6%82%d6%80-%d5%a8%d5%b6%d5%af%d5%a1%d5%ac%d5%b8%d6%82%d5%b4-%d5%a9%d5%a5%d5%bd%d5%bf.%d5%b0%d5%a1%d5%b5/,
I don't know why you're having trouble, but rule number one is never never never call URL(string). Use URLComponents. That's what it's for.
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
let scheme = "https"
var comps = URLComponents()
comps.scheme = scheme
comps.host = uLabel
let url = comps.url // works for me
Until, someone can find a way to tell the URL framework to correctly represent the URL "https://համընդհանուր-ընկալում-թեստ.հայ", it is possible to translate the domain part to an A-LABEL before passing it to the URL constructor and elude its wrong internal representation:
import Foundation
import IDNA
// an idn domain:
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
guard let aLabel = uLabel.idnaEncoded else { exit(1) }
let supportedUrl = "https://" + aLabel
guard let url = URL(string: supportedUrl) else { exit(1) }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let rawContent = data else { exit(1) }
guard let content = String(data: rawContent, encoding: String.Encoding.utf8) else { exit(1) }
if content.contains("UASG Testbed Landing Page") {
// successfully fetch content of the page
exit(0)
} else {
// error during fetching
exit(1)
}
}
task.resume()
RunLoop.main.run()
This piece of code does exit 0. The IDNA library is there (ensure you take the master branch, because released versions are still on IDNA2003):
https://github.com/Wevah/IDNA-Cocoa

Master Detail app in Swift - how to convert string into URL

I'm new here, so please bear with me. I have a Master-Detail app, and in the DetailViewController.swift file, here's the configureView func where it calls a web view to open a web page.
It's complaining on the let request = URLRequest(url:url) line because url variable is defined as a string. I've tried everything but it won't work.
By the way, MasterViewController.MyVariables.urlString is an array of strings.
func configureView() {
// Update the user interface for the detail item.
if let detail: AnyObject = detailItem {
if let myWebview = webView {
let url = MasterViewController.MyVariables.urlString
let request = URLRequest(url: url)
myWebview.scalesPageToFit = true
myWebview.loadRequest(request)
}
}
}
You can either pass an URL object or create an URL object from the string that you´re passing. Anyhow you need to convert your string into an URL.
if let url = URL(string: MasterViewController.MyVariables.urlString) {
// use url in here
}
Update:
Use this for your example:
if let url = URL(string: MasterViewController.MyVariables.urlString) {
// use url in here
let request = URLRequest(url: url)
}
Update 2:
You have your Struct which is an array of strings. Then you need to do this to get the value you want:
struct MyVariables {
static var urlString: [String]? = ["something"]
}
if let str = MyVariables.urlString?.first, let url = URL(string: str) {
// use url in here
let request = URLRequest(url: url)
print(url)
}
Right now I´m using MyVariables.urlString?.first, in the future if you want another index then you need to get that instead.
This is what I did to make it work:
let stringRepresentation = MasterViewController.MyVariables.urlString?.joined(separator:"")
print ("urlString", MasterViewController.MyVariables.urlString)
print ("sR",stringRepresentation)
let url = NSURL(string: stringRepresentation as! String)
let request = URLRequest(url: url! as URL)
myWebview.scalesPageToFit = true
myWebview.loadRequest(request)

swift NSURL gets back an "unable to read data" message even with https://

I am trying to learn iOS following a course and they ask to do the following:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//var string1 = "http://www.google.com"
//var string1 = "https://www.weather-forecast.com/locations/San-Antonio/forecasts/latest"
//var url = NSURL(string: string1)
var url = NSURL(string: "https://google.com")
print(url)
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
var urlError = false
if error == nil {
var urlContent = NSString(data: data!, encoding: NSUTF8StringEncoding)
print(urlContent)
} else {
urlError = true
}
if urlError == true {
self.showError()
}
})
task.resume()
} else {
showError()
}
}
the app doesn't show any web page content and when debugging I find that the object for the url says that it is "unable to read data"
I have tried with http and https. I have tried with different web sites.
I have tried the address in the safari of the simulator and it loads.
Can someone tell me why is this not working
Thanks in advance.
gariva
You're using wrong encoding. The webpage you're trying to fetch (http://www.google.com/) uses ISO-8859-1.
I was able to reproduce your issue. Fetch worked when I changed encoding. Try this:
var urlContent = NSString(data: data!, encoding: NSISOLatin1StringEncoding)
For display web page you should use UIWebView element. Something like this:
let url = NSURL(string: "https://google.com")
let webView = UIWebView(frame: self.view.frame)
self.view.addSubview(webView)
webView.loadRequest(NSURLRequest(URL: url!))

Converting URL to String and back again

So I have converted an NSURL to a String.
So if I println it looks like file:///Users/... etc.
Later I want this back as an NSURL so I try and convert it back as seen below, but I lose two of the forward slashes that appear in the string version above, that in turn breaks the code as the url is invalid.
Why is my conversion back to NSURL removing two forward slashes from the String I give it, and how can I convert back to the NSURL containing three forward slashes?
var urlstring: String = recordingsDictionaryArray[selectedRow]["path"] as String
println("the url string = \(urlstring)")
// looks like file:///Users/........etc
var url = NSURL.fileURLWithPath(urlstring)
println("the url = \(url!)")
// looks like file:/Users/......etc
In Swift 5, Swift 4 and Swift 3
To convert String to URL:
URL(string: String)
or,
URL.init(string: "yourURLString")
And to convert URL to String:
URL.absoluteString
The one below converts the 'contents' of the url to string
String(contentsOf: URL)
fileURLWithPath() is used to convert a plain file path (e.g. "/path/to/file") to an URL. Your urlString is a full URL string including the scheme, so you should use
let url = NSURL(string: urlstring)
to convert it back to NSURL. Example:
let urlstring = "file:///Users/Me/Desktop/Doc.txt"
let url = NSURL(string: urlstring)
println("the url = \(url!)")
// the url = file:///Users/Me/Desktop/Doc.txt
There is a nicer way of getting the string version of the path from the NSURL in Swift:
let path:String = url.path
2021 | SWIFT 5.1:
FOR LOCAL PATHS
String --> URL :
let url1 = URL(fileURLWithPath: "//Users/Me/Desktop/Doc.txt")
let url2 = URL(fileURLWithPath: "//Users/Me/Desktop", isDirectory: true)
// !!!!!NEVER DO THIS!!!!!!
let url3 = URL(string: "file:///Users/Me/Desktop/Doc.txt")!
// !!!!!NEVER DO THIS!!!!!!
URL --> String :
let a = String(describing: url1) // "file:////Users/Me/Desktop/Doc.txt"
let b = "\(url1)" // "file:////Users/Me/Desktop/Doc.txt"
let c = url1.absoluteString // "file:////Users/Me/Desktop/Doc.txt"
// Best solution in most cases
let d = url1.path // "/Users/Me/Desktop/Doc.txt"
FOR INTERNET URLs
String --> URL :
let url = URL(string: "https://stackoverflow.com/questions/27062454/converting-url-to-string-and-back-again")!
URL --> String :
url.absoluteString // https://stackoverflow.com/questions/27062454/converting-url-to-string-and-back-again
url.path // /questions/27062454/converting-url-to-string-and-back-again
NOTICE: pay attention to the url, it's optional and it can be nil.
You can wrap your url in the quote to convert it to a string. You can test it in the playground.
Update for Swift 5, Xcode 11:
import Foundation
let urlString = "http://ifconfig.me"
// string to url
let url = URL(string: urlString)
//url to string
let string = "\(url)"
// if you want the path without `file` schema
// let string = url.path
let url = URL(string: "URLSTRING HERE")
let anyvar = String(describing: url)
Swift 3 (forget about NSURL).
let fileName = "20-01-2017 22:47"
let folderString = "file:///var/mobile/someLongPath"
To make a URL out of a string:
let folder: URL? = Foundation.URL(string: folderString)
// Optional<URL>
// ▿ some : file:///var/mobile/someLongPath
If we want to add the filename. Note, that appendingPathComponent() adds the percent encoding automatically:
let folderWithFilename: URL? = folder?.appendingPathComponent(fileName)
// Optional<URL>
// ▿ some : file:///var/mobile/someLongPath/20-01-2017%2022:47
When we want to have String but without the root part (pay attention that percent encoding is removed automatically):
let folderWithFilename: String? = folderWithFilename.path
// ▿ Optional<String>
// - some : "/var/mobile/someLongPath/20-01-2017 22:47"
If we want to keep the root part we do this (but mind the percent encoding - it is not removed):
let folderWithFilenameAbsoluteString: String? = folderWithFilenameURL.absoluteString
// ▿ Optional<String>
// - some : "file:///var/mobile/someLongPath/20-01-2017%2022:47"
To manually add the percent encoding for a string:
let folderWithFilenameAndEncoding: String? = folderWithFilename.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
// ▿ Optional<String>
// - some : "/var/mobile/someLongPath/20-01-2017%2022:47"
To remove the percent encoding:
let folderWithFilenameAbsoluteStringNoEncodig: String? = folderWithFilenameAbsoluteString.removingPercentEncoding
// ▿ Optional<String>
// - some : "file:///var/mobile/someLongPath/20-01-2017 22:47"
The percent-encoding is important because URLs for network requests need them, while URLs to file system won't always work - it depends on the actual method that uses them. The caveat here is that they may be removed or added automatically, so better debug these conversions carefully.
Swift 3 version code:
let urlString = "file:///Users/Documents/Book/Note.txt"
let pathURL = URL(string: urlString)!
print("the url = " + pathURL.path)
Swift 5.
To convert a String to a URL:
let stringToURL = URL(string: "your-string")
To convert a URL to a String:
let urlToString = stringToURL?.absoluteString
Swift 3 used with UIWebViewDelegate shouldStartLoadWith
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
let urlPath: String = (request.url?.absoluteString)!
print(urlPath)
if urlPath.characters.last == "#" {
return false
}else{
return true
}
}