Can I use generics to cast NSNumber to T in swift? - swift

What I want
func safeGet<T>() -> T {
let value = magic()
if let typedValue = value as? T {
return typedValue
}
}
The reason this doesn't work is the fact that you can't do <NSNumber> as Int in swift
What do I put in the <what do I put here?> placeholder?
func safeGet<T>() -> T {
let value = magic()
if let typedValue = value as? NSNumber {
return <what do I put here?>
} else if let typedValue = value as? T {
return typedValue
}
}

In the end I did this to be able to get typed values from the dictionary in a way that throws errors. It's used like this
let name:String = dictionary.safeGet("name")
or
let name = dictionary.safeGet("name") as String`
The source code:
import Foundation
extension Dictionary {
func safeGet<T>(key:Key) throws -> T {
if let value = self[key] as? AnyObject {
if let typedValue = value as? T {
return typedValue
}
let typedValue: T? = parseNumber(value)
if typedValue != nil {
return typedValue!
}
let typeData = Mirror(reflecting: value)
throw generateNSError(
domain: "DictionaryError.WrongType",
message: "Could not convert `\(key)` to `\(T.self)`, it was `\(typeData.subjectType)` and had the value `\(value)`"
)
} else {
throw generateNSError(
domain: "DictionaryError.MissingValue",
message: "`\(key)` was not in dictionary. The dictionary was:\n\(self.description)"
)
}
}
private func parseNumber<T>(value: AnyObject) -> T? {
if Int8.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.charValue as? T
} else if let stringValue = value as? String {
return Int8(stringValue) as? T
}
} else if Int16.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.shortValue as? T
} else if let stringValue = value as? String {
return Int16(stringValue) as? T
}
} else if Int32.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.longValue as? T
} else if let stringValue = value as? String {
return Int32(stringValue) as? T
}
} else if Int64.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.longLongValue as? T
} else if let stringValue = value as? String {
return Int64(stringValue) as? T
}
} else if UInt8.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.unsignedCharValue as? T
} else if let stringValue = value as? String {
return UInt8(stringValue) as? T
}
} else if UInt16.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.unsignedShortValue as? T
} else if let stringValue = value as? String {
return UInt16(stringValue) as? T
}
} else if UInt32.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.unsignedIntValue as? T
} else if let stringValue = value as? String {
return UInt32(stringValue) as? T
}
} else if UInt64.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.unsignedLongLongValue as? T
} else if let stringValue = value as? String {
return UInt64(stringValue) as? T
}
} else if Double.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.doubleValue as? T
} else if let stringValue = value as? String {
return Double(stringValue) as? T
}
} else if Float.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.floatValue as? T
} else if let stringValue = value as? String {
return Float(stringValue) as? T
}
} else if String.self == T.self {
if let numericValue = value as? NSNumber {
return numericValue.stringValue as? T
} else if let stringValue = value as? String {
return stringValue as? T
}
}
return nil
}
private func generateNSError(domain domain: String, message: String) -> NSError {
return NSError(
domain: domain,
code: -1,
userInfo: [
NSLocalizedDescriptionKey: message
])
}
}

Related

How to encode/decode [CKRecordZone.ID: CKServerChangeToken]?

public var zonesChangeToken: [CKRecordZone.ID: CKServerChangeToken]? {
get {
if(backingPreviousZonesChangeToken == nil) {
guard let defaults: UserDefaults = UserDefaults(suiteName: CloudKitHandler.APP_GROUP_ID) else { return nil }
guard let data = defaults.data(forKey: CloudKitHandler.CK_PREVIOUS_ZONES_CHANGE_TOKEN)
else { return [CKRecordZone.ID: CKServerChangeToken]() }
do {
let unarchiver: NSKeyedUnarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
unarchiver.requiresSecureCoding = true
backingPreviousZonesChangeToken = try unarchiver.decodeTopLevelObject() as? [CKRecordZone.ID: CKServerChangeToken]
} catch { }
}
return backingPreviousZonesChangeToken
}
set(value) {
backingPreviousZonesChangeToken = value
guard let value = value else { return }
guard let defaults: UserDefaults = UserDefaults(suiteName: CloudKitHandler.APP_GROUP_ID) else { return }
let archiver: NSKeyedArchiver = NSKeyedArchiver(requiringSecureCoding: true)
archiver.encode(value)
archiver.finishEncoding()
defaults.setValue(archiver.encodedData, forKey: CloudKitHandler.CK_PREVIOUS_ZONES_CHANGE_TOKEN)
}
}
I'm trying to encode/decode a dictionary of IDs and Tokens. But for some reason the decode always gives me a nil.
How to fix?
extension CKServerChangeToken {
func dataRepresentation() -> Data {
let coder = NSKeyedArchiver.init(requiringSecureCoding: true)
coder.requiresSecureCoding = true
self.encode(with: coder)
coder.finishEncoding()
return coder.encodedData
}
class func token(data: Data) -> CKServerChangeToken? {
do{
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
coder.requiresSecureCoding = true
let record = CKServerChangeToken(coder: coder)
coder.finishDecoding()
return record
} catch {
print(error)
}
return nil
}
}

Swift firebase search is one reload behind

I have a search function in my app which works perfectly except for the fact that it seems to be one reload behind. For example if I type in 'S' nothing will show but if I then type an 'a' after all the results for 'S' will show up. I tried adding collectionView reload at the end of the fetch function but if I do that nothing shows up period.
#objc func textFieldDidChange(_ textField: UITextField) {
guard let text = textField.text else { return }
if text.count == 0 {
self.posts.removeAll()
self.filteredPosts.removeAll()
} else {
fetchSearchedPosts(searchTerm: text)
}
self.collectionView.reloadData()
}
func fetchSearchedPosts(searchTerm: String) {
self.collectionView.refreshControl?.endRefreshing()
let ref = Database.database().reference().child("posts").queryOrdered(byChild: "title").queryStarting(atValue: searchTerm).queryEnding(atValue: "\(searchTerm)\u{f8ff}")
ref.observeSingleEvent(of: .value) { (snapshot) in
if !snapshot.exists() { return }
guard let dictionaries = snapshot.value as? [String: Any] else { return }
self.posts.removeAll()
dictionaries.forEach({ (key, value) in
guard let postDictionary = value as? [String: Any] else { return }
guard let uid = postDictionary["uid"] as? String else { return }
Database.fetchUserWithUID(uid: uid, completion: { (user) in
let post = Post(postId: key, user: user, dictionary: postDictionary)
let nowTimeStamp = Date().timeIntervalSince1970
let dateTime = post.endTimeDate
let timeStamp = dateTime.timeIntervalSince1970
if nowTimeStamp < timeStamp {
post.id = key
self.posts.append(post)
} else {
return
}
})
self.posts.sort(by: { (post1, post2) -> Bool in
return post1.title.compare(post2.title) == .orderedAscending
})
})
self.collectionView.reloadData()
}
}
You need a DispatchGroup as you have nested asynchronous calls
func fetchSearchedPosts(searchTerm: String) {
self.collectionView.refreshControl?.endRefreshing()
let ref = Database.database().reference().child("posts").queryOrdered(byChild: "title").queryStarting(atValue: searchTerm).queryEnding(atValue: "\(searchTerm)\u{f8ff}")
ref.observeSingleEvent(of: .value) { (snapshot) in
if !snapshot.exists() { return }
guard let dictionaries = snapshot.value as? [String: Any] else { return }
self.posts.removeAll()
let g = DispatchGroup() ///// 1
dictionaries.forEach({ (key, value) in
guard let postDictionary = value as? [String: Any] else { return }
guard let uid = postDictionary["uid"] as? String else { return }
g.enter() ///// 2
Database.fetchUserWithUID(uid: uid, completion: { (user) in
let post = Post(postId: key, user: user, dictionary: postDictionary)
let nowTimeStamp = Date().timeIntervalSince1970
let dateTime = post.endTimeDate
let timeStamp = dateTime.timeIntervalSince1970
if nowTimeStamp < timeStamp {
post.id = key
self.posts.append(post)
} else {
g.leave() ///// 3.a
return
}
g.leave() ///// 3.b
})
})
g.notify(queue:.main) { ///// 4
self.posts.sort(by: { (post1, post2) -> Bool in
return post1.title.compare(post2.title) == .orderedAscending
})
self.collectionView.reloadData()
}
}
}

Swift 4 Tuple Iteration

I am trying to retrieve wallpapers from Bing by json in Swift 4
https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8&mkt=en-US
But images property is a tuple with another objects. I can not iterate this tuple and reach the url values so that I can collect them in an array.
(
{
bot = 1;
copyright = "For the winter solstice, Santa Fe's Farolito Walk (\U00a9 Julien McRoberts/Danita Delimont)";
copyrightlink = "http://www.bing.com/search?q=santa+fe+new+mexico&form=hpcapt&filters=HpDate:%2220181221_0800%22";
drk = 1;
enddate = 20181222;
fullstartdate = 201812210800;
hs = (
);
hsh = 6ba85f1fbab1b9a290a19af763ca404d;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181221_AdobeSantaFe%22&FORM=HPQUIZ";
startdate = 20181221;
title = "All is bright in Santa Fe";
top = 1;
url = "/az/hprichbg/rb/AdobeSantaFe_EN-US4037753534_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/AdobeSantaFe_EN-US4037753534";
wp = 1;
},
{
bot = 1;
copyright = "Nabana-no-Sato gardens at Nagashima Spa Land in Kuwana, Japan (\U00a9 Julian Krakowiak/Alamy)";
copyrightlink = "http://www.bing.com/search?q=nabana+no+sato+nagashima+spa+land&form=hpcapt&filters=HpDate:%2220181220_0800%22";
drk = 1;
enddate = 20181221;
fullstartdate = 201812200800;
hs = (
);
hsh = eda366ca6eee5c653a59d8f16a54ae63;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181220_WinterIllumination%22&FORM=HPQUIZ";
startdate = 20181220;
title = "Winter illuminations in Nabana-no-Sato";
top = 1;
url = "/az/hprichbg/rb/WinterIllumination_EN-US0071328313_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/WinterIllumination_EN-US0071328313";
wp = 0;
},
{
bot = 1;
copyright = "The Charles Bridge in Prague, Czech Republic (\U00a9 borchee/E+/Getty Images)";
copyrightlink = "http://www.bing.com/search?q=prague&form=hpcapt&filters=HpDate:%2220181219_0800%22";
drk = 1;
enddate = 20181220;
fullstartdate = 201812190800;
hs = (
);
hsh = 37c19c3f45952fd81c429e5e92c386e9;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181219_PragueChristmas%22&FORM=HPQUIZ";
startdate = 20181219;
title = "Snow falls on Bohemia";
top = 1;
url = "/az/hprichbg/rb/PragueChristmas_EN-US8649790921_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/PragueChristmas_EN-US8649790921";
wp = 1;
},
{
bot = 1;
copyright = "For the anniversary of the premiere of 'The Nutcracker,' a scene of the Moscow Ballet performing the popular dance (\U00a9 Tytus Zmijewski/Epa/Shutterstock)";
copyrightlink = "http://www.bing.com/search?q=the+nutcracker&form=hpcapt&filters=HpDate:%2220181218_0800%22";
drk = 1;
enddate = 20181219;
fullstartdate = 201812180800;
hs = (
);
hsh = b6303e82458de5692e94b26cb332fbd1;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181218_NutcrackerSeason%22&FORM=HPQUIZ";
startdate = 20181218;
title = "A holiday tradition is born";
top = 1;
url = "/az/hprichbg/rb/NutcrackerSeason_EN-US8373379424_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/NutcrackerSeason_EN-US8373379424";
wp = 0;
},
{
bot = 1;
copyright = "Wilbur Wright gliding down Big Kill Devil Hill in Kitty Hawk, North Carolina (\U00a9 Library of Congress)";
copyrightlink = "http://www.bing.com/search?q=wright+brothers+first+flight&form=hpcapt&filters=HpDate:%2220181217_0800%22";
drk = 1;
enddate = 20181218;
fullstartdate = 201812170800;
hs = (
);
hsh = 5a132d06c5e7b66f0a1ec9f434a0dca1;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181217_WrightGlider%22&FORM=HPQUIZ";
startdate = 20181217;
title = "Wright brothers fly into history";
top = 1;
url = "/az/hprichbg/rb/WrightGlider_EN-US10185286591_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/WrightGlider_EN-US10185286591";
wp = 0;
},
{
bot = 1;
copyright = "Holiday decorations on a canal in Murano, Italy (\U00a9 John Warburton-Lee/DanitaDelimont.com)";
copyrightlink = "http://www.bing.com/search?q=murano+island&form=hpcapt&filters=HpDate:%2220181216_0800%22";
drk = 1;
enddate = 20181217;
fullstartdate = 201812160800;
hs = (
);
hsh = c4cc26f7a803502632d940f9466e2b7c;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181216_MuranoChristmas%22&FORM=HPQUIZ";
startdate = 20181216;
title = "Murano aglow";
top = 1;
url = "/az/hprichbg/rb/MuranoChristmas_EN-US10759540271_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/MuranoChristmas_EN-US10759540271";
wp = 1;
},
{
bot = 1;
copyright = "The Stoneman Bridge on the Merced River in Yosemite National Park (\U00a9 Ron_Thomas/E+/Getty Images)";
copyrightlink = "http://www.bing.com/search?q=yosemite+national+park&form=hpcapt&filters=HpDate:%2220181215_0800%22";
drk = 1;
enddate = 20181216;
fullstartdate = 201812150800;
hs = (
);
hsh = 6a49f8fc62b09ce83305ac0a13000a7a;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181215_YosemiteBridge%22&FORM=HPQUIZ";
startdate = 20181215;
title = "Season of solitude in Yosemite";
top = 1;
url = "/az/hprichbg/rb/YosemiteBridge_EN-US10544416282_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/YosemiteBridge_EN-US10544416282";
wp = 1;
},
{
bot = 1;
copyright = "A female northern cardinal (\U00a9 Matthew Studebaker/Minden Pictures)";
copyrightlink = "http://www.bing.com/search?q=northern+cardinal&form=hpcapt&filters=HpDate:%2220181214_0800%22";
drk = 1;
enddate = 20181215;
fullstartdate = 201812140800;
hs = (
);
hsh = 0dcf20ffcfd5413167161ad20b412fb5;
quiz = "/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20181214_CardinalBerries%22&FORM=HPQUIZ";
startdate = 20181214;
title = "The Christmas Bird Count begins";
top = 1;
url = "/az/hprichbg/rb/CardinalBerries_EN-US11262203078_1920x1080.jpg";
urlbase = "/az/hprichbg/rb/CardinalBerries_EN-US11262203078";
wp = 1;
}
)
How can I handle this JSON tuple in Swift 4?
That's not a tuple.
That's how a NSArray is printed (that's the description implementation result). It's more Objective-C like.
(
content...
)
With force unwrap (use of !) that I'd strongly recommend to not use in production, and use if let/guard let instead (because it will cause a crash if it fails the unwrap). I used force unwrap to be more direct, that's all, show the logic.
let dataJSON = initialJSONData
//Your JSON is a Dictionary at top level
let json = try! JSONSerialization.jsonObject(with: dataJSON, options: []) as! [String: Any]
//The value of a images key is an Array of Dictionaries
let imagesArray = json["images"] as! [[String: Any]]
let imagesURLStr = imagesArray.flatMap{ $0["url"] as? String }
Now, if you use Swift 4+, I'd recommend to use Codable:
struct Result: Codable {
let images: [Image]
struct Image: Codable {
let url: String
}
}
To call it:
let jsonDecoder = JSONDecoder()
let result = try! jsonDecoder.decode(Result.self, from: dataJSON)
let images = result.images
let imagesURLStr = images.map{ $0.url }
Side Note:
This code is not tested, written only here, so there might be a typo error, a minor compiler issue but it shouldn't be un-fixable.
With the help of quicktype.io, let's use these structs :
struct Result: Codable {
let images: [Image]
let tooltips: Tooltips
}
struct Image: Codable {
let startdate, fullstartdate, enddate, url: String
let urlbase, copyright: String
let copyrightlink: String
let title, quiz: String
let wp: Bool
let hsh: String
let drk, top, bot: Int
let hs: [JSONAny]
}
struct Tooltips: Codable {
let loading, previous, next, walle: String
let walls: String
}
// MARK: Encode/decode helpers
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
class JSONCodingKey: CodingKey {
let key: String
required init?(intValue: Int) {
return nil
}
required init?(stringValue: String) {
key = stringValue
}
var intValue: Int? {
return nil
}
var stringValue: String {
return key
}
}
class JSONAny: Codable {
let value: Any
static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
return DecodingError.typeMismatch(JSONAny.self, context)
}
static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
return EncodingError.invalidValue(value, context)
}
static func decode(from container: SingleValueDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if container.decodeNil() {
return JSONNull()
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if let value = try? container.decodeNil() {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer() {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
if let value = try? container.decode(Bool.self, forKey: key) {
return value
}
if let value = try? container.decode(Int64.self, forKey: key) {
return value
}
if let value = try? container.decode(Double.self, forKey: key) {
return value
}
if let value = try? container.decode(String.self, forKey: key) {
return value
}
if let value = try? container.decodeNil(forKey: key) {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer(forKey: key) {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
var arr: [Any] = []
while !container.isAtEnd {
let value = try decode(from: &container)
arr.append(value)
}
return arr
}
static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
var dict = [String: Any]()
for key in container.allKeys {
let value = try decode(from: &container, forKey: key)
dict[key.stringValue] = value
}
return dict
}
static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
for value in array {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer()
try encode(to: &container, array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
try encode(to: &container, dictionary: value)
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
}
static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
for (key, value) in dictionary {
let key = JSONCodingKey(stringValue: key)!
if let value = value as? Bool {
try container.encode(value, forKey: key)
} else if let value = value as? Int64 {
try container.encode(value, forKey: key)
} else if let value = value as? Double {
try container.encode(value, forKey: key)
} else if let value = value as? String {
try container.encode(value, forKey: key)
} else if value is JSONNull {
try container.encodeNil(forKey: key)
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer(forKey: key)
try encode(to: &container, array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
try encode(to: &container, dictionary: value)
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
}
static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
public required init(from decoder: Decoder) throws {
if var arrayContainer = try? decoder.unkeyedContainer() {
self.value = try JSONAny.decodeArray(from: &arrayContainer)
} else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
self.value = try JSONAny.decodeDictionary(from: &container)
} else {
let container = try decoder.singleValueContainer()
self.value = try JSONAny.decode(from: container)
}
}
public func encode(to encoder: Encoder) throws {
if let arr = self.value as? [Any] {
var container = encoder.unkeyedContainer()
try JSONAny.encode(to: &container, array: arr)
} else if let dict = self.value as? [String: Any] {
var container = encoder.container(keyedBy: JSONCodingKey.self)
try JSONAny.encode(to: &container, dictionary: dict)
} else {
var container = encoder.singleValueContainer()
try JSONAny.encode(to: &container, value: self.value)
}
}
}
We can get the url properties this way:
//Create the URL
guard let url = URL(string: "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8&mkt=en-US") else {
fatalError("Invalid URL")
}
//Fetch the data from the URL
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) -> Void in
guard let jsonData = data, error == nil else {
fatalError("Data error")
}
let jsonDecoder = JSONDecoder()
do {
let result = try jsonDecoder.decode(Result.self, from: jsonData)
let images = result.images
let urls = images.map { $0.url }
urls.forEach { print($0) }
}
catch {
print(error)
}
}).resume()
This prints:
/az/hprichbg/rb/AdobeSantaFe_EN-US4037753534_1920x1080.jpg
/az/hprichbg/rb/WinterIllumination_EN-US0071328313_1920x1080.jpg
/az/hprichbg/rb/PragueChristmas_EN-US8649790921_1920x1080.jpg
/az/hprichbg/rb/NutcrackerSeason_EN-US8373379424_1920x1080.jpg
/az/hprichbg/rb/WrightGlider_EN-US10185286591_1920x1080.jpg
/az/hprichbg/rb/MuranoChristmas_EN-US10759540271_1920x1080.jpg
/az/hprichbg/rb/YosemiteBridge_EN-US10544416282_1920x1080.jpg
/az/hprichbg/rb/CardinalBerries_EN-US11262203078_1920x1080.jpg

Convert to string an Any value

This fails (Non-nominal type 'Any' cannot be extended)
extension Any {
func literal() -> String {
if let booleanValue = (self as? Bool) {
return String(format: (booleanValue ? "true" : "false"))
}
else
if let intValue = (self as? Int) {
return String(format: "%d", intValue)
}
else
if let floatValue = (self as? Float) {
return String(format: "%f", floatValue)
}
else
if let doubleValue = (self as? Double) {
return String(format: "%f", doubleValue)
}
else
{
return String(format: "<%#>", self)
}
}
}
as I would like to use it in a dictionary (self) to xml string factory like
extension Dictionary {
// Return an XML string from the dictionary
func xmlString(withElement element: String, isFirstElement: Bool) -> String {
var xml = String.init()
if isFirstElement { xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n") }
xml.append(String(format: "<%#>\n", element))
for node in self.keys {
let value = self[node]
if let array: Array<Any> = (value as? Array<Any>) {
xml.append(array.xmlString(withElement: node as! String, isFirstElemenet: false))
}
else
if let dict: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>) {
xml.append(dict.xmlString(withElement: node as! String, isFirstElement: false))
}
else
{
xml.append(String(format: "<%#>", node as! CVarArg))
xml.append((value as Any).literal
xml.append(String(format: "</%#>\n", node as! CVarArg))
}
}
xml.append(String(format: "</%#>\n", element))
return xml.replacingOccurrences(of: "&", with: "&amp", options: .literal, range: nil)
}
}
I was trying to reduce the code somehow, as the above snippet is repeated a few times in a prototype I'm building but this is not the way to do it (a working copy with the snippet replicated works but ugly?).
Basically I want to generate a literal for an Any value - previously fetched from a dictionary.
It seems like you can't add extensions to Any. You do have some other options though - either make it a function toLiteral(value: Any) -> String, or what is probably a neater solution; use the description: String attribute which is present on all types that conform to CustomStringConvertible, which includes String, Int, Bool, and Float - your code would be simplified down to just xml.append(value.description). You then just have make a simple implementation for any other types that you might get.
Ok, finally got this working. First the preliminaries: each of your objects needs to have a dictionary() method to marshal itself. Note: "k.###" are struct static constants - i.e., k.name is "name", etc. I have two objects, a PlayItem and a PlayList:
class PlayItem : NSObject {
var name : String = k.item
var link : URL = URL.init(string: "http://")!
var time : TimeInterval
var rank : Int
var rect : NSRect
var label: Bool
var hover: Bool
var alpha: Float
var trans: Int
var temp : String {
get {
return link.absoluteString
}
set (value) {
link = URL.init(string: value)!
}
}
func dictionary() -> Dictionary<String,Any> {
var dict = Dictionary<String,Any>()
dict[k.name] = name
dict[k.link] = link.absoluteString
dict[k.time] = time
dict[k.rank] = rank
dict[k.rect] = NSStringFromRect(rect)
dict[k.label] = label ? 1 : 0
dict[k.hover] = hover ? 1 : 0
dict[k.alpha] = alpha
dict[k.trans] = trans
return dict
}
}
class PlayList : NSObject {
var name : String = k.list
var list : Array <PlayItem> = Array()
func dictionary() -> Dictionary<String,Any> {
var dict = Dictionary<String,Any>()
var items: [Any] = Array()
for item in list {
items.append(item.dictionary())
}
dict[k.name] = name
dict[k.list] = items
return dict
}
}
Note any value so marshal has to be those legal types for a dictionary; it helps to have aliases so in the PlayItem a "temp" is the string version for the link url, and its getter/setter would translate.
When needed, like the writeRowsWith drag-n-drop tableview handler, I do this:
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
if tableView == playlistTableView {
let objects: [PlayList] = playlistArrayController.arrangedObjects as! [PlayList]
var items: [PlayList] = [PlayList]()
var promises = [String]()
for index in rowIndexes {
let item = objects[index]
let dict = item.dictionary()
let promise = dict.xmlString(withElement: item.className, isFirstElement: true)
promises.append(promise)
items.append(item)
}
let data = NSKeyedArchiver.archivedData(withRootObject: items)
pboard.setPropertyList(data, forType: PlayList.className())
pboard.setPropertyList(promises, forType:NSFilesPromisePboardType)
pboard.writeObjects(promises as [NSPasteboardWriting])
}
else
{
let objects: [PlayItem] = playitemArrayController.arrangedObjects as! [PlayItem]
var items: [PlayItem] = [PlayItem]()
var promises = [String]()
for index in rowIndexes {
let item = objects[index]
let dict = item.dictionary()
let promise = dict.xmlString(withElement: item.className, isFirstElement: true)
promises.append(promise)
items.append(item)
}
let data = NSKeyedArchiver.archivedData(withRootObject: items)
pboard.setPropertyList(data, forType: PlayList.className())
pboard.setPropertyList(promises, forType:NSFilesPromisePboardType)
pboard.writeObjects(promises as [NSPasteboardWriting])
}
return true
}
What makes this happen are these xmlString extensions and the toLiteral function - as you cannot extend "Any":
func toLiteral(_ value: Any) -> String {
if let booleanValue = (value as? Bool) {
return String(format: (booleanValue ? "1" : "0"))
}
else
if let intValue = (value as? Int) {
return String(format: "%d", intValue)
}
else
if let floatValue = (value as? Float) {
return String(format: "%f", floatValue)
}
else
if let doubleValue = (value as? Double) {
return String(format: "%f", doubleValue)
}
else
if let stringValue = (value as? String) {
return stringValue
}
else
if let dictValue: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>)
{
return dictValue.xmlString(withElement: "Dictionary", isFirstElement: false)
}
else
{
return ((value as AnyObject).description)
}
}
extension Array {
func xmlString(withElement element: String, isFirstElemenet: Bool) -> String {
var xml = String.init()
xml.append(String(format: "<%#>\n", element))
self.forEach { (value) in
if let array: Array<Any> = (value as? Array<Any>) {
xml.append(array.xmlString(withElement: "Array", isFirstElemenet: false))
}
else
if let dict: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>) {
xml.append(dict.xmlString(withElement: "Dictionary", isFirstElement: false))
}
else
{
xml.append(toLiteral(value))
}
}
xml.append(String(format: "<%#>\n", element))
return xml
}
}
extension Dictionary {
// Return an XML string from the dictionary
func xmlString(withElement element: String, isFirstElement: Bool) -> String {
var xml = String.init()
if isFirstElement { xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n") }
xml.append(String(format: "<%#>\n", element))
for node in self.keys {
let value = self[node]
if let array: Array<Any> = (value as? Array<Any>) {
xml.append(array.xmlString(withElement: node as! String, isFirstElemenet: false))
}
else
if let dict: Dictionary<AnyHashable,Any> = (value as? Dictionary<AnyHashable,Any>) {
xml.append(dict.xmlString(withElement: node as! String, isFirstElement: false))
}
else
{
xml.append(String(format: "<%#>", node as! CVarArg))
xml.append(toLiteral(value as Any))
xml.append(String(format: "</%#>\n", node as! CVarArg))
}
}
xml.append(String(format: "</%#>\n", element))
return xml
}
func xmlHTMLString(withElement element: String, isFirstElement: Bool) -> String {
let xml = self.xmlString(withElement: element, isFirstElement: isFirstElement)
return xml.replacingOccurrences(of: "&", with: "&amp", options: .literal, range: nil)
}
}
This continues another's solution, the toLiteral() suggestion above, in hopes it helps others.
Enjoy.

Casting AnyObject to Specific Class

I'm using socket.io Swift Library. With the following line of code,
socket.on("info") { (dataArray, socketAck) -> Void in
let user = dataArray[0] as? User
print(user._id)
}
dataArray[0] is a valid object but user appears to be nil after casting.
Since dataArray[0] returns as an AnyObject,
how can i cast AnyObject to User Object?. Or somehow manage to do what i want with a different approach?
Since after this line
let user = dataArray[0] as? User
you have a nil value inside user it means that you don't have a User value at the first position of dataArray.
Since dataArray comes from a server (as I guess) it probably contains a serialized version of User.
Now we really need to know what really dataArray[0] is. However...
if dataArray[0] contains NSData
In this case try this
let json = JSON(dataArray[0] as! NSData)
let user = User(json:json)
You need to create a constructor that accept AnyObject and read data in it.
I guess in this case dataArray[0] is an JSON Object.
class User {
init(data: [String: AnyObject]) {
username = data["username"] as? String ?? ""
}
}
This is how i manage mine:
// Structure used as parameter
struct InfoStruct {
var nome: String = ""
var sobrenome:String = ""
var nascimentoTimestamp: NSNumber = 0
init() {
}
// Struct to AnyObject
func toAnyObject() -> Any {
var dic = [String:AnyObject?]()
if (nome != "") { dic["nome"] = nome as AnyObject }
if (sobrenome != "") { dic["sobrenome"] = sobrenome as AnyObject }
if (nascimentoTimestamp != 0) { dic["nascimentoTimestamp"] = nascimentoTimestamp as AnyObject }
return dic
}
// AnyObject to Struct
func fromAnyObject(dic:[String:AnyObject]) -> InfoStruct {
var retorno = InfoStruct()
if (dic["nome"] != nil) { retorno.nome = dic["nome"] as? String ?? "" }
if (dic["sobrenome"] != nil) { retorno.sobrenome = dic["sobrenome"] as? String ?? "" }
if (dic["nascimentoTimestamp"] != nil) { retorno.nascimentoTimestamp = dic["nascimentoTimestamp"] as? NSNumber ?? 0 }
return retorno
} }
// User class
class Usuario: NSObject {
var key: String
var admin: Bool
var info: InfoStruct // Struct parameter
init(_ key: String?) {
self.key = key ?? ""
admin = false
info = InfoStruct() // Initializing struct
}
// From Class to AnyObject
func toAnyObject() -> Any {
var dic = [String:AnyObject?]()
if (key != "") { dic["key"] = key as AnyObject }
if (admin != false) { dic["admin"] = admin as AnyObject }
dic["info"] = info.toAnyObject() as AnyObject // Struct
return dic
}
// From AnyObject to Class
func fromAnyObject(dic:[String:AnyObject]) -> Usuario {
let retorno = Usuario(dic["key"] as? String)
if (dic["key"] != nil) { retorno.key = dic["key"] as? String ?? "" }
if (dic["admin"] != nil) { retorno.admin = dic["admin"] as? Bool ?? false }
if (dic["info"] != nil) { retorno.info = InfoStruct.init().fromAnyObject(dic: dic["info"] as! [String : AnyObject]) } // Struct
return retorno
} }
// Using
let dao = FirebaseDAO.init(_type: FirebaseDAOType.firebaseDAOTypeUser)
dao.loadValue(key: uid) { (error, values:[String : AnyObject]) in
if error == nil {
let user = Usuario(values["key"] as? String).fromAnyObject(dic: values)
}
}
I hope it helps!