Difference of String(contentsOf: URL).data(using: .utf8) vs. Data(contentsOf: URL) - swift

I have been playing with a json file in a playground and I've seen examples of reading the file like this:
do {
let jsonData = try String(contentsOf: url).data(using: .utf8)
} catch {
...
}
And like this:
do {
let jsonData = try Data(contentsOf: url)
} catch {
...
}
Is there a difference in the data? The only difference I see is the String data method is being formatted as UTF8 when read, where I am assuming the Data method is reading with a default format (UTF8 also??)? I can't see a difference in the data, however, but just want to make sure.

The difference is that String(contentsOf: url) tries to read text from that URL, whereas Data(contentsOf: url) reads the raw bytes.
Therefore, if the file at the URL is not a plain text file, String(contentsOf: url) could throw an error, whereas Data(contentsOf: url) would read it successfully.
Regarding the encoding, String(contentsOf: url) is undocumented, but from its implementation, we can see that it calls NSString.init(contentsOf:usedEncoding:):
public init(
contentsOf url: __shared URL
) throws {
let ns = try NSString(contentsOf: url, usedEncoding: nil)
self = String._unconditionallyBridgeFromObjectiveC(ns)
}
NSString.init(contentsOf:usedEncoding:) is documented:
Returns an NSString object initialized by reading data from a given URL and returns by reference the encoding used to interpret the data.
So apparently the encoding is guessed (?) and returned by reference, which is then ignored by String.init(contentsOf:), as it passed nil for the usedEncoding parameter.
This means that for some non-UTF-8 files, there is a chance of String(contentsOf:) guessing the correct encoding, and then data(using: .utf8) encodes the string to UTF-8 bytes, making the rest of your code work. If you had used Data(contentsOf:), you would be reading in the wrong encoding, and though it wouldn't throw an error, the JSON-parsing code later down the line probably would.
That said, JSON is supposed to be exchanged in UTF-8 (See RFC), so an error when you read a non-UTF-8 file is probably desired.
So basically, if we are choosing between these two options, just use Data(contentsOf:). It's simpler and less typing. You don't need to worry about thing like wrong encodings, or that the file is not plain text. If anything like that happens, it is not JSON, and the JSONDecoder later down the line would throw.

Related

Use of init(contentsOfFile:encoding:) Swift 4

I want to get a string from a file. I've researched how to do it, and I've found the next code:
import Foundation
// Read data from this file.
let path = "/Users/samallen/file.txt"
do {
// Use contentsOfFile overload.
// ... Specify ASCII encoding.
// ... Ignore errors.
var data = try NSString(contentsOfFile: path,
encoding: String.Encoding.ascii.rawValue)
// If a value was returned, print it.
print(data)
}
The important part are the lines:
var data = try NSString(contentsOfFile: path,
encoding: String.Encoding.ascii.rawValue)
I looked in Apple's documentation about this and found init(contentsOfFile:usedEncoding:)
What I don't get is why you can use String(contentsOfFile:usedEncoding:) instead of init(contentsOfFile:usedEncoding:). Why can you replace String for init? I have seen somthing similar with UIImage.
Thanks in advance

data from base64 url

I have a URL in the form of
foo://?data:application/x-foo;base64,OjAyMDAwMDA0MDAwMEZBDQo6MTAwMDA...
and now need to extract the base64 data into a Data object.
Unfortunately it seems the Data object does not support this yet as
let data = try Data(contentsOf: url)
returns NSURLConnection finished with error - code -1002 when trying.
While I could decode the URL manually I am wondering if I am missing a simple standard way of doing this. How would you do this?
Actually you can decode Base64 data from an URL (see for
example Base64 Decoding in iOS 7+ where this is demonstrated in Objective-C). The format is a bit different from what
you have:
let url = URL(string: "data:application/octet-stream;base64,SGVsbG8gd29ybGQh")!
let data = try! Data(contentsOf: url)
print(String(data: data, encoding: .utf8)!) // Hello world!
(Error checking omitted for brevity.)
You have to separate the base64 encoded part of the URL from the other parts, decode it, then join the original non-encoded part with the decoded part and get the data from there.
extension URL {
init?(partialBase64Url: String){
guard let base64part = base64Url.components(separatedBy: "base64,").last, let base64Data = Data(base64Encoded: base64part), let decodedString = String(data: base64Data, encoding: .utf8) else {
return nil
}
let decodedUrl = base64Url.components(separatedBy: "base64,").dropLast().joined() + decodedString
self.init(string: decodedUrl)
}
}
let decodedUrl = URL(partialBase64Url: "foo://?data:application/x-foo;base64,dGVzdFVybFN0cmluZw==")
Value of decodedUrl: "foo://?data:application/x-foo;testUrlString", as expected, since dGVzdFVybFN0cmluZw== is the base64 encoded value of testUrlString.

cannot invoke initializer for type 'int' with an argument list of type 'NSString?'

I am developing an app, where I have to read from web page. Or that is my solution for my app. It is reading from database, but I have errors when using JSON, so i tried this, because i have to read only one number. But when I download the webpage code, which is only "3" for example, just the number. In my program it is like (NSString?), is there any possibilities to convert it in INT?
let url = NSURL(string: "http://servis.dronysitmp.cz/cteni.php")
if url != nil {
let task = URLSession.shared.dataTask(with: url! as URL, completionHandler: { (data, response, error) -> Void in
//print(data)
if error == nil {
let urlContent = NSString(data: data!, encoding: String.Encoding.ascii.rawValue) as NSString!
let urlContent2: Int = Int(urlContent)
print(urlContent!)
}
}
task.resume()
}
Josef, you’re not using common Swift patterns correctly. Instead of
let url = ...
if url != nil { ...
you would boost legibility by using this pattern:
if let url = ... {
In this case url is non-optional and cannot be nil. This construct is called “safe-unwrapping an optional” in Swift.
The next part of code that doesn’t look too nice is this:
let urlContent = NSString(data: data!, encoding: String.Encoding.ascii.rawValue) as NSString!
If you use a NSStringconstructor then there’s no need to cast it to NSString. What you’re probably trying to achieve is to get around the fact that NSString(data:encoding:) returns an optional—which it does for good reason as it cannot guarantee to always be successful. You should therefore rather do something like this:
if let urlContent = NSString(data: data!, encoding: String.Encoding.ascii.rawValue) {
which makes sure that no crash will occur in such cases when NSString cannot convert the supplied data into an integer.
Finally, please consider these two general advices:
Never use force-unwrapping of an optional without prior testing for nil. I. e. never do this: variable! if you cannot be 100% sure that variable is non-nil. So NSString(data: data!,... in your example is not good.
Try to avoid Foundation classes (NS stuff like NSString, etc.) if pure Swift substitutes exist. In your example: if let urlContent = String(data: data!, encoding: String.Encoding.ascii.rawValue) {. This makes your code “swifter” which is what Swift coders prefer…

Different console output data via NSData and Data (Xcode 8 beta 6, Swift 3)

I write a little snippet of usual code but found that my code don't return hex data from server with this line of code:
let currentData = try! Data(contentsOf: fullURL!)
print("currentData=", currentData)
And the output:
currentData= 24419 bytes
I tried to use Leo's comment link:
stackoverflow.com/q/39075043/2303865
I got something hex data without spaces, and validator (http://jsonprettyprint.com) can't recognise it and returns null.
Let's try to sort out the different issues here and summarize the
above comments.
The description method
of Data prints only a short summary "NNN bytes", and not a hex dump
as NSData did:
let o = ["foo": "bar"]
let jsonData = try! JSONSerialization.data(withJSONObject: o)
print(jsonData) // 13 bytes
You can get a hex dump by bridging to NSData (source):
print(jsonData as NSData) // <7b22666f 6f223a22 62617222 7d>
or by writing an extension method for Data (How to convert Data to hex string in swift).
But that is actually not the real problem. The JSON validator needs
the JSON as a string, not as a hex dump (source):
print(String(data: jsonData, encoding: .utf8)!) // {"foo":"bar"}
And to de-serialize the JSON data into an object you would need
none of the above and just call
let obj = try JSONSerialization.jsonObject(with: jsonData)

Swift Conversion UTF16 to UTF8 and back

I'm building an app in Swift an I'm using Backendless as my backend. Turns out their database is UTF8 and thus I can't save emojis without converting the String first.
I can't seem to find the right way to make this conversion to UTF8. I tried this:
let encoding = processedText.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
But after this operation the emojis look like this:
%F0%9F%99%84%F0%9F%98%80%F0%9F%98%92%F0%9F%98%89%F0%9F%98%B6%F0%9F%98%B6%F0%9F%98%80%F0%9F%99%81
And I tried this:
class func stringToUTF8String (string: String) -> String? {
let encodedData = string.dataUsingEncoding(NSUTF8StringEncoding)!
let attributedOptions = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
do{
let attributedString = try NSAttributedString(data: encodedData, options: attributedOptions, documentAttributes: nil)
return attributedString.string
}catch _ {
}
return nil
}
And the emojis look like this:
🤔🤔👅ðŸ™ðŸ˜‚😭😎😉😅😉
Does anyone have any suggestions? Thanks
First, to create a String from NSData with utf8 encoding you use
String(data: theData, encoding: NSUTF8StringEncoding)
Second, swift String's already are unicode compliant. You do not have to convert them as they already do that. You can access different encodings with their respective properties, e.g. String.utf8, String.utf16, and so on.
Third, to have NSAttributedString properly utf8 encode your string from data you have to add NSCharacterEncodingDocumentAttribute key to the attributedOptions dictionary with the value NSUTF8StringEncoding.
Final notes, I don't know if that's a partial method, but attributed string shouldn't be used just to encode a string.
Here is NSAttributedString encoding the data in some format returning gibberish.
Here is NSAttributedString encoding data as utf8 and returning correct text.
Here is encoding a string as utf8 string.
I know it's an image, but I wanted the results to show. If these don't work, the database may be stripping bits. Which sucks, also have no idea what to do then, and you probably shouldn't use that database if you want unicode support.