On the process of exploring Core Data with Swift I have the following function working, as a test:
func insertObject (entityName:String) {
var newItem = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext:managedObjectContext!) as! EventList
let now = NSDate()
newItem.date = now
newItem.eventDescription = “Whatever Anniversary"
}
This seems to work, but to make my function more useful, I want to pass it a dictionnary describing the object I intend to insert.
Something like the following:
func insertObject (entityName:String,dico:NSDictionary) {
var newItem = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext:managedObjectContext!) as! EventList
for (key, value) in dico {
println("\(key) : \(value)")
newItem.key = value
}
}
Here comes the problem, this line is wrong:
newItem.key = value
What is the proper syntax to use?
This line shows me that the looping part works fine:
println("\(key) : \(value)")
You can use Key-Value coding for managed objects:
func insertObject (entityName:String, dico: [String : NSObject]) {
let newItem = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext:managedObjectContext!) as! EventList
for (key, value) in dico {
newItem.setValue(value, forKey: key)
}
}
which can be shortened to
func insertObject (entityName:String, dico: [String : NSObject]) {
let newItem = NSEntityDescription.insertNewObjectForEntityForName(entityName, inManagedObjectContext:managedObjectContext!) as! EventList
newItem.setValuesForKeysWithDictionary(dico)
}
The problem with this general approach is that it will crash at
runtime if the dictionary contains keys which are not properties
of the entity, or if the data type does not match.
Related
I am using EVReflection in my app. One JSON response should be parsed as type Dictionary<String,Array<MyObject>>. I have successfully parsed this by overriding the setValue method like this:
override func setValue(_ value: Any!, forUndefinedKey key: String) {
switch key {
case "response":
if let dict = value as? NSDictionary {
response = Dictionary<String,Array<MyObject>>();
for (key, value) in dict {
var listValues = Array<MyObject>();
if let array = value as? NSArray {
for vd in array {
listValues.append(MyObject(dictionary: vd as! NSDictionary));
}
}
response![key as? String ?? ""] = listValues;
}
}
break;
}
}
However, I am seeing the following error in the console:
ERROR: Could not create an instance for type Swift.Dictionary<Swift.String, Swift.Array<MyObject>>
Is there a different way I should be doing this? How do I get the error to go away?
I was able to figure this out by using a propertyConverter as follows:
override public func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
return[
(
key: "response"
, decodeConverter: {
if let dict = $0 as? NSDictionary {
self.response = Dictionary<String,Array<MyObject>>();
for (key, value) in dict {
var listValues = Array<MyObject>();
if let array = value as? NSArray {
for vd in array {
listValues.append(MyObject(dictionary: vd as! NSDictionary));
}
}
self.response![key as? String ?? ""] = listValues;
}
}
}
, encodeConverter: { return nil }
)
]
}
With EVReflection you should be using NSDictionary not a Dictionary (which is a struct).
If you do this then you shouldn't need to override any property converter methods.
I have a json database on firebase and trying to get them and put into local array of dictionaries.
My json model on Firebase
My struct model is also like below
struct Places {
var type:String!
var country:String!
var name:String!
var image:String!
var coords:[Coords]!
init(type: String, country: String, name: String, image: String, coords: [Coords]) {
self.type = type
self.country = country
self.name = name
self.image = image
self.coords = coords
}
init(snapshot: FIRDataSnapshot) {
let snapshotValue = snapshot.value as! [String:Any]
type = snapshotValue["type"] as! String
country = snapshotValue["country"] as! String
name = snapshotValue["name"] as! String
image = snapshotValue["image"] as! String
coords = snapshotValue["coords"] as! [Coords]!
}
}
And also [Coords] struct model like below:
struct Coords {
var latStart:String!
var latEnd:String!
var lonStart:String!
var lonEnd:String!
init(latStart: String, latEnd: String, lonStart: String, lonEnd: String) {
self.latStart = latStart
self.latEnd = latEnd
self.lonStart = lonStart
self.lonEnd = lonEnd
}
}
And I am trying to get and put json data by below code:
placesRef.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
print("data not exist")
return
}
var plc: [Places] = []
for eachPlace in (snapshot.children){
let place = Places(snapshot: eachPlace as! FIRDataSnapshot)
plc.append(place)
}
self.allPlaces = plc
The problem is that I can get the array of dictionary except coords dictionary inside array of dictionary. [Coords] dictionary seems null and I would like to know what the problem is. Thanks for any suggestion.
Because snapshotValue["coords"] as! [Coords]! are not Coords yet. They are just dictionaries. You have to go through each dictionary in snapshotValue[“coords”] and init a Coords object, then when you’re finished group them all into an array and assign it to self.coords of the Places struct. The map function is really convenient for this.
Example:
I would change the Coords init function to something like:
init(dictionary: [String : AnyObject]) {
self.latStart = dictionary["lat1"] as? String
//...
//...
}
Then in Places init use something like:
coords = (snapshotValue["coords"] as? [[String : AnyObject]])?.map({ Coord(dictionary: $0) })
I didn't test this and making some assumptions here, but you should be able to make something similar work.
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!
Why do I get an error when I used valueForKey... I am using same trick like in objectiveC ...
In ObjectiveC, the code is
self.strSubscribe =[responseObject[#"subscribe"] valueForKey:#"subscribe_ids"];
In Swift , the code is
self.strSubscribe = responseObject["subscribe"].valueForKey["subscribe_ids"] as! String
I declare the variables like
var arraySubCategory : NSMutableArray! = NSMutableArray()
var strSubscribe:String!
And I tried to access the value from below response
{
subscribe =
{
"subscribe_ids" = "1,14";
}
}
Edit
It works using Amit and Eric's solution but now for following data
{
data = (
{
"subscribe_ids" = "1,14";
}
);
}
let dictionary = responseObject["data"][0] as! Dictionary<String,AnyObject>
self.strSubscribe = dictionary["subscribe_ids"] as! String
OR//
if let dic = responseObject["data"][0] as? [String:String], let ids = dic["subscribe_ids"] {
self.strSubscribe = ids
}
but it gives me error:
could not find member 'subscript'
Swift doesn't know the type of responseObject["subscribe"], you have to help the compiler a bit; for example:
if let dic = responseObject["subscribe"] as? [String:String], let ids = dic["subscribe_ids"] {
self.strSubscribe = ids // "1,14"
}
UPDATE:
It's still the same problem: the compiler doesn't know the type of responseObject["data"], so when you try to access the subscript there's an error (because you know it's a dictionary inside the array, but the compiler doesn't).
One solution is to give the type to the compiler by declaring an array of dictionaries in the if let condition:
if let arr = responseObject["data"] as? [[String:String]], let ids = arr[0]["subscribe_ids"] {
self.strSubscribe = ids
}
Notice that it's [[String:String]] (array of dictionaries), not [String:String] (dictionary).
Write like this.
let dictionary = responseObject["subscribe"] as! Dictionary<String, AnyObject>
self.strSubscribe = dictionary["subscribe_ids"] as! String
Since responseObject["subscribe"] will give a AnyObject? output and AnyObject does not have any member called valueForKey.
I have a question, I do have an array which holds dictionaries, these dictionaries then go into a tableView. Now I need a search Function to filter the Table.
For example: I have that kind of Array/Dictionary
var filteredItems = [[String: String]]()
var unfilteredItems = [[String: String]]()
and then I wanted to filter that with a function like this
func filterContentForSearch (searchText:String, scope: Int) {
self.filteredItems = self.unfilteredItems.filter({ (description: String) -> Bool in
var searchMatch = description.rangeOfString(searchText)
return searchMatch != nil
})
}
My feeling says there is something wrong and it don't work, so far I couldn't figure out what could be the issue or an approach which could work... any ideas?
The error message I get from Xcode is: "[String, String] is not a subtype of String
Running on Xcode 6.1.1
Thanks for your help...
You have dictionaries in unfilteredItems array. But you treat them as String inside filter closure. Correct type should be
(description: [String: String]) -> Bool
Try this:
var filteredItems : NSArray = [];
var unfilteredItems : NSArray = []; // this array should contain your string data
func filterContentForSearch (searchText:String, scope: Int) {
if let predicate = NSPredicate(format: "SELF CONTAINS %#", searchText) {
self.filteredItems = self.unfilteredItems.filteredArrayUsingPredicate(predicate)
}
else
self.filteredItems = nil;
}
Here is how I did it:
func filterContentForSearch (searchText:String, scope: Int) {
self.unfiltredItems = filtredItems.filter({
$0["description"] == searchText
})
}
Very simple but it works...