Swift[String: AnyObject] not convertible to T Swift Arrays - ios

I am working inside a Swift Extension. I am trying to append data to an array of the type [[String: AnyObject]]. The reason that this is in an extension is because I have to do this lot's of times to lot's of arrays. The problem is, when I append an object of type: [String: AnyObject], I get the error: Dictionary'<'String, AnyObject'>' Not Convertible to T (the quotes are there because within the carrots nothing showed up).
mutating func appendData(data: [String: [String: AnyObject]]?) {
if data != nil {
for (id, object) in data! {
var mutatingObject = object
mutatingObject["id"] = id
append(mutatingObject)
}
}
}

I am not certain what exactly are you trying to achieve. but take a note - Arrays are generic collections that store specific type. Extension for Array might not know what type will be used in each case, so it cannot simply allow you to store Dictionary<String, AnyObject>.
Here is an example on how to make your code more generic:
extension Array {
mutating func appendData(data: [String: T]?) {
if data != nil {
for (id, object) in data! {
if var mutatingObject = object as? [String : AnyObject] {
mutatingObject["id"] = id
}
append(object)
}
}
}
}

Related

I'm only getting one object back from a Get Request. How to retrieve all of it?

I'm only getting one object back from a Get Request. How do I retrieve all of them? Here is my Data Model.
class DataClass {
// MARK VARIABLES
init(price: Double, title: String, firstImage: String, allImages: [String]) {
_price = Price
_title = title
_allImages = allImages
_firstImg = firstImage
}
var _title: String!
var _firstImg: String!
var _allImages: [String]!
var _propertyPrice: Double!
func downloadHandMProperties(completed: #escaping downloadComplete) {
Alamofire.request(propertyListing).responseJSON { response in
if let result = response.result.value {
let dict = JSON(result)
if let data = dict["data"].dictionary {
if let listingResultDict = data["listings"]?.array {
for list in listingResultDict {
if let propertyName = list["data"]["name"].string {
self._title = propertyName
/etc..
// I parsed all of the data and passed them to the variables.
completed()
}
}
Here is the ViewController from where I'm receiving it.
Class ViewController: UIViewController {
let array = [DataClass]()
var property = DataClass(price: 0, title: "", firstImage: "", allImages: [])
override func viewDidLoad() {
super.viewDidLoad()
let data = DataClass(price: property.price, title: property.title, firstImage: property.firstImage, allImages: property.allImages)
self.array.append(data)
print(array)
}
}
There is only one object in that array. How do I get all of them. I thought of looping through the results, but I can only loop through Arrays and Dictionaries, and the Object is neither. Any suggestions as to how to retrieve all of the objects and put them in to the array?
Try casting your result object as collection type that you can loop through. You'll have to figure out what you can cast your result object as with some trial and error but it could look something like this:
Alamofire.request(propertyListing).responseJSON { response in
if let result = response.result.value as? [[String: Any]] {
//here the result object is casted as an array of [String: Any] dictionaries
// I parsed all of the data and passed them to the variables.
}
completed()
}
Then you could loop through the array of dictionaries since each dictionary would represent one of your objects.
What you could do to test out what type you could cast your result object as is add a break point on the if let result = response.result.value { line of code and then run po response.result.value as? [[String: Any]] in the xcode console. If you get a good looking result printed out you're set!

Swift--Reading in JSON file

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")
}
}

Filtering NSDictionary with empty strings in Swift and iOS Support >= 8.0

I have a Dictionary that contains values in such a way that some values are empty string.
let fbPostDic: [String: AnyObject] = [
"title":"",
"first_name”:”Ali“,
"last_name”:”Ahmad“,
"nick_name”:”ahmad”,
"gender":1,
"gender_visibility":2,
"dob":"1985-08-25",
"dob_visibility":2,
"city":"Lahore",
"city_visibility":2,
"bio”:”Its bio detail.”,
"bio_visibility":2,
"nationality":"Pakistan",
"nationality_visibility":2,
"relationship_status”:”Single”,
"rel_status_visibility":2,
"relation_with":"",
"relation_with_visibility":2,
"social_media_source":"Facebook",
]
I want to filter this dictionary such a way that new dictionary should just contains elements without empty strings.
let fbPostDic: [String: AnyObject] = [
"first_name”:”Ali“,
"last_name”:”Ahmad“,
"nick_name”:”ahmad”,
"gender":1,
"gender_visibility":2,
"dob":"1985-08-25",
"dob_visibility":2,
"city":"Lahore",
"city_visibility":2,
"bio”:”Its bio detail.”,
"bio_visibility":2,
"nationality":"Pakistan",
"nationality_visibility":2,
"relationship_status”:”Single”,
"rel_status_visibility":2,
"relation_with_visibility":2,
"social_media_source":"Facebook",
]
There are present ways like
let keysToRemove = dict.keys.array.filter { dict[$0]! == nil }
But its support iOS9.0 or above. I want to give support of iOS8.0 as well.
Any suggestions?
Because the above dict is a constant, add an extra init method in Dictionary extension can simplify the process:
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
init(_ elements: [Element]){
self.init()
for (key, value) in elements {
self[key] = value
}
}
}
print(Dictionary(dict.filter { $1 as? String != "" }))
However, if above dict can be declared as a variable. Probably try the one below without above extra Dictionary extension:
var dict: [String : AnyObject] = [...]
dict.forEach { if $1 as? String == "" { dict.removeValueForKey($0) } }
print(dict)
This solution will also work; But Allen's solution is more precise.
public class func filterDictionaryFromEmptyString(var dictionary:[String: AnyObject]) -> [String: AnyObject]
{
print(dictionary.keys.count)
for key:String in dictionary.keys
{
print(key)
print(dictionary[key]!)
if String(dictionary[key]!) == ""
{
dictionary.removeValueForKey(key)
}
}
print(dictionary.keys.count)
return dictionary
}

How to use For Loop through keys of NSUserDefault Dictionary

I can't get a value out of this computed property (userList)...is my init set up correctly? I'm trying to build a list that will populate the rows in my table view. (*Edit: I've edited my question with a saveData function, but "cannot invoke setObject" with an argument list of type ([String : User], forKey: String)" –
import Foundation
class DataManager {
static let sharedInstance = DataManager()
var users = [String : User]()
init() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let var userFromDefaults = userDefaults.objectForKey("userKey") as? [String : User] {
users = userFromDefaults
}
else {
// add default values later
}
}
func saveData() {
let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(users, forKey: "userKey")
}
var userList: [String] {
var list: [String] = []
for name in users.keys {
list.append(name)
}
list.sort(<)
return list
}
My viewcontroller:
import UIKit
class NameViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var users: [String] = DataManager.sharedInstance.userList
User Struct with a method to convert into json:
import Foundation
struct User {
var name = ""
var stores: [Store] = []
init?(json: [String: AnyObject]) {
if let name = json["name"] as? String,
storesJSON = json["stores"] as? [[String: AnyObject]]
{
self.name = name
self.stores = storesJSON.map { Store(json: $0)! }
} else {
return nil
}
}
init() { }
func toJSON() -> [String: AnyObject] {
return [
"name": name,
"stores": stores.map { $0.toJSON() }
]
}
}
You can grab all keys/Values using (from apple documentation)
dictionaryRepresentation() Returns a dictionary that contains a union
of all key-value pairs in the domains in the search list.
Using this you can loop all the elements in the dictionary
for element in NSUserDefaults.standardUserDefaults().dictionaryRepresentation() {
println(element)
//Do what you need to do here
}
You can also retrieve just the keys:
println(NSUserDefaults.standardUserDefaults().dictionaryRepresentation().keys.array);
or just the values
println(NSUserDefaults.standardUserDefaults().dictionaryRepresentation().values.array);
As Matt pointed out in his comment, you need to save contents to NSUserDefaults before you can read them.
Something else that will prevent your code from working:
NSUserDefaults will only save "property list" objects (NSString, NSData, NSDate, NSNumber, NSArray, or NSDictionary objects or their Swift equivalents.)
If you try to save anything else, or a collection containing anything but those data types, the save fails and you get nil when you try to read it back. Your code shows you trying to save a dictionary where the value for each key is an object of type "User". That's going to fail since "User" is not one of the short list of "property list" object types.
This is not a good user of NSUserDefaults, which is meant to store preferences and other small bits of data between app launches. See the documentation on NSUserDefaults.
You should be managing this dictionary of users in memory when you are displaying data, and if you want to persist it to disk between app launches you should use CoreData or make your User class conform to NSCoding and read/write it with archiving. Here's a nice tutorial.

How to convert between Key and String in Swift?

I'm making an extension on Dictionary, just a convenience method to traverse a deep json structure to find a given dictionary that might be present. In the general extension of a Dictionary, i'm not able to subscript because i give a String instead of a Key
extension Dictionary {
func openingHoursDictionary() -> Dictionary<String,AnyObject>? {
if let openingHours = self["openingHours"] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
return nil
}
}
Error: String is not convertible to DictionaryIndex<Key, Value>
on self["openingHours"]
How can i make a Key from the String "openingHours" or check the dictionary for ths string?
You can check at runtime if the string is a valid key for the dictionary:
extension Dictionary {
func openingHoursDictionary() -> [String : AnyObject]? {
if let key = "openingHours" as? Key {
if let openingHours = self[key] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
}
return nil
}
}
But this will "silently" return nil if called for other dictionaries
like [Int, AnyObject].
If you want the compiler to check if it is safe to subscript the
dictionary with a string then you have to use a (generic) function:
func openingHoursDictionary<T>(dict : [String : T]) -> [String : AnyObject]? {
if let openingHours = dict["openingHours"] as? Array<AnyObject> {
// traverses further and finds opening hours dictionary
}
return nil
}
It is (currently) not possible to write Dictionary (or Array)
extension methods that apply only to a restricted type of the
generic parameters.
Just cast the String as Key using "openingHours" as Key
if let pickupPoints = self["openingHours" as Key] as? Array<AnyObject> {
}
Downside is that if doing this i will get a crash if i have a Dictionary<Int,AnyObject> and use the method there.
0x10e8c037d: leaq 0x362aa(%rip), %rax ; "Swift dynamic cast failure"

Resources