How to get nil from string.data(using: .utf8) - swift

Is there any string that fails to construct "".data(using: .utf8) and returns nil?

According to this discussion, string.data(using: .utf8) is the same as Data(string.utf8), which can't fail (for now, anyway. This is an implementation detail.).
Therefore, there is no string that you can use, to make data(from: .utf8) return nil.
data(from:) is declared to return an optional because you can pass in other encodings, which may not support the characters in the string. The method can't suddenly change its return type to non-optional just because you passed in .utf8, after all!

Related

How do I deal with the String.Encoding initializer never failing?

if let encoding = String.Encoding(rawValue: 999) {
// ...
}
Produces a compiler error saying "Initializer for conditional binding must have Optional type, not 'String.Encoding'" because despite the docs saying the String.Encoding initializer is failable, it is not and will happily create non-existent encodings.
How do I check if the encoding returned by initializer is an actual encoding?
The two ideas I have are
Check the String.Encoding description is not empty. This assumes that supported encodings must have a description
Encode something - e.g. "abc".data(using: encoding) == nil - which assumes that the string "abc" can be encoded by all supported encodings
The documentation is misleading, String.Encoding has a non-failable
public init(rawValue: UInt)
initializer. A list of all valid string encodings is String.availableStringEncodings, so you can use that to check the validity:
let encoding = String.Encoding(rawValue: 999)
print(String.availableStringEncodings.contains(encoding)) // false
You can use CoreFoundation's CFStringIsEncodingAvailable() function to check if the encoding is valid, and also if it can be used on that particular device.
However, as MartiR pointed out, CFStringIsEncodingAvailable needs a CFStringEncoding to work with, so the String.Encoding needs to be converted first to a CF one.
let encoding = String.Encoding(rawValue: 999)
let cfEncoding = CFStringConvertNSStringEncodingToEncoding(encoding.rawValue)
if CFStringIsEncodingAvailable(cfEncoding) {
// ...
}
, or, as MartinR nicely suggested, the result of CFStringConvertNSStringEncodingToEncoding can also be used:
if CFStringConvertNSStringEncodingToEncoding(encoding.rawValue) != kCFStringEncodingInvalidId {
// ...
}

Swift Optionals: When should you be expecting them

So I am teaching myself Swift and I get optionals when I am declaring them, so for example:
var thisString:String?
I would have to either force unwrap thisString, or use
if let anotherString = thisString {
or use
guard if let another string = thisString else { return }
or nil coalesce it
let anotherString = thisString ?? "Something else"
But where I am getting hung up is there are times I create something that I don't think it an optional but the compiler does.
For example, why is the URL an optional here?
let myURL = URL(string: "https://itunes.apple.com/search?term=U2")
var myRequest = URLRequest(url: myURL!)
I didn't declare it as an optional and it clearly has a value. So why does the compiler see this as an optional? If I didn't force unwrap it I would get this error:
Value of optional type 'URL?' not unwrapped; did you mean to use '!' or '?'?
Is there some standard that I am missing? I've gone over both the docs and Swift Programming Book from Big Nerd and I still don't seem to get this part.
Thanks for the assist.
But where I am getting hung up is there are times I create something that I don't think it an optional but the compiler does.
But what you think is not what matters. What matters is what the API call you are making actually returns. You find that out by reading the docs.
So why does the compiler see this as an optional
Well, let's read the docs. You are calling URL(string:). That is the same as URL's init(string:). Look at the docs:
https://developer.apple.com/documentation/foundation/url/1779737-init
The declaration is
init?(string: String)
See the question mark? That means, "This call returns an Optional." So you should expect an Optional here.
The compiler can't determine if the url that you define in the string is valid or not.
Suppose instead of:
let myURL = URL(string: "https://itunes.apple.com/search?term=U2")
You miss typed the myURL definition as:
let myURL = URL(string: "https:/itunes.apple.com/search?term=U2")
The string contains a malformed URL, so the program would crash the moment you went to define myURL.
let myURL = URL(string: "https://itunes.apple.com/search?term=U2")
Here you are creating an url from string.That string might not be a valid string.All the strings are not valid url. So you are getting an optional because if that string can be turned in to a valid url then that url will be returned else nil will be returned. See the Apple documentation here.The initializer what you are using is a failable initializer itself.
init?(string: String)
#Keshav is correct, to get a better idea, hold the option button and click on the 'string' part of the init function for the URL class. You will see in the swift reference the init declaration is init?(string: String). This means that a optional is returned. Any function can return a optional, you can have func returnMyString(_ myString: String) -> String {} or func returnMyString(_ myString: String) -> String? {}. Both of those functions are pretty much the same, except the second one returns a optional.
URL has optional initializers. For example you can have
class A {
init?() {
return nil //You will get Optional<A>.none
}
}
A() === Optional<A>.none //true
which kind of implies that initialization failed. Such initializers wrap returned object into Optional. In Swift nil == Optional<Any>.none so you can speak of them interchangeably.
For example if you will attempt to construct a URL with something that is not an actual url, it will return nil.
let notAURL = URL(string: "{") //notAURL will be nil
On a side note: I believe optional initializers are a very poor design choice since they don't communicate anything about what went wrong inside the init. If init is fallible, it should throw. I don't understand why Swift designers allow optional initializers and I see why it births a lot of confusion.

Can the conversion of a String to Data with UTF-8 encoding ever fail?

In order to convert a String instance to a Data instance in Swift you can use data(using:allowLossyConversion:), which returns an optional Data instance.
Can the return value of this function ever be nil if the encoding is UTF-8 (String.Encoding.utf8)?
If the return value cannot be nil it would be safe to always force-unwrap such a conversion.
UTF-8 can represent all valid Unicode code points, therefore a conversion
of a Swift string to UTF-8 data cannot fail.
The forced unwrap in
let string = "some string .."
let data = string.data(using: .utf8)!
is safe.
The same would be true for .utf16 or .utf32, but not for
encodings which represent only a restricted character set,
such as .ascii or .isoLatin1.
You can alternatively use the .utf8 view of a string to create UTF-8 data,
avoiding the forced unwrap:
let string = "some string .."
let data = Data(string.utf8)

Swift3 optional chaining and unwrapping with error handling [duplicate]

This question already has answers here:
Optional binding with try? and as? still produces an optional type
(2 answers)
Closed 5 years ago.
I am attempting to unwrap an optional using the guard let syntax. The goal with guard let is to unwrap optionals, aborting the procedure if .none/nil is ever encountered.
I had thought that this meant that an optional could not be legally returned from an expression on the right-hand-side of a guard let. However, it seems if you combine two optional-producing expressions into the RHS expression, only one of those optional-producing expressions is evaluated for the purposes of the guard let. For example, the parsedResponse variable below will store an optional:
guard let parsedResponse = try? (JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any]) else {
displayError(withString: "Error parsing data as JSON")
return
}
The try? keyword produces an optional, as does the conditional cast operator as?. If you change either of these two operators to try! or as!, you get the expected result.
Is this the expected behavior? I had assumed that attempting to do something like this would produce behavior similar to optional chaining, and the guard let would execute the 'else' code the moment it encountered .none/nil anywhere along the execution path of the evaluation of the RHS. However, this does not appear to be the case.
What does it even mean to have an optional evaluate into a constant/variable introduced by a guard let?
I think if you rearrange the parens it will work.
guard let parsedResponse = (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)) as? [String:Any] else {

Is there any way to use an immutable outside of a try block in Swift?

I know that if I want to use the variable text outside of this try block in Swift, I would write this code thusly,
var text = NSString()
do {
text = try NSString( contentsOfURL: url, encoding: NSUTF8StringEncoding ) }
catch let errOpening as NSError {
// ...
}
From days of yore when storage was meted by bytes, my mantra has been to use constants if at all possible. So in the instance where text will be loaded once and never changed, my gut tells me to make it a constant.
do {
let text = try NSString( contentsOfURL: url, encoding: NSUTF8StringEncoding ) }
catch let errOpening as NSError {
// ...
}
But then I can't use the loaded text outside of the try block. Is there any way to have text be treated as a constant outside of the try block in this context by Swift? Or is this just the yearning of an old man coding in an old style, and I should use var, forget about it, and move on?
Many thanks in advance!
You can do:
let text: String
do {
text = try String(contentsOfURL: url, encoding: NSUTF8StringEncoding)
}
catch let error as NSError {
// ...
}
(I’ve used String, the native string type in Swift, rather than NSString.)
Assuming later code makes use of text, the catch block must either assign something to it or return from the enclosing function, as constants must be initialised before being used.
Bear in mind that the method you’re using to retrieve the contents of a URL as a string does so synchronously, so if this code runs on the main thread you will block the UI. Look up the NSURLSession docs for details on asynchronously loading the content of URLs, which will avoid blocking the UI.