Twitter Api Response:
"retweet_count" = 0;
retweeted = 0;
source = "Twitter for Android";
text = "ALTIN alm\U0131\U015f ba\U015f\U0131n\U0131 gidiyor... bakal\U0131m t\U00fcrk liras\U0131 daha ne kadar de\U011fersizle\U015fecek #Turkiye #BorsaAltin\U2026 https://twitter.com/i/web/status/1216306036602277889";
truncated = 1;
My code:
let request = client.urlRequest(withMethod: "GET", urlString: statusesShowEndpoint, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if connectionError != nil {
print("Error: \(connectionError)")
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("json: \(json)")
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
}
}
}
how can I convert Unicode to string ? i have been don't use model.
I hope this will help.
I made a playground to test this issue. Swift will complain that the String contains incorrect escape characters.
This is a problem and the code will not compile:
Swift expect the Unicode to be formatted with ex '\u0131' or '\u{0131} but you receive the twitter API with '\U0131'
You need to "sanitise" the input first otherwise it will not work! Also when I tested the below I could not save the input in a string with the incorrect escaping. The compiler checks that the strings are correct before doing any operations on them.
I used a trick. Before saving the input from the file I split into an array of characters with map, of these characters I check with filter which one is an escaping backlash remove it and join the characters again to form a string.
Sorry but I did not find any other way, just having '\U' in my input would get Swift yelling at me.
What remains in the input string is "ALTIN almU0131U015f ..etc"
Now I need to replace those 'U0131' with '\u0131' and for this I use Regex:
And this is the final output of my String test as property of my struct after the conversion.
I apologise if my code is a bit messy but it was not easy to get past the string validation in Swift!
The below is the playground code in detail.
What I did is to create a json file with your input as a test:
Then create a struct reflecting the JSON properties, in this case only one: "test"
public struct Test: Codable {
var test = ""
}
// this is the initialisation of the empty struct to be filled from json
var unicodetest: Test = Test()
func parse(json: Data) {
let decoder = JSONDecoder()
if let testmodel = try? decoder.decode(Test.self, from: json) {
unicodetest = testmodel
print(unicodetest.test)
}
}
// Here I get my Json and parse with the above function
do {
guard let fileUrl = Bundle.main.url(forResource: "test", withExtension: "json") else { fatalError() }
let input = try String(contentsOf: fileUrl, encoding: String.Encoding.utf8).map {String($0)}.filter {$0 != "\\"}.joined(separator: "")
if let sanitisedData = input.replacingOccurrences(of: "U(.*?)", with: "\\\\u$1", options: .regularExpression).data(using: .utf8){
parse(json: sanitisedData)
}
} catch {
// if something did not work out
print(error)
}
You can use \u{Unicode}:
print("Ain\u{2019}t i am a smart boy")
/* Prints "Ain’t i am a smart boy"
You can Use this Extension as well
extension String {
var unescapingUnicodeCharacters: String {
let mutableString = NSMutableString(string: self)
CFStringTransform(mutableString, nil, "Any-Hex/Java" as NSString, true)
return mutableString as String
}
}
let input = "ALTIN alm\\u0131\\u015f ba\\u015f\\u0131n\\u0131 gidiyor... bakal\\u0131m t\\u00fcrk liras\\u0131 daha ne kadar de\\u011fersizle\\u015fecek #Turkiye #BorsaAltin\\u2026 https://twitter.com/i/web/status/1216306036602277889"
print("result: \(input.unescapingUnicodeCharacters)")
//ALTIN almış başını gidiyor... bakalım türk lirası daha ne kadar değersizleşecek #Turkiye #BorsaAltin… https://twitter.com/i/web/status/1216306036602277889
Related
I want to print a pretty-printed representation of a JSON I get from the server.
The JSON has many string values in Hebrew.
I noticed that when the output is printed in the debug console, sometimes I see the symbol "327\" inserted inside some of the texts. The location is inconsistent.
Example:
"Description" : "מעבדות קורונה",
"DictionaryPlaceholder" : "ל\327דוגמא : ראש העין",
And if I print the RAW value of the same JSON, it isn't pretty but the string is correct:
\"Description\":\"מעבדות קורונה\",\"DictionaryPlaceholder\":\"לדוגמא : ראש העין\",
The additional 327\ creates parsing issues when I copy the JSON from the console and try to parse it in other systems.
This is the relevant code:
private let separator = "\n"
private let terminator = "\n==========================================\n"
private func logNetworkResponse(_ response: URLResponse?, data: Data?, target: TargetType) -> String {
var output = String()
output.append("[\(date)]\(separator)")
if let data = data {
if let pretty = data.prettyJSON {
output.append("\(target) (pretty-printed) Response: \(pretty)")
} else if let string = String(data: data, encoding: .utf8) {
output.append("\(target) (raw) Response: \(string))")
}
}
output.append(terminator)
return output
}
extension Data {
var prettyJSON: String? {
guard let jsonObject = try? JSONSerialization.jsonObject(with: self) else {
return nil
}
var pretty: Data?
if #available(iOS 13.0, *) {
pretty = try? JSONSerialization.data(withJSONObject: jsonObject, options: [.prettyPrinted, .withoutEscapingSlashes])
} else {
pretty = try? JSONSerialization.data(withJSONObject: jsonObject, options: [.prettyPrinted])
}
guard let pretty = pretty else {
return nil
}
return String(data: pretty, encoding: .utf8)
}
}
And this is how the output of logNetworkResponse() is printed:
public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
var outputMessage: String?
if case .success(let response) = result {
// Attempt to parse the data for pretty printing only in QA Env
if printNetworkActivity {
outputMessage = logNetworkResponse(response.response, data: response.data, target: target)
}
} else {
outputMessage = logNetworkResponse(nil, data: nil, target: target)
}
output(message: outputMessage)
}
private func output(message: String?) {
if let message = message,
printNetworkActivity {
print(message)
}
}
I want the pretty printed output to be valid and without random additional characters that messes it up. Does anybody know what is this symbol 327\ and why is is sometimes inserted inside the texts?
I am using URLSession right now. I want to print only the profile value among the userId and profile in the data value here. How can I print it?
Some of my code
if let httpResponse = response as? HTTPURLResponse {
print( httpResponse.allHeaderFields )
guard let data = data else {return}
print( String (data: data, encoding: .utf8) ?? "" )
let profile = "{\"profile\":\"\(data.profile ?? "")}" // ERROR [Value of type 'Data' has no member 'profile']
}
print (String (data: data, encoding: .utf8) ??"") <When I run this code, I get the result like this. userId and profile I want to display only the profile excluding the userId. Thanks for reading.
I'd define a type conforming to the Codable protocol and use a JSONDecoder to decode your data to something user friendly.
struct UserData: Codable {
let userId: String
let profile: String
}
Here's how to decode your data:
if let httpResponse = response as? HTTPURLResponse {
guard let data = data else { return }
let decoder = JSONDecoder()
do {
let userData = try decoder.decode(UserData.self, from: data)
print(userData.profile)
} catch {
print("Error: \(error)")
}
}
You can convert data to json and use however you want
json = try JSON(data: data)
if you want to parse it easily use SwiftyJson
I am trying to create some structs to decode some JSON received from an API using JSONSerialization.jsonObject(with: data, options: [])
This is what the JSON looks like:
{"books":[{"title":"The Fountainhead.","author":"Ayn Ranyd"},{"title":"Tom Sawyer","author":"Mark Twain"},{"title":"Warhol","author":"Blake Gopnik"}]}
Here are the structs that I am trying to use for decoding.
struct BooksReturned : Codable {
let books : [Book]?
}
struct Book : Codable {
let BookParts: Array<Any>?
}
struct BookParts : Codable {
let titleDict : Dictionary<String>?
let authorDict : Dictionary<String>?
}
The error is:
The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
The non-working code I am using to decode is:
let task = session.dataTask(with: url) { data, response, error in
if let data = data, error == nil {
let nsdata = NSData(data: data)
DispatchQueue.main.async {
if let str = String(data: data, encoding: .utf8) {
let json = try? JSONSerialization.jsonObject(with: data, options: [])
do {
let mybooks = try JSONDecoder().decode(BooksReturned.self, from: data)
//do something with book
}
} catch {
print(error.localizedDescription)
print(error)
}
}
}
} else {
// Failure
}
}
task.resume()
}
I have some very limited ability to change JSON. The only thing I can do is remove the "books" : Everything else is received from an external API.
Thanks for any suggestions on how to get this to work.
The JSON you provided seems to be valid. Modify your Book model and the decoding part as the following.
Model:
struct Book: Codable {
let title, author: String
}
Decoding:
let task = session.dataTask(with: url) { data, response, error in
if let data = data, error == nil {
DispatchQueue.main.async {
do {
let mybooks = try JSONDecoder().decode(BooksReturned.self, from: data)
print(mybooks)
}
} catch {
print(error.localizedDescription)
print(error)
}
}
} else {
// Failure
}
task.resume()
I have used the following code to encode/decode the string which has emojis.
extension String {
func encodeEmoji() -> String {
let data = self.data(using: .nonLossyASCII, allowLossyConversion: true)!
return String(data: data, encoding: .utf8)!
}
func decodeEmoji() -> String? {
let data = self.data(using: .utf8)!
return String(data: data, encoding: .nonLossyASCII)
}
}
I have called this function like this below. Converted the response in the 'User' model.
let user = User() // Loaded API's response in this model
let textWithEmoji = user.aboutMe.decodeEmoji() //Here, I am getting the string as the same as before decoding
lblAboutMe.text = textWithEmoji
Following is the encoded string which is not decoding:
"I love too...\n\u2705 Laugh \uD83D\uDE02\n\u2705 Read novels \uD83D\uDCDA\n\u2705 Watch movies \uD83C\uDFAC\n\u2705 Go for bike rides \uD83D\uDEB5\uD83C\uDFFD\u200D\u2640\uFE0F\n\u2705 Go for long walks \uD83D\uDEB6\uD83C\uDFFD\u200D\u2640\uFE0F\n\u2705 Cook \uD83D\uDC69\uD83C\uDFFD\u200D\uD83C\uDF73\n\u2705 Travel \uD83C\uDDEA\uD83C\uDDFA\uD83C\uDDEE\uD83C\uDDF3\uD83C\uDDEC\uD83C\uDDE7\n\u2705 Eat \uD83C\uDF2E\uD83C\uDF5F\uD83C\uDF73\n\u2705 Play board games \u265F\n\u2705 Go to the theatre \uD83C\uDFAD\nMy favourite season is autumn \uD83C\uDF42, i love superhero movies \uD83E\uDDB8\u200D\u2642\uFE0F and Christmas is the most wonderful time of the year! \uD83C\uDF84"
Here is the original text image:
The string you are using is invalid ("I love too...\n\u2705 Laugh \uD83D\uDE02\n\u2705 Read novels \uD83D\uDCDA\n\u2705 Watch movies \uD83C\uDFAC\n\u2705")
It should be in valid String literal
"\\uD83D\\uDCDA\\u2705"
You have a string non-BMP characters in form of JSON String. And your decodeEmoji cannot convert them into valid characters.
So we need to forcefully convert such strings.
extension String {
var jsonStringRedecoded: String? {
let data = ("\""+self+"\"").data(using: .utf8)!
let result = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! String
return result
}
}
After that you need to decode emoji from above string using below function.
extension String {
var decodeEmoji: String? {
let data = self.data(using: String.Encoding.utf8,allowLossyConversion: false);
let decodedStr = NSString(data: data!, encoding: String.Encoding.nonLossyASCII.rawValue)
if decodedStr != nil{
return decodedStr as String?
}
return self
}
}
Usually JSON decoder can decode these type of characters into emoji
May be there is chances of invalid JSON
First need to verify these things that json is valid or not before using.
USAGE:
let jsonDecodedString = "Your string".jsonStringRedecoded
let decodedEmojiText = jsonDecodedString?.decodeEmoji
debugPrint("\(decodedEmojiText)")
I am receiving a result from an API, I can iterate through the result. My understanding is I can pass the value into a model immediately.
Apple Developer article on struct models
My issue is I am not doing it properly and am receiving a nil value. Perhaps someone can see where I need to change. I am using Swift 4.2
Here is my struct model.
import Foundation
struct ProfileModel {
//MARK: Properties
var name: String
var email: String
var profileURL: String
//MARK: Initialization
}
extension ProfileModel{
init?(json: [String:AnyObject]) {
guard
let name = json["name"] as? String,
let email = json["email"] as? String,
let profileURL = json["profileURL"] as? String
else { return nil }
self.name = name
self.email = email
self.profileURL = profileURL
}
}
Here is my result code from my urlConnection. Let me know if we want to see the entire swift file
//create dataTask using the session object to send data to the server
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject] {
self.onSuccess(data: json)
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
func onSuccess(data: [String:AnyObject]){
print("onSuccess")
let myProfile = ProfileModel(json: data)
//myProfile is nil while unwrapping
let title: String = myProfile!.name
print(title)
}
I could just iterate through the strings since I am able to print 'data'. I just figured it would be cleaner to put everything into a ProfileModel and manage that object as a whole.
This json is my more simple one which is why I used it for this question. I also can't remember but I had to use "[String:AnyObject]" to get the json properly. This was pulled directly from my terminal, this was the data being passed in my JsonResponse. The output json from Xcode has [] on the outside instead.
{
'detail': 'VALID',
‘name’: ‘Carson,
'email': ‘carson.skjerdal#somethingelselabs.com',
'pic_url': None
}
EDIT:
So my problem is solved, and ultimately moving to Codable was the key. Here is my fixed code for anyone who might need a working solution.
URLSession.shared.dataTask(with: request as URLRequest) { (data, response
, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(ProfileModel.self, from: data)
print(gitData.name)
self.onSuccess(data: gitData)
} catch let err {
print("Err", err)
}
}.resume()
}
func onSuccess(data: ProfileModel){
print("onSuccess")
print(data.email)
}
My Codable Struct - slightly simplified
import Foundation
struct ProfileModel: Codable {
let detail, name, email: String
private enum CodingKeys: String, CodingKey {
case detail, email
case name = "firstname"
//case picUrl = "pic_url"
}
}
After "Codable" has been introduced I always uses that.
You can take your JSON ans pars it in to QuickType.io, and you will get a Struct that confirms to the codadable
// To parse the JSON, add this file to your project and do:
//
// let aPIResponse = try? newJSONDecoder().decode(APIResponse.self, from: jsonData)
import Foundation
struct APIResponse: Codable {
let detail, name, email, picUrl: String
enum CodingKeys: String, CodingKey {
case detail, name, email
case picUrl = "pic_url"
}
}