I have this function searchMoviesOnJson:
func searchMoviesOnJson(imdbTitle: String, completionHandler: #escaping (Dictionary<String, Any>?) -> ()) {
let urlByName: String = "https://www.omdbapi.com/?s=\(imdbTitle)&type=movie"
//returns a list of movies that contains the title searched
//------------------------------------------------
Alamofire.request(urlByName).responseJSON {
response in
switch response.result {
case .success(let value):
let moviesJSON = value
completionHandler(moviesJSON as? Dictionary<String, Any>)
case .failure(_):
completionHandler(nil)
}
}
//------------------------------------------------
}
That gives me this response from api (e.g.: imdbTitle = "arq"):
{
Response = True;
Search = (
{
Poster = "https://images-na.ssl-images-amazon.com/images/M/MV5BMjAxODQ2MzkyMV5BMl5BanBnXkFtZTgwNjU3MTE5OTE#._V1_SX300.jpg";
Title = ARQ;
Type = movie;
Year = 2016;
imdbID = tt5640450;
},
{
Poster = "N/A";
Title = Arq;
Type = movie;
Year = 2011;
imdbID = tt2141601;
},
{
Poster = "N/A";
Title = "A.R.Q.";
Type = movie;
Year = 2015;
imdbID = tt3829612;
}
);
totalResults = 3;
}
So I created a this class MovieByTitle
class MovieByTitle {
var poster : String?
var title : String?
var year : Int?
var imdbID : String?
init(poster: String?, title: String?, year: Int?, imdbID: String?) {
//validation
if let isPoster = poster { self.poster = isPoster }
else { self.poster = nil }
if let isTitle = title { self.title = isTitle }
else { self.title = nil }
if let isYear = year { self.year = isYear }
else { self.year = nil }
if let isImdbID = imdbID { self.imdbID = isImdbID }
else { self.imdbID = nil }
}
}
And now my doubt, I also create this MovieDAO:
class MovieDao {
func getMovies(imdbTitle: String, completionHandler: #escaping (([MovieByTitle]) -> ())) {
//function that conects to the api
searchMoviesOnJson(imdbTitle: imdbTitle, completionHandler: {
moviesJSON in
//array to keep the attributes received by the dictionary
var moviesArray = [MovieByTitle]()
//searchResults is the response from my request as an array
if let searchResults = moviesJSON?["Search"] as? NSArray{
for searchResult in searchResults {
let movieResult = searchResult as! Dictionary<String,Any>
let movieDetail = MovieByTitle()
movieDetail.poster = movieResult["Poster"] as? String
movieDetail.title = movieResult["Title"] as? String
movieDetail.year = movieResult["Year"] as? Int
movieDetail.imdbID = movieResult["imdbID"] as? String
moviesArray.append(movieDetail)
}
}
})
}
}
But the xcode returns an error in line:
let movieDetail = MovieByTitle()
Error message: missing argument for parameter 'poster' in call (and so on with the others)
What is the right sintax for that? What is the better way to cast my dictionary response as an object?
You MovieByTitle init function requires 4 parameters that are missing.
Solution 1: Add a secondary init:
init() {}
Solution 2: Define existing init parameters as optional by giving them default values:
init(poster: String? = nil, title: String? = nil, year: Int? = nil, imdbID: String? = nil)
Solution 3: Call the existing init with the parameters it needs:
let movieDetail = MovieByTitle(poster: movieResult["Poster"] as? String, title: movieResult["Title"] as? String, year: movieResult["Year"] as? Int, imdbID: movieResult["imdbID"] as? String)
Your init function requires 4 parameters. You haven't included any. Try the following
let poster = movieResult["Poster"] as? String
let title = movieResult["Title"] as? String
let year = movieResult["Year"] as? Int
let imdbID = movieResult["imdbID"] as? String
let movieDetail = MovieByTitle(poster:poster, title:title, year:year, imdbID:imdbDB)
Related
I'm getting an array of dictionaries from the server. Then I'm trying to convert it to jsonDictionary it seems like I'm doing something wrong. How can I also init my Users model?
Here is the code:
func getSearchedUsers(key: String, completion: #escaping(SearchedUsers?) -> Void) {
if let url = URL(string: baseURL + "search?qerty=\(key)") {
Alamofire.request(url).responseJSON { (response) in
if let array = response.result.value as? [[String:Any]] {
var dictionary = [String:Any]()
for item in array {
for (key, value) in item {
dictionary.updateValue(value, forKey: key)
}
}
} else {
completion(nil)
}
}
}
}
And here is the model:
class SearchedUsers {
let id: Int
let username: String?
let fullName: String?
let profilePicture: URL?
let isPrivate: Bool
init(data: [String: Any]) {
id = data["id"] as! Int
username = data["username"] as? String
fullName = data["fullName"] as? String
isPrivate = data["isPrivate"] as! Bool
profilePicture = data["profilePicUrl"] as? URL
}
}
How can I get this to work?
Here is the response I get:
[Result]: SUCCESS: (
{
byline = "21.9k followers";
followerCount = 21911;
friendshipStatus = {
following = 0;
"incoming_request" = 0;
"is_bestie" = 0;
"is_private" = 0;
"outgoing_request" = 0;
};
fullName = "Undefined Variable";
hasAnonymousProfilePicture = 0;
id = 8513861541;
isPrivate = 0;
isVerified = 0;
mutualFollowersCount = 0;
picture = "https://scontent-ams3-1.cdninstagram.com/vp/885ac17fe17809de22790f0559f61877/5CD13A1C/t51.2885-19/s150x150/39312159_480582069091253_3011569611268161536_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
pk = 8513861541;
profilePicId = "1857507164564653723_8513861541";
profilePicUrl = "https://scontent-ams3-1.cdninstagram.com/vp/885ac17fe17809de22790f0559f61877/5CD13A1C/t51.2885-19/s150x150/39312159_480582069091253_3011569611268161536_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
reelAutoArchive = on;
username = "i_am_variable";
},
{
byline = "467 followers";
followerCount = 467;
friendshipStatus = {
following = 0;
"incoming_request" = 0;
"is_bestie" = 0;
"is_private" = 0;
"outgoing_request" = 0;
};
fullName = undefined;
hasAnonymousProfilePicture = 0;
id = 8657882817;
isPrivate = 0;
isVerified = 0;
latestReelMedia = 1547794887;
mutualFollowersCount = 0;
picture = "https://scontent-ams3-1.cdninstagram.com/vp/fb3c992c899aa269bdce2c4c1db8575b/5CD068BA/t51.2885-19/s150x150/46378106_2062632390480778_1266491662662631424_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
pk = 8657882817;
profilePicId = "1931972067016763185_8657882817";
profilePicUrl = "https://scontent-ams3-1.cdninstagram.com/vp/fb3c992c899aa269bdce2c4c1db8575b/5CD068BA/t51.2885-19/s150x150/46378106_2062632390480778_1266491662662631424_n.jpg?_nc_ht=scontent-ams3-1.cdninstagram.com";
reelAutoArchive = on;
username = "undefi.ned";
})
It's an array of dictionaries, I need to parse it in a proper way. That's my main issue.
If you know how to parse dictionary, then you should know how to make one ;) There are tools out there to make your own model class, like: http://www.jsoncafe.com/
EDIT: As suggested by Robert in the comment section below, you can learn Decodable.
You can use that to give yourself an idea how a model class could or should look like. Use it however you like. In a decent project, there could be tons of data, and you don't want to make a class model out of it especially if you're the only one handling the iOS project.
So we suppose, we have this json data, based on your post:
{
"id": 1,
"username": "dd",
"fullName": "dd",
"profilePicture": "ddd",
"isPrivate": true
}
We could make a model out of it like so:
//
// UserRootClass.swift
// Model Generated using http://www.jsoncafe.com/
// Created on January 18, 2019
import Foundation
class UserRootClass : NSObject {
var fullName : String!
var id : Int!
var isPrivate : Bool!
var profilePicture : String!
var username : String!
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(fromDictionary dictionary: [String:Any]){
fullName = dictionary["fullName"] as? String
id = dictionary["id"] as? Int
isPrivate = dictionary["isPrivate"] as? Bool
profilePicture = dictionary["profilePicture"] as? String
username = dictionary["username"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if fullName != nil{
dictionary["fullName"] = fullName
}
if id != nil{
dictionary["id"] = id
}
if isPrivate != nil{
dictionary["isPrivate"] = isPrivate
}
if profilePicture != nil{
dictionary["profilePicture"] = profilePicture
}
if username != nil{
dictionary["username"] = username
}
return dictionary
}
}
The model class above was made using the tool I gave above, but I removed the NSCoding protocol methods.
I hope this helps! Good luck and welcome to Stackoverflow.
You can use Decodable if you have Struct instead of Class for easy parsing. Here is the example in Alamofire 5.0
struct SearchedUsers: Decodable {
let id: Int
let username: String?
let fullName: String?
let profilePicture: URL?
let isPrivate: Bool
}
AF.request("http://url_endpoint/").responseData { response in
do {
// data we are getting from network request
let decoder = JSONDecoder()
let response = try decoder.decode([SearchedUsers].self, from: response.data!)
} catch { print(error) }
}
I want to put the information I get from the API into the corresponding Label inside, I use the Alamofire to get the API information and put the corresponding Label inside, but I found that my Label text has not been changed, would like to ask this happen What's the problem? Who can answer me for me? Thank you
Here is my Information class:
import Foundation
import Alamofire
class Information {
var account:String?
var date:String?
var name:String?
var sex:String?
var born:String?
var phoneNumber:String?
var email:String?
init(account:String,date:String,name:String,sex:String,born:String,phoneNumber:String,email:String) {
self.account = account
self.date = date
self.name = name
self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email
}
typealias DownlaodComplete = () -> ()
func downlaodInformation(completion:#escaping DownlaodComplete) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON { response in
print(response)
if let json = response.result.value as? Dictionary<String,Any> {
guard let account = json["Account"] as? String ,let date = json["Date"] as? String , let name = json["Name"] as? String , let sex = json["Sex"] as? String , let born = json["Born"] as? String , let phoneNumber = json["PhoneNumber"] as? String , let email = json["Email"] as? String else {
return
}
self.account = account
self.date = date
self.name = name
self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email
completion()
}
}
}
}
And here is my ViewController:
var information:Information?
override func viewDidLoad() {
super.viewDidLoad()
if let currentInformation = information {
currentInformation.downlaodInformation {
self.accountLabel.text = currentInformation.account
self.dateLabel.text = currentInformation.date
self.nameLabel.text = currentInformation.name
self.sexLabel.text = currentInformation.sex
self.bornLabel.text = currentInformation.born
self.phoneNumberLabel.text = currentInformation.phoneNumber
self.emailLabel.text = currentInformation.email
}
}
You need to use your completion block which will be called whenever Alamofire has finished the data request. You can also improve your code a bit by for example have a onCompletion block that passes an Information object and an onError block to display if you have any errors. Example below:
func downlaodInformation(parameterOne: String, parameterTwo: Int, onCompletion: #escaping (Information) -> Void, onError: #escaping(NSError) -> Void) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON { response in
if let json = response.result.value as? Dictionary<String,Any> {
let account = json["Account"] as? String
let date = json["Date"] as? String
let name = json["Name"] as? String
let sex = json["Sex"] as? String
let born = json["Born"] as? String
let phoneNumber = json["PhoneNumber"] as? String
let email = json["Email"] as? String
let information = Information(account: account, date: date, name: name, sex: sex, born: born, phoneNumber: phoneNumber, email: email)
onCompletion(information)
} else {
onError(NSError(domain: "Error while getting data", code: 0, userInfo: nil))
}
}
}
Usage:
downlaodInformation(parameterOne: "someParam", parameterTwo: 123, onCompletion: { (currentInformation) in
print(currentInformation.account)
self.accountLabel.text = currentInformation.account
self.dateLabel.text = currentInformation.date
self.nameLabel.text = currentInformation.name
self.sexLabel.text = currentInformation.sex
self.bornLabel.text = currentInformation.born
self.phoneNumberLabel.text = currentInformation.phoneNumber
self.emailLabel.text = currentInformation.email
}) { (error) in
print(error.domain)
}
Here you declare information to be an Information optional
var information:Information?
But you don't give it an initial value, meaning that it is nil
In your viewDidLoad you do the right thing and check whether information has a value:
if let currentInformation = information
But I'm guessing it hasn't, because you haven't created an instance of it. Therefore you don't end up inside your if let loop and never calls downlaodInformation
So you need to create a new instance of Information before you can use it.
However
This leads to a problem with your Information class.
If I was to instantiate an Information object, I'd need to have:
account
date
name
sex
born
phoneNumber
email
Or..since you've created them as optionals, pass nil.
But that is not what you want, is it?
I'm guessing you'd like to do something along the lines of this in your ViewController:
let information = Information()
and then in viewDidLoad
information.downloadInformation( currrentInformation in
self.accountLabel.text = currentInformation.account
....
}
To do so you could change your Information to not take parameters to its constructor and then create another struct which would hold your data.
Something like:
struct Information {
var account:String?
var date:String?
var name:String?
var sex:String?
var born:String?
var phoneNumber:String?
var email:String?
}
class InformationLoader {
func downloadInformation(completion: (Information?) -> ()) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON{ response in
print(response)
if let json = response.result.value as? Dictionary<String,Any> {
guard let account = json["Account"] as? String,
let date = json["Date"] as? String,
let name = json["Name"] as? String,
let sex = json["Sex"] as? String,
let born = json["Born"] as? String,
let phoneNumber = json["PhoneNumber"] as? String,
let email = json["Email"] as? String else {
completion(nil)
return
}
let information = Information(account: account, date: date, name: name, sex: sex, born: born, phoneNumber: phoneNumber, email: email)
completion(information)
}
}
}
And you'd need to change your code in the ViewController to:
let informationLoader:InformationLoader()
In viewDidLoad
informationLoader.downloadInformation{ currentInformation in
if let currentInformation = currentInformation {
//populate your textfields
self.accountLabel.text = currentInformation.account
....
}
}
Hope that makes sense and helps you.
Your code has a lot of mistakes, so here is a working variant. Better to call an updateUI or something like that from the closure. I hope this will help:
ViewController.swift:
class ViewController: UIViewController
{
#IBOutlet weak var accountLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var nameLabel: UILabel!
var information: Information?
override func viewDidLoad()
{
super.viewDidLoad()
information = Information.init(account: "aaaa", date: "dddd", name: "nnnn", sex: "ssss", born: "bbbb", phoneNumber: "pppp", email: "eeee")
information?.downlaodInformation(completion:
{
self.updateUI()
})
}
func updateUI()
{
print("called")
self.accountLabel.text = information?.account
self.dateLabel.text = information?.date
self.nameLabel.text = information?.name
/*self.sexLabel.text = currentInformation.sex
self.bornLabel.text = currentInformation.born
self.phoneNumberLabel.text = currentInformation.phoneNumber
self.emailLabel.text = currentInformation.email*/
}
}
Information.swift:
class Information
{
var account:String?
var date:String?
var name:String?
var sex:String?
var born:String?
var phoneNumber:String?
var email:String?
typealias DownlaodComplete = () -> ()
init(account:String,date:String,name:String,sex:String,born:String,phoneNumber:String,email:String) {
self.account = account
self.date = date
self.name = name
self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email
}
func downlaodInformation(completion:#escaping DownlaodComplete) {
Alamofire.request("http://163.18.22.78:81/api/Information/A1001a").responseJSON { response in
print(response)
completion()
if let json = response.result.value as? Dictionary<String,Any>
{
print("Dictionary done")
guard
let account = json["Account"] as? String,
let date = json["Date"] as? String ,
let name = json["Name"] as? String else
{
print("Parse error!")
return
}
self.account = account
self.date = date
self.name = name
/*self.sex = sex
self.born = born
self.phoneNumber = phoneNumber
self.email = email*/
completion()
}
}
}
}
Tested, and got the following response:
SUCCESS: {
Account = A1001a;
Born = 841031;
CarParking = "";
Date = "0001/1/1 \U4e0a\U5348 12:00:00";
Email = "zxc#gmail.com";
Name = Ray;
Phone = 09361811111;
Sex = "\U7537"; } called Dictionary done called
A long time since I have written iOS code but I have the following Model in an iOS app and works great but now we are finding out that detail is optional and we should allow nil values. How would I adjust the initializer to support this? Sorry, I find the optionals a bit difficult to grasp (concept makes sense - executing it is difficult).
class Item{
var id:Int
var header:String
var detail:String
init?(dictionary: [String: AnyObject]) {
guard let id = dictionary["id"] as? Int,
let header = dictionary["header"] as? String,
let detail = dictionary["detail"] as? String else {
return nil
}
self.id = id
self.header = header
self.detail = detail
}
and creating:
var items = [Item]()
if let item = Item(dictionary: dictionary) {
self.items.append(item)
}
As in above answer by #AMomchilov, you could assign the value only if it exists in your init method.
But also you could check for the value and then access it like below:
class Item {
var id:Int
var header:String
var detail: String?
init?(dictionary: [String: AnyObject]) {
guard let id = dictionary["id"] as? Int,
let header = dictionary["header"] as? String else {
return nil
}
self.id = id
self.header = header
self.detail = dictionary["detail"] as? String //if there is value then it will assign else nil will be assigned.
}
}
let dictionary = ["id": 10, "header": "HeaderValue"]
var items = [Item]()
if let item = Item(dictionary: dictionary) {
items.append(item)
print(item.id)
print(item.detail ?? "'detail' is nil for this item")
print(item.header)
}else{
print("No Item created!")
}
And the console is :
10
'detail' is nil for this item
HeaderValue
And if there is `detail' value present then:
let dictionary = ["id": 10, "header": "HeaderValue", "detail":"DetailValue"]
var items = [Item]()
if let item = Item(dictionary: dictionary) {
items.append(item)
print(item.id)
print(item.detail ?? "'detail' is nil for this item")
print(item.header)
}else{
print("No Item created!")
}
Console:
10
DetailValue
HeaderValue
Remove detail from the guard (as now a nil value is acceptable), and assign self.detail to dictionary["detail"] as? String.
class Item {
var id: Int
var header: String
var detail: String?
init?(dictionary: [String: AnyObject]) {
guard let id = dictionary["id"] as? Int,
let header = dictionary["header"] as? String else {
return nil
}
self.id = id
self.header = header
self.detail = dictionary["detail"] as? String
}
Edit: Improved based on Santosh's answer.
I'm trying to parse a JSON in my app and it's not working.
Here is a look at the JSON I'm trying to parse:
Check out the following screenshots. I get the error "Unexpectedly found nil while unwrapping an optional value" on the line
editorialElement.title = nodeElement!.valueForKey("title") as! String
Can anyone help me properly parse this JSON ?
EDIT 1: Here is more code. It shows what class I'm using (i.e. what objects I am creating based on the JSON file). I imagine this is what you meant by where I initialize JSON objects.
Here is my router as well (build using Alamofire). I feel like something might be wrong with it but at the same time, it makes a lot of sense and I don't know what missing:
EDIT 2: Here is the actual code:
func populateEditorials() {
if populatingEditorials {
return
}
populatingEditorials = true
Alamofire.request(GWNetworking.Router.Editorials(self.currentPage)).responseJSON() { response in
if let JSON = response.result.value {
/*
if response.result.error == nil {
*/
/* Creating objects for every single editorial is long running work, so we put that work on a background queue, to keep the app very responsive. */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
/*
// Making an array of all the node IDs from the JSON file
let nodeIDArray : [String]
var nodeCounter : Int = 0 */
for node in JSON as! NSDictionary {
var nodeElement = JSON.valueForKey(String(node))
self.nodeIDArray.addObject(String(node.key))
var editorialElement : EditorialElement = EditorialElement(title: "init", nodeID: 0, timeStamp: 0, imageURL: "init", author: "init", issueNumber: "init", volumeNumber: "init", articleContent: "init")
editorialElement.title = nodeElement!.valueForKey("title") as! String
editorialElement.nodeID = nodeElement!.valueForKey("nid") as! Int
editorialElement.timeStamp = nodeElement!.valueForKey("revision_timestamp") as! Int
editorialElement.imageURL = nodeElement!.valueForKey("image_url") as! String
editorialElement.author = nodeElement!.valueForKey("author") as! String
editorialElement.issueNumber = nodeElement!.valueForKey("issue_int") as! String
editorialElement.volumeNumber = nodeElement!.valueForKey("volume_int") as! String
editorialElement.articleContent = nodeElement!.valueForKey("html_content") as! String
self.editorialObjects.addObject(editorialElement)
/*
nodeIDArray[nodeCounter] = jsonValue{nodeCounter}.string
let editorialInfos : EditorialElement = ((jsonValue as! NSDictionary).valueForKey("\(nodeIDArray[nodeCounter])") as! [NSDictionary]).map { EditorialElement(title: $0["title"] as! String, nodeID: $0["nid"] as! Int, timeStamp: $0["revision_timestamp"] as! Int, imageURL: $0["image_url"] as! String, author: $0["author"], issueNumber: $0["issue_int"] as! Int, volumeNumber: $0["volume_int"] as! Int, articleContent: $0["html_content"] as! String) /* I am going to try to break this line down to simplify it and fix the build errors */
*/
}
print(self.editorialObjects)
}
/* Sorting the elements in order of newest to oldest (as the array index increases) */
self.editorialObjects.sort({ $0.timeStamp > $1.timeStamp })
let lastItem = self.editorialObjects.count
let indexPaths = (lastItem..<self.editorialObjects.count).map { NSIndexPath(forItem: $0, inSection: $0) }
dispatch_async(dispatch_get_main_queue()) {
self.editorialsTableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Automatic) // Animation implemented for testing, to be removed for version 1.0
}
self.currentPage++
/* } */
}
self.populatingEditorials = false
}
}
Here is the code for my Class and Router:
class EditorialElement: NSObject {
var title: String // title
var nodeID: Int // nid
var timeStamp: Int // revision_timestamp
var imageURL: String? // image_url
var author: String // author
var issueNumber: String // issue_int
var volumeNumber: String // volume_int
var articleContent: String // html_content
/* To get an NSDate objec from Unix timestamp
var date = NSDate(timeIntervalSince1970: timeStamp) */
init(title: String, nodeID: Int, timeStamp: Int, imageURL: String, author: String, issueNumber: String, volumeNumber: String, articleContent: String) {
self.title = title
self.nodeID = nodeID
self.timeStamp = timeStamp
self.imageURL = imageURL
self.author = author
self.issueNumber = issueNumber
self.volumeNumber = volumeNumber
self.articleContent = articleContent
}
override func isEqual(object: AnyObject!) -> Bool {
return (object as! EditorialElement).nodeID == self.nodeID
}
override var hash: Int {
return (self as EditorialElement).nodeID
}
}
enum Router : URLRequestConvertible {
static let baseURLString = MyGlobalVariables.baseURL
case Issue
case Editorials(Int)
case News(Int)
case Random(Int)
case Pictures(Int)
var URLRequest: NSMutableURLRequest {
let path : String
let parameters: [String: AnyObject]
(path) = {
switch self {
case .Issue:
return ("/issue")
case .Editorials (let editorialsSection): /* If section == 0, this will return the first ten editorials. If section == 1, then section * 10 = 10, and we will get the ten editorials after that. */
return ("/list/editorials/\(editorialsSection * 10)")
case .News (let newsSection):
return ("/list/news/\(newsSection * 10)")
case .Random (let randomSection):
return ("/list/random/\(randomSection * 10)")
case .Pictures (let page):
return ("/list/pictures/\(page)")
}
}()
let URL = NSURL(string: Router.baseURLString)
let GoldenWordsURLRequest = NSMutableURLRequest(URL: URL!.URLByAppendingPathComponent(path))
/* let encoding = Alamofire.ParameterEncoding.URL */
return GoldenWordsURLRequest
/* return encoding.encode(URLRequest, parameters: parameters).0 */
}
}
}
That's because you take an optional reference (editorialElement returned from a failable initializer) and call valueForKey("title") on it. It stumbles over the access to "title" because it goes first in you code while the target of the call is nil. I would recommend organizing your code as follows:
if let editorialElement = EditorialElement(title:..., nodeID: and such)
{
... assigning new values to the properties
}
and you will notice that you don't enter the if-let scope. You will avoid the crash and make sure the problem is inside the arguments you initialize the editorialElement with.
I'm trying to use reflection in Swift with Core Data entities, but when I execute the following code, my reflected var has only a reference for a super class, it didn't have a reference for any of it's attributes.
func printProperties() {
let mirror = reflect(self)
for var i = 0; i < mirror.count; i++ {
let (propertyName, childMirror) = mirror[i]
println("property name: \(propertyName)")
println("property value: \(childMirror.value)")
}
}
Does anyone have some idea why this happens?
Update: As suggested by Anderson in his answer I tried another approach and ended up with this code:
func loadFromJson(json: JSON) {
for attributeKey in self.entity.attributesByName.keys {
let attributeDescription = self.entity.propertiesByName[attributeKey]!
as! NSAttributeDescription
let attributeClassName = attributeDescription.attributeValueClassName
let jsonValue = json[(attributeKey as! String)]
var attributeValue: AnyObject? = attributeDescription.defaultValue
if jsonValue.type != .Null && attributeClassName != nil {
if attributeClassName == "NSNumber" {
attributeValue = jsonValue.number!
} else if attributeClassName == "NSString" {
attributeValue = jsonValue.string!
}
}
setValue(attributeValue, forKey: (attributeKey as! String))
}
}
I believe that this code can help you.
I wrote this extension to make a dictionary from a NSmanagedObject and it accesses all attributes and values of the object.
extension NSManagedObject {
func toDict() -> Dictionary<String, AnyObject>! {
let attributes = self.entity.attributesByName.keys
let relationships = self.entity.relationshipsByName.keys
var dict: [String: AnyObject] = [String: AnyObject]()
var dateFormater = NSDateFormatter()
dateFormater.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
for attribute in attributes {
if self.entity.propertiesByName[attribute]!.attributeValueClassName != nil && self.entity.propertiesByName[attribute]!.attributeValueClassName == "NSDate" {
let value: AnyObject? = self.valueForKey(attribute as! String)
if value != nil {
dict[attribute as! String] = dateFormater.stringFromDate(value as! NSDate)
} else {
dict[attribute as! String] = ""
}
} else {
let value: AnyObject? = self.valueForKey(attribute as! String)
dict[attribute as! String] = value
}
}
for attribute in relationships {
let relationship: NSManagedObject = self.valueForKey(attribute as! String) as! NSManagedObject
let value = relationship.valueForKey("key") as! String
dict[attribute as! String] = value
}
return dict
}
}
I hope to have helped you.