I got json data from server like this.
[
"http:\/\/helloWord.com\/user\/data\/000001.jpg?1497514193433",
"http:\/\/helloWord.com\/user\/data\/000002.jpg?1500626693722"
]
And What should I do to get each user url?
I try to use removingPercentEncoding, but it doesn't work.
What should I do?
Thanks.
let string:String = chatroom.avatar
let tempArr = string.components(separatedBy: ",")
var stringArr = Array<String>()
print("**tempArr\(tempArr)")
for a in tempArr {
var b = a.replacingOccurrences(of: "\"", with: "")
b = b.replacingOccurrences(of: "[", with: "")
b = b.replacingOccurrences(of: "]", with: "")
b = b.removingPercentEncoding //not working!!!!
print("b: \(b)")
//b: http:\/\/helloWord.com\/user\/data\/000001.jpg?1497514193433
//b: http:\/\/helloWord.com\/user\/data\/000002.jpg?1500626693722
}
I use swiftyJson
class User : Model {
var url:String = ""
func fromJson(_ json:JSON) {
url = json["url"].zipString
saveSqlite()
}
}
extension JSON {
var prettyString: String {
if let string = rawString() {
return string
}
return ""
}
var zipString: String {
if let string = rawString(.utf8, options: JSONSerialization.WritingOptions.init(rawValue: 0)) {
return string
}
return ""
}
}
Err, you shouldn't try to write your own JSON parser.
Try: https://github.com/SwiftyJSON/SwiftyJSON
It is one file of Swift code and makes using JSON much easier in Swift.
In swift 4 you will be able to use Structs to directly decode, but we're not there yet.
The apple way:
func read(payload: Data) throws -> [String]? {
guard let result = try JSONSerialization.jsonObject(with: payload, options: JSONSerialization.ReadingOptions.allowFragments) as? [String] else { return nil }
return result
}
Then for example you could read them out this way:
var userURLs: [URL] = []
for jsonURL in result {
guard let userURL = URL(string: jsonURL) else { continue }
userURLs.append(userURL)
}
This way you get only valid URL objects in your last result array.
If you get problems using the JSONSerialization code I described above it might be that it expects a different type. Then you'd have to use [Any] as a cast instead, or in case you have objects [String: Any] usually works. Keep in mind that in that case you will have to cast the objects you get from the array like so:
URL(string: (jsonURL as? String) ?? "")
Swifty JSON makes it easier to go about with the nullability, as it provides an easy way to safely traverse an object tree and return empty but non nil values!
It seems to me that you want to use removingPercentEncoding to remove escape characters - those backslashes? While removingPercentEncoding is to work on percent encoded characters, e.g. to convert http%3A%2F%2Fwww.url-encode-decode.com%2F to http://www.url-encode-decode.com/. So you are using it at the wrong place. Make sure to call this method only on the URLs that are percent encoded.
For this scenario, like others already suggested, use JSONSerialization is the way to go.
Related
How to parse more then one JSON which each ending with null character(through socket TCP/IP).
{"ObjectID":"UHJvY1dpcmVsZXNzTXNn","DeviceCode":"RUNEOjI=","ActiveInputNames":"Q2hlY2sgaW4gRmFpbA==","DeviceInputNo":"999999","Activation":false,"Reset":true,"LocationID":"","LocationGroupText":"","ProtocolText":"","CallBackNo":"OTE5MTgyNTcyMjQ5"}��{"ObjectID":"VFBpbmdPYmplY3Q="}��
As you can see the above response which has 2 JSON's each ending with null character...I can easily parse the single JSON but unable to parse more then one JSON..
It would be great if any one suggest any solutions!!
First of all separate both the JSONs using components(separatedBy:) so we can parse them individually.
let str = """
{"ObjectID":"UHJvY1dpcmVsZXNzTXNn","DeviceCode":"RUNEOjI=","ActiveInputNames":"Q2hlY2sgaW4gRmFpbA==","DeviceInputNo":"999999","Activation":false,"Reset":true,"LocationID":"","LocationGroupText":"","ProtocolText":"","CallBackNo":"OTE5MTgyNTcyMjQ5"}��{"ObjectID":"VFBpbmdPYmplY3Q="}��
"""
let jsonArr = str.components(separatedBy: "��")
jsonArr contains both the JSON Strings. Let's see how we can parse them.
We'll use Codable to parse both the JSONs using the below model.
struct Root: Codable {
let objectID: String
let deviceCode: String?
let activeInputNames: String?
let deviceInputNo: String?
let activation: Bool?
let reset: Bool?
let locationID: String?
let locationGroupText: String?
let protocolText: String?
let callBackNo: String?
enum CodingKeys: String, CodingKey {
case objectID = "ObjectID"
case deviceCode = "DeviceCode"
case activeInputNames = "ActiveInputNames"
case deviceInputNo = "DeviceInputNo"
case activation = "Activation"
case reset = "Reset"
case locationID = "LocationID"
case locationGroupText = "LocationGroupText"
case protocolText = "ProtocolText"
case callBackNo = "CallBackNo"
}
}
Parse the JSONstrings like,
let parsedObjs = jsonArr.map { (str) -> Root? in
if let data = str.data(using: .utf8) {
do {
let obj = try JSONDecoder().decode(Root.self, from: data)
return obj
} catch {
print(error)
return nil
}
}
return nil
}
parsedObjs will contain parsed Root objects for both the JSON strings.
Let me know if there is any confusion left regarding this.
I answered the same question in Android Yesterday. Here is the Swift Version
let s = "{\"ObjectID\":\"UHJvY1dpcmVsZXNzTXNn\",\"DeviceCode\":\"RUNEOjI=\",\"ActiveInputNames\":\"Q2hlY2sgaW4gRmFpbA==\",\"DeviceInputNo\":\"999999\",\"Activation\":false,\"Reset\":true,\"LocationID\":\"\",\"LocationGroupText\":\"\",\"ProtocolText\":\"\",\"CallBackNo\":\"OTE5MTgyNTcyMjQ5\"}��{\"ObjectID\":\"VFBpbmdPYmplY3Q=\"}��".components(separatedBy: "��")
for string in s{
// do your parsing here
print(string)
}
All you need to do is split the string with �� and you are good to go. Parse the JSON the way you used to.
From some URL I create an array of strings, and I would like to grab some data from those strings and turn them into another array of variables.
My array of strings looks like this:
#EXTINF:-1 tvg-logo="https://www.thetvdb.com/banners/posters/248741-9.jpg" group-title="Broke Girls", trailer
#EXTINF:-1 tvg-logo="https://www.thetvdb.com/banners/posters/210841-10.jpg" group-title="Alphas", Alphas trailer
#EXTINF:-1 tvg-logo="https://www.thetvdb.com/banners/posters/309053-2.jpg" group-title="American Gothic", trailer
Every line represents a new string item from my array.
I am trying to create a function to do it, but until now, I only have this:
func grabValuesFromUrl(savedUrl: String) {
var trailersArray = []()
if let url = URL(string: savedUrl) {
do {
let contents = try String(contentsOf: url)
contents.enumerateLines { (line, stop) in
// here i need to grab the values from every string inside tvg-logo="", group-title="", and the last one after "," that's the title, and put them into trailersArray[], afterwards i will make some model class to get the data like trailersArray.logo and trailersArray.group and trailersArray.title
}
} else {
print("no url added")
}
}
Thanks in advance
I'd use regex for anything related to extracting data from a string with known format. For this, lets first define helper function:
func matches(for regex: String, inText text: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
let nsString = text as NSString
let results = regex.matches(in: text, options: [], range: NSMakeRange(0, nsString.length))
return results.flatMap { result in
(0..<result.numberOfRanges).map {
result.range(at: $0).location != NSNotFound ? nsString.substring(with: result.range(at: $0)) : ""
}
}
}
And then define the regular expression that will extract required data:
let regex = "^.*tvg-logo=\"(.+)\".*group-title=\"(.+)\".*, (.+)$"
Beware that this regex is sensitive to data format so you'll have to adapt it to new one in case of changes.
Finally, in your line enumeration closure you can extract the data:
let parts = matches(for: regex, inText: line).dropFirst()
parts is now an array with three corresponding items (we drop the first one because it is the line itself) if the line matches the regex, so we can, for example, append a tuple with values to the array:
if parts.count == 3 {
trailersArray.append((logo: parts[0], group: parts[1], title: parts[2]))
}
I have a simple GET request for login. Username is Silver and password is MOto

I am using SwiftHttp framework for handling requests. On hitting login request, I always get response as false.
However on hitting the login request url on browser (replaced actual domain with server) I get true :
https://server/api/check-access/by-login-pass?_key=wlyOF7TM8Y3tn19KUdlq&login=silver&pass=MOto%26#10
There is something wrong with encoding & in the password. Though I have replaced it with percent encoding. Here is my code :
do {
let passwordString = self.convertSpecialCharacters(string: password.text!)
print("%#", passwordString)
let opt = try HTTP.GET(Constants.kLoginUrl, parameters: ["login": username.text!, "pass": passwordString])
opt.start { response in
if let err = response.error {
print("error: \(err.localizedDescription)")
return
}
print("opt finished: \(response.description)")
self.parseLoginResponse(response.data)
}
} catch _ as NSError {
}
And this is convertSpecialCharacters :
func convertSpecialCharacters(string: String) -> String {
var newString = string
let arrayEncode = ["&", "<", ">", "\"", "'", "-", "..."]
for (escaped_char) in arrayEncode {
newString = newString.encode(escaped_char)
}
return newString
}
Extension for encoding :
extension String {
func encode(_ chars: String) -> String
{
let forbidden = CharacterSet(charactersIn: chars)
return self.addingPercentEncoding(withAllowedCharacters: forbidden.inverted) ?? self
}
}
A suitable way is to use URLComponents which handles all percent encoding:
var urlComponents = URLComponents(string: "https://server/api/check-access/by-login-pass")!
let queryItems = [URLQueryItem(name:"_key", value:"wlyOF7TM8Y3tn19KUdlq"),
URLQueryItem(name:"login", value:"silver"),
URLQueryItem(name:"pass", value:"MOto
")]
urlComponents.queryItems = queryItems
let url = urlComponents.url
print(url) // "https://server/api/check-access/by-login-pass?_key=wlyOF7TM8Y3tn19KUdlq&login=silver&pass=MOto%26#10"
PS: I totally agree with luk2302's comment.
I've decided to go full custom for my GET-request, since everything else didn't want to work and got me angry. I used the request for something different though, like getting a list from our server. The login is done via POST requests which was easier.
However, to stick with GET-requests:
I needed characters like "+" and "/" encoded...
First I couldn't get the "+" encoded with the "stringByAddingPercentEncodingWithAllowedCharacters" method.
So I have built my own extension for String:
extension String
{
return CFURLCreateStringByAddingPercentEscapes(
nil,
self as CFString,
nil,
"!*'();:#&=+$,/?%#[]" as CFString,
CFStringBuiltInEncodings.UTF8.rawValue
) as String
}
2nd step was to add this to my url for the final request. I wanted to use URLQueryItems and add them to the url by using url.query or url.queryItems. Bad surprise: My already correctly encoded string got encoded again and every "%" inside of it became "%25" making it invalid. -.-
So now I have appended each encoded value to a string which will be added to the url. Doesn't feel very "swift" but ok..
let someUrl = "https://example.com/bla/bla.json"
var queryString = ""
var index = 0
// dicParam is of type [String: AnyObject] with all needed keys and values for the query
for (key, value) in dicParam
{
let encodedValue = (value as! String).encodeUrl()
if index != 0
{
queryString.append("&\(key)=\(encodedValue)")
}
else
{
queryString.append("?\(key)=\(encodedValue)")
}
index += 1
}
let url = URLComponents(string: someUrl + queryString)!
Hope this helps someone and saves a few hours :/
Using Xcode 6.4 and programming in swift.
I am typing a program that should read in JSON from a URL. A sample of the JSON can be found at this URL (https://itunes.apple.com/us/rss/topmovies/limit=2/json) and Ive been using this site to parse the JSON in order to read it better (http://json.parser.online.fr/).
Now I need to work through the levels of the JSON in order to get to
the actual movie names and image URL's but I am lost at what kind of variable entryDictionary should be. I was thinking it should be an array of dictionaries, and this compiles, but the output of entryDictionary in the console is sloppy looking, starting with Optionl( and not entry{ as it should. And when I go to loop through entryDictionary, I get an error saying entry does not have a subscript of type AnyObject.
So I am asking how I retrieve the im:name fields and im:image from the JSON.
func downloadDataFromURLString(urlString: String) {
//Downloaded data and is now stored in data. Took code out because
//irrelevant to my problem at this point. Data variable has correct
//JSON, now I am trying to parse it.
} else { //download suceeded, time to parse
var error: NSError? = nil
var names = [String]()
if let rootDictionary = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? [String: AnyObject] {
let feedDictionary = rootDictionary["feed"] as! [String: AnyObject]
let entryDictionary: AnyObject? = feedDictionary["entry"]
println(entryDictionary) //For debugging
//for entry in entryDictionary as! NSArray {
// let name = entryDictionary["name"]
// let image = entryDictionary["image"]
// let movie = Movie(name: name!, image: image!)
// weakSelf!.movies.append(movie)
//}
here is a blueprint of the JSON
"feed":{
"author":{},
"entry":[
{
"im:name":{
"label":"Deadpool"
},
"im:image":[],
"summary":{},
"im:price":{},
"im:contentType":{},
"rights":{},
"title":{},
"link":[],
"id":{},
"im:artist":{},
"category":{},
"im:releaseDate":{}
AnyObject is indeed not subscriptable (you're trying to subscript a variable whose type is AnyObject? with ["feed"]). You should also avoid casting to Cocoa container types like NSArray and NSDictionary whenever you can. Here's an example of how you might get the labels out of the entries array's names array:
import Foundation
func labels(feedDictionary:[String:AnyObject]) -> [String] {
guard let entries = feedDictionary["entry"] as? [String:AnyObject] else {
return []
}
return entries.flatMap { (key:String, value:AnyObject) -> String? in
guard key == "im:name" else {
return nil
}
guard let name = value as? [String:String] else {
return nil
}
return name["label"]
}
}
I'd however advise against using NSJSONSerialization on its own in Swift for anything but the simplest case, as you end up casting and wrapping optionals until the cows come home.
There are good 3rd party libraries such as Freddy and SwiftyJSON which apply Swift language features to accomplish a very convenient JSON (de)serialization experience.
For instance with Freddy you could express your problem in the following style:
let json = try JSON(data: data)
json.decode("feed", type:Feed.self)
struct Feed: JSONDecodable {
let entries:[Entry]
init(json: JSON) throws {
self.entries = try json.arrayOf("entry", type:Entry.self)
}
}
struct Entry:JSONDecodable {
let name:IMName
init(json: JSON) throws {
self.name = try json.decode("im:name", type:IMName.self)
}
}
struct IMName:JSONDecodable {
let label:String
init(json: JSON) throws {
self.label = try json.string("label")
}
}
I am using Swift 1.2 to develop my iPhone application and I am communicating with a http web service.
The response I am getting is in query string format (key-value pairs) and URL encoded in .Net.
I can get the response, but looking the proper way to decode using Swift.
Sample response is as follows
status=1&message=The+transaction+for+GBP+12.50+was+successful
Tried following way to decode and get the server response
// This provides encoded response String
var responseString = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
var decodedResponse = responseString.stringByReplacingEscapesUsingEncoding(NSUTF8StringEncoding)!
How can I replace all URL escaped characters in the string?
To encode and decode urls create this extention somewhere in the project:
Swift 2.0
extension String
{
func encodeUrl() -> String
{
return self.stringByAddingPercentEncodingWithAllowedCharacters( NSCharacterSet.URLQueryAllowedCharacterSet())
}
func decodeUrl() -> String
{
return self.stringByRemovingPercentEncoding
}
}
Swift 3.0
extension String
{
func encodeUrl() -> String
{
return self.addingPercentEncoding( withAllowedCharacters: NSCharacterSet.urlQueryAllowed())
}
func decodeUrl() -> String
{
return self.removingPercentEncoding
}
}
Swift 4.1
extension String
{
func encodeUrl() -> String?
{
return self.addingPercentEncoding( withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
}
func decodeUrl() -> String?
{
return self.removingPercentEncoding
}
}
Swift 2 and later (Xcode 7)
var s = "aa bb -[:/?&=;+!##$()',*]";
let sEncode = s.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
let sDecode = sEncode?.stringByRemovingPercentEncoding
You only need:
print("Decode: ", yourUrlAsString.removingPercentEncoding)
The stringByReplacingEscapesUsingEncoding method is behaving correctly. The "+" character is not part of percent-encoding. This server is using it incorrectly; it should be using a percent-escaped space here (%20). If, for a particular response, you want spaces where you see "+" characters, you just have to work around the server behavior by performing the substitution yourself, as you are already doing.
It's better to use built-in URLComponents struct, since it follows proper guidelines.
extension URL
{
var parameters: [String: String?]?
{
if let components = URLComponents(url: self, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems
{
var parameters = [String: String?]()
for item in queryItems {
parameters[item.name] = item.value
}
return parameters
} else {
return nil
}
}
}
In my case, I NEED a plus ("+") signal in a phone number in parameters of a query string, like "+55 11 99999-5555". After I discovered that the swift3 (xcode 8.2) encoder don't encode "+" as plus signal, but space, I had to appeal to a workaround after the encode:
Swift 3.0
_strURL = _strURL.replacingOccurrences(of: "+", with: "%2B")
In Swift 3
extension URL {
var parseQueryString: [String: String] {
var results = [String: String]()
if let pairs = self.query?.components(separatedBy: "&"), pairs.count > 0 {
for pair: String in pairs {
if let keyValue = pair.components(separatedBy: "=") as [String]? {
results.updateValue(keyValue[1], forKey: keyValue[0])
}
}
}
return results
}
}
in your code to access below
let parse = url.parseQueryString
print("parse \(parse)" )