try? returns double optional in Swift 4 - exception

There are a lot of similar questions but no one describes how to handle exceptions with try? + double optional.
I know str1 doesn't generate exception. It is just an example:
let str1: String? = "some string"
let str2: String? = try? str1 //compilation error because "try? str1" returns object of type String??
One of the possible solutions is replace try? -> try:
let str1: String? = "some string"
let str2: String? = {
do {
return try str1
} catch {
return nil
}
}()
But is it possible to preserve try? like the following?
let str2: String? = try? ...//do something with str1 in the same single line
P.S. In Swift 5 the initial code works but I need to something with it in Swift 4 (upgrading to Swift 5 requires significant changes in project)

Returning a double optional was the default behavior since Error Handling with do - try - catch was introduced in Swift 3.
In Swift 5 SE 230: Flatten nested optionals resulting from 'try?' has been implemented.
upgrading to Swift 5 requires significant changes in project
Do it, it's worth it. The changes are less dramatic than from Swift 2 to 3

Related

Unable to parse Json properly with SwiftyJson

I have the folllowing Json response:
` [Body]:
{"id":"cmpl-6Z45N6vfd2312vdfqyYPb8aDRVe9Nft7vfnpoEsL","object":"text_completion","created":16738vfv15205,"model":"text-davinci-003","choices":[{"text":"\n1. Full Stack Developer\n2. Project Engineering Intern\n3. AI Programmer\n4. Systems Trade Studies Engineer\n5. BLE Technology Developer\n6. macOS Menu Bar App Developer\n7. Mobile App Developer\n8. Research Engineer\n9. Writing and Design Lab Researcher","index":0,"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":726,"completion_tokens":60,"total_tokens":786}}`
And I'm using SwiftyJson and Alamofire to parse the Json using the following code:
` AF.request(...).responseJSON { response in
print("response: \(response)")
switch response.result {
case .success(_):
if let JSON = response.value as! [[String : Any]]?{
print("JSON: \(JSON)")
let dic = JSON[0] as [String:AnyObject]?
print("TitularEmail : ",dic?["choices"])
}
break
case .failure(_):
print("There is an error")
break
}
}`
But it keeps breaking and I keep getting the error 'Thread 1: signal SIGABRT' at 'if let JSON = response.value as! [[String : Any]]?{'
Does anyone know what could've gone wrong? I've read through quite some threads but I can't seem to figure this out. Any info shall be appreciated.
Using the built-in Decodable would be a much better alternative. At the risk of slightly wandering from the original SwiftyJSON question, I highly recommend using Decodable to parse JSON (see also Encodable for writing and Codable for the combination of both). Here's an example of a very basic use of Decodable. Supposed you want to parse the following:
{
"name": "New York City",
"mayor": "Eric Adams",
"population": 8468000
}
You can parse it super easily with Decodable:
struct City: Decodable {
let name:String
let mayor:String
let population:Int
}
You can create an instance of City in this example by taking the JSON– assume it is stored in a variable called jsonData– and simply doing the following:
let NYC = try JSONDecoder().decode(City.self, from: jsonData)
You should highly consider this route because it can get a lot done with very little work.
You can also use JSONSerialization if you don't want to use the aforementioned ways of parsing it. Here is a simple tutorial on how to do that.
In your case, you can implement Decodable to solve your problem like this:
var json = try! Data(contentsOf: URL(fileURLWithPath: "PATH TO FILE WITH EXAMPLE JSON TEXT"))
struct FirstStruct:Decodable {
let id:String
let object:String
let created:String //I'm calling this a string because, despite the lack of quotes, it has letters in it and cannot be an Int. You could also do something like Int? so it can fail safely if there are letters. I am adding quotes to the example text make it work.
let model:String
let choices:[SecondStruct]
let usage:ThirdStruct
}
struct SecondStruct:Decodable {
let text:String
let index:Int
let logprobs:String? //idk what type this is supposed to be from the text given
let finish_reason:String
}
struct ThirdStruct:Decodable {
let prompt_tokens:Int
let completion_tokens:Int
let total_tokens:Int
}
var test = try! JSONDecoder().decode(FirstStruct.self, from: json)
print(test.id)
print(test.usage.total_tokens) //these two examples show it clearly works here
Obviously, have a bit more care with error handling, try! was just easier for the example.

Swift 4 JSON String with unknown UTF8 "�" character is not convertible to Data/ Dictionary

Edit: I was able to pin down the issue to a MUCH more concentrated field. Although this post here isn't necessarily wrong with its assumptions, Swift 4 base64 String to Data not working due to special character is much more clear and has a Playground example.
I have a string that has to be be serialized into a Dictionary in Swift 4. The app lets users upload data (JSON serialized as Data) and download it later. For the latter, the app does the following with the downloaded data (dlData)
if let rootDict = NSKeyedUnarchiver.unarchiveObject(with: dlData) as? Dictionary<String, Any> {
if let content = rootDict["C"] as? String {
if let data = content.data(using: .utf8, allowLossyConversion: true){
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any]
...
} else {
print("DATA DIDNT WORK") //gets printed with his data
}
Pretty much every time this has worked fine but recently a user has contacted me that on his iPhone there isn't any data showing up. I've added the else path and that's where it goes. It doesn't seem to be able to convert this particular string to Data
When I print() the string, copy the console output and then hardcode it into the method, it works just fine. The string is valid JSON (validated with 3 different online validators), and the JSONSerialization also works. But not in the "live" environment where it uses the downloaded data instead of the hardcoded print()-representation
Where I think the issue might be is that the Xcode console "cleans up" the string and "bad" characters that might be in it which is why copy-pasting makes it work but a direct download does not. The only weird thing I can spot in the print()ed string is the replacement character (the � symbol).
dlData > rootDict > content > data > json
Data > Dictionary > string > Data > Dictionary
Isn't the best possible chaining for this task but I am not in the position to change the infrastructure of this system. And because it does work for at least 95% of the users, I think it should work for all of them.
I've tried doing replacingOccurrences(of: "�", with: "?") but this doesn't affect the string, probably because in the actual string this is no "�" but something else and the "�" only gets displayed because the console doesn't know how else to put it.
I've come across this blog https://natrajbontha.wordpress.com/2017/10/12/replacement-character-in-json-data/ but I would actually prefer to do the cleaning up only when the conversion has already failed once.
The original character at this spot is the American flag emoji and my users could live without having the latest emojis in there but generally, I want emojis to be displayed so replacing all of them isn't a choice.
I've just tried the same in the Playground and it results in the same.
//b64String is dlData but in base64
let decodedData = Data(base64Encoded: b64String)! //works
if let unarchivedDictionary = NSKeyedUnarchiver.unarchiveObject(with: decodedData) as? Dictionary<String, Any> { //works
if let dF = unarchivedDictionary["C"] as? String { //works
print(dF) //prints
if let data = dF.data(using: .utf8, allowLossyConversion: true) { //fails
print(data)
} else {
print("NO DATA") //prints
}
}
}

change JSON to String in Swift [duplicate]

This question already has answers here:
Simple and clean way to convert JSON string to Object in Swift
(17 answers)
Closed 5 years ago.
I got JSON file from API but the content looks like this:
[
"https:\/\/seekingalpha.com\/article\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL"
]
Suppose the JSON Object above is named as json. Then I just convert the object to string using String() method.
strings = String(json)
When I changed it to String type, it seems to get unnecessary '\n' and whitespace in it.
"[\n \"https:\\/\\/seekingalpha.com\\/article\\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL\"\n]"
So it seems like the content of the JSON file is:
["\n""whitespace""whitespace""String""\n"]
When I changed it to String type, Swift just treats all the elements in it as a whole and wrapped it as a string.
My question is, how to extract the String inside so it looks like:
"https:\\/\\/seekingalpha.com\\/article\\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL\"
As I am not so familiar with Swift so how to extract String or JSON Object is not easy for me. Any hints or help will be appreciated.
Swift 3,4 :
The given JSON format is Array of String.
if let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String]{
let firstElement = json?.first ?? "Element Not Found!"
print(firstElement)
}
Swift 4:
if let json = try? JSONDecoder().decode(Array<String>.self, from: data){
let firstElement = json.first ?? "First Element Not Found!"
print(firstElement)
}
Note:
If your the Array contains more than one String. Here,urls is the class variable. i.e.,var urls = [String]()
Swift 3,4 :
if let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String]{
if json != nil{
self.urls = json!
}
print(self.urls)
}
Swift 4:
if let json = try? JSONDecoder().decode(Array<String>.self, from: data){
self.urls = json
}
1. You will first have to convert JSON to Data
2. Convert data to string wrt to encoding
func jsonToString(jsonTOConvert: AnyObject){
do {
let data = try JSONSerialization.data(withJSONObject: jsonTOConvert, options: JSONSerialization.WritingOptions.prettyPrinted)
let convertedString = String(data: data, encoding: String.Encoding.utf8)
} catch let myJSONError {
print(myJSONError)
}
}
You are asking that a String be created with the contents:
[
"https:\/\/seekingalpha.com\/article\/4125943-apple-homepod-delay-big-deal?source=feed_symbol_AAPL"
]
The string object is doing exactly what you told it to — the thing you've asked it to represent begins with a square bracket, then there's a line break, then two spaces, etc, so the string contains a square bracket, then a line break, then two spaces, etc. There is no 'unnecessary' \n, there is only the \n you told it to put in.
If you obtained a JSON object then you need to parse it as JSON. JSONSerialization will do that job. What you've actually got is an array with a single item, which is a string. So JSONSerialization will return an array. The first item of that should be a string that is the seekingalpha.com URL.

JSON's numeric value to string in swift?

I get json from server and I need to convert id field to string in swift code.
The problem is json sometimes returns "12345", sometimes returns 12345 (with or without quotes).
Is it possible to resolve this issue without of checking the value type and checking if the conversion result is nil?
UPDATED
Example of code I use with checking conversion result:
let result = (some_index as? String) ?? String(some_index as! Int)
The problem is in objective-C you have [NSString stringWithFormat:#"%#", some_object]. But in swift you have optionals and it tries to insert word "optional" into result.
UPDATED
STOP spam with random answers about optionals. The question is concrete - "how to simply unwrap json value which may look like String, Int or doesn't exist at all?"
swift How to remove optional String Character
In this question they ask how to convert Int? -> Int, String? -> String and similar. In my case I don't know if I have Int? or String? as the initial type.
Both Stringand Int conform to CustomStringConvertible, so you could optional downcast the value to CustomStringConvertible and use String Interpolation
let dict : [String:Any] = ["Foo" : 12345]
if let value = dict["Foo"] as? CustomStringConvertible {
let result = "\(value)"
}
And blame the owner of the web service for sending inconsistent data ;-)

Int does not conform to protocol 'StringLiteralConvertible'

Im trying to parse json in weather app, but have hit a snag that i cannot get past.
I do get an error, "Type 'int' does not conform to Protocol 'StringLiteralConvertible'" in the following code.
Ive tried casting the jsonResult["main"] but that does instead give the error "Operand of postfix should have optional type, type is AnyObject". Do i need to downcast the Array in some way and how, if so, should i do that?
I´ve searched so much for this but could not find any help in other posts. Code as follows.
func updateWeatherInfo(latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
Alamofire.request(.GET, AlongRequest)
.responseJSON { (_, _, JSON, error) in
println(JSON)
self.updateUISuccess(JSON as NSArray!)
}
}
func updateUISuccess(jsonResult: NSArray) {
self.loading.text = nil
self.loadingIndicator.hidden = true
self.loadingIndicator.stopAnimating()
if let tempResult = ((jsonResult["main"] as NSArray)["temp"] as? Double)
This would be easier to give a definitive answer to if you provide the JSON that you're trying to parse, but the error message you're getting is clear.
That error is because you're trying to access what you've declared as an NSArray instance with a string subscript, twice in this one line:
if let tempResult = ((jsonResult["main"] as NSArray)["temp"] as? Double)
jsonResult is declared as an NSArray parameter, and then you're casting jsonResult["main"] to NSArray before trying to subscript it with ["temp"]. The problem here is that NSArray (and built-in Swift arrays) only use integer-based subscripting. The error is saying that where the Swift compiler is expecting an Int, you've provided a string literal.
To fix this, you'll need to go in one of two directions. If the structure you're trying to access actually has these string keys, then you should be using NSDictionary instead of NSArray in both cases. If not, and it's an integer-index array, you should be using integers.