Two similar classes show different initialization errors - ios

I found an error when I test some codes from Github.
class Profile {
let text: String
let date: String
let id: String?
init?(data: NSDictionary?) {
if let text = data?.valueForKeyPath(Test.Text) as? String {
self.text = text
if let date = data?.valueForKeyPath(Test.Created) as? String {
self.date = date
id = data?.valueForKeyPath(Test.ID) as? String
}
}else{
return nil
}
}
struct Test {
static let Text = "text"
static let Created = "created"
static let ID = "id"
}
}
The line of init? shows the error "constants self.data used before being initialized."
And I create a similar class of it, like
class Context {
let words: String
init?(text:String?) {
if let words = text {
self.words = words
}else{
return nil
}
}
}
This time it shows " all stored properties of class instance must be initialized before returing nil from an initializer."
For the first one , there is a workaround that I can delete the else block and give each properties an empty value would fix the error. However it would have me change the properties mutable.
( I don't want it to be mutable)
And for the second example, I just insert self.word = ""before the line of return nil could also fix the error.
But I really wonder why these similar cases show the different errors and realize the logic of Swift, and how can I fix it correctly?
Thank you for helping me.

Try this version of the code.
Code 1:
class Profile {
var text: String = ""
var date: String = ""
var id: String? = ""
init?(data: NSDictionary?) {
if let text = data?.valueForKeyPath(Test.Text) as? String {
self.text = text
if let date = data?.valueForKeyPath(Test.Created) as? String {
self.date = date
id = data?.valueForKeyPath(Test.ID) as? String
}
}else{
return nil
}
}
struct Test {
static let Text = "text"
static let Created = "created"
static let ID = "id"
}
}
Code 2:
class Context {
var words: String = ""
init?(text:String?) {
if let words = text {
self.words = words
} else {
return nil
}
}
}

York initializers are incomplete and that's why you get the message. Swift is type save, which means that all non optional properties must be initialized before the class is returned. Even when the class returns a nil. secondly, you can't call self (which is the class itself) if you haven't initialized the class. However, that should not be a problem in your case, since you've defined a root class. For your first class, please, implement the code like this and it should work.
class Profile {
struct Test {
static let Text = "text"
static let Created = "created"
static let ID = "id"
}
let text: String
let date: String
let id: String?
init?(data: NSDictionary?) {
guard let tempText = data?.valueForKeyPath(Test.Text) as? String else {
text = ""
date = ""
id = nil
return nil
}
text = tempText
if let tempDate = data?.valueForKeyPath(Test.Created) as? String {
date = tempDate
id = data?.valueForKeyPath(Test.ID) as? String
} else {
date = ""
id = nil
}
}
}
For the second class you need to do a similar thing, which means in the else statement give words a value and it should be okay.

Related

How to check if a class model created using alamofire is nil in swift?

I have created class models using HTTP networking library Alamofire which contains following data:
class MyData: NSObject {
var name = ""
var params = PublicData()
func initUserWithInfo(userInfo: [String : AnyObject]) {
if let name = userInfo["name"] as? String {
self.name = name
}
if let params = userInfo["params"] as? ParamData {
self.params = params
}
}
}
class PublicData: NSObject {
var city = ""
func initUserWithInfo(userInfo: [String : AnyObject]) {
if let city = userInfo["city"] as? String {
self.city = city
}
}
}
Now, when I am trying to check whether params is nil or not, it's giving the following warning message:
let data = MyData()
if data.params != nil {
}
Comparing non-optional value of type 'params' to nil always returns true
or
if data.params {
}
'params' is not convertible to 'Bool'
or
if data.rams as Bool {
}
Cannot convert value of type 'ImpressionObject' to type 'Bool' in
coercion
how can I able to check if the nested model class is nil or not?
You are getting this error because you are initialing the value of params and can not be nil later on. If you want to make it optional than you should try below code
class MyData: NSObject {
var name = ""
/// making variable optional
var params: PublicData?
func initUserWithInfo(userInfo: [String : AnyObject]) {
if let name = userInfo["name"] as? String {
self.name = name
}
if let params = userInfo["params"] as? ParamData {
self.params = params
}
}
}
After that you should use it as below
let data = MyData()
if let parameters = data.params {
}
Or you can write something even smaller:
class MyData: NSObject {
var name = ""
/// making variable optional
var params: PublicData?
func initUserWithInfo(userInfo: [String : AnyObject]) {
guard let name = userInfo["name"] as? String, let param = userInfo["params"] as? ParamData else { return }
self.name = name
self.params = params
}
}

API doesn't contain any value for some objects and Xcode gives a fatal error when running the app

My app takes some data from this API: https://api.jqestate.ru/v1/properties/country
GitHub link to my project: https://github.com/armansharvel/JQ-Estate.git (download branch "Refreshing")
There are no compiler errors but when I run my app in the simulator Xcode prints in console "Fatal error: Index out of range".
In the ObjectModel.swift I created a class of the object with some data types. One of them is the variable mainPic (URL of picture for TableVeiw that I want to get from the API also). But the problem is not every object in the API contains value of URL of the picture.
So Xcode (when I try to run the app) marks the second line of code block that initialises mainPic variable and the error is: "Thread 7: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)"
Here is the whole class in code:
import Foundation
class Houses {
// Data Encapsulation
private var _mainPic: String
private var _localityName: String
private var _routeName: String
private var _mkadDistance: String
private var _rentOffer: String
private var _saleOffer: String
// Make a getted
var mainPic: String {
return _mainPic
}
var localityName: String {
return _localityName
}
var routeName: String {
return _routeName
}
var mkadDistance: String {
return _mkadDistance
}
var rentOffer: String {
return _rentOffer
}
var saleOffer: String {
return _saleOffer
}
// Initialization
init(data: JSONDictionary) {
// Main Picture
if let images = data["images"] as? JSONArray,
pic0 = images[0] as? JSONDictionary, // THIS LINE IS WITH ERROR
mainPic = pic0["url"] as? String {
self._mainPic = mainPic
} else {
_mainPic = ""
}
// Locality Name
if let location = data["location"] as? JSONDictionary,
localityName = location["localityName"] as? String {
self._localityName = localityName
} else {
_localityName = ""
}
// Route Name
if let location = data["location"] as? JSONDictionary,
routeName = location["routeName"] as? String {
self._routeName = routeName
} else {
_routeName = ""
}
// MKAD Distance
if let location = data["location"] as? JSONDictionary,
mkadDistance = location["mkadDistance"] as? String {
self._mkadDistance = mkadDistance
} else {
_mkadDistance = ""
}
// Rent Offer
if let rentDict = data["rentOffer"] as? JSONDictionary,
rentOffer = rentDict["price"] as? String {
self._rentOffer = rentOffer
} else {
_rentOffer = ""
}
// Sale Offer
if let saleDict = data["saleOffer"] as? JSONDictionary,
saleOffer = saleDict["price"] as? String {
self._saleOffer = saleOffer
} else {
_saleOffer = ""
}
}
}
Just in case, JSONDictionary and JSONArray are just typealiases:
typealias JSONDictionary = [String : AnyObject]
typealias JSONArray = Array<AnyObject>
Thanks in advance!
images[0] will crash with "Fatal error: Index out of range" if the images array is empty.
Since you're using optional binding, use first instead of [0]:
if let images = data["images"] as? JSONArray,
pic0 = images.first as? JSONDictionary,
mainPic = pic0["url"] as? String {
self._mainPic = mainPic
} else {
_mainPic = ""
}

How to fix: "fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)"

I am working on a Firebase Swift project using CocoaPods.
Every time after I log in the main ViewController, automatically I get EXC_BREAKPOINT error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
Here are some of my code lines where I got errors:
All codes from Joke.swift:
import Foundation
import Firebase
class Joke {
private var _jokeRef: Firebase!
private var _jokeKey: String!
private var _jokeText: String!
private var _jokeVotes: Int!
private var _username: String!
var jokeKey: String {
return _jokeKey
}
var jokeText: String {
return _jokeText
}
var jokeVotes: Int {
return _jokeVotes //1
}
var username: String {
return _username
}
// Initialize the new Joke
init(key: String, dictionary: Dictionary<String, AnyObject>) {
self._jokeKey = key
// Within the Joke, or Key, the following properties are children
if let votes = dictionary["votes"] as? Int {
self._jokeVotes = votes
}
if let joke = dictionary["jokeText"] as? String {
self._jokeText = joke
}
if let user = dictionary["author"] as? String {
self._username = user
} else {
self._username = ""
}
// The above properties are assigned to their key.
self._jokeRef = DataService.dataService.JOKE_REF.childByAppendingPath(self._jokeKey)
}
// Add or Subtract a Vote from the Joke.
func addSubtractVote(addVote: Bool) {
if addVote {
_jokeVotes = _jokeVotes + 1
} else {
_jokeVotes = _jokeVotes - 1
}
// Save the new vote total.
_jokeRef.childByAppendingPath("votes").setValue(_jokeVotes)
}
}
In JokeCellTableViewCell.swift:
var joke: Joke!
...............
func configureCell(joke: Joke) {
self.joke = joke
// Set the labels and textView.
self.jokeText.text = joke.jokeText
self.totalVotesLabel.text = "Total Votes: \(joke.jokeVotes)" // 2
self.usernameLabel.text = joke.username
// Set "votes" as a child of the current user in Firebase and save the joke's key in votes as a boolean.
.........
}
And in the main ViewController, JokesFeedTableViewController.swift:
var jokes = [Joke]()
....................
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let joke = jokes[indexPath.row]
// We are using a custom cell.
if let cell = tableView.dequeueReusableCellWithIdentifier("JokeCellTableViewCell") as? JokeCellTableViewCell {
// Send the single joke to configureCell() in JokeCellTableViewCell.
cell.configureCell(joke) // 3
return cell
} else {
return JokeCellTableViewCell()
}
...........
// 1, // 2, // 3 are code lines where errors appear.
I hope you could help me to fix this!
Thank you in advance!
Your problem is that you have not clearly defined the expectations of the Joke class.
Your initializer suggests that the properties on Joke should be optional, however, you are using them as though they are not. You must decide on which way you want to take it.
If the properties can be optional, I would suggest something like this:
class Joke {
private let jokeReference: Firebase
let jokeKey: String
private(set) var jokeText: String?
private(set) var jokeVotes: Int?
let username: String
// Initialize the new Joke
init(key: String, dictionary: Dictionary<String, AnyObject>) {
jokeKey = key
// Within the Joke, or Key, the following properties are children
if let votes = dictionary["votes"] as? Int {
jokeVotes = votes
}
if let joke = dictionary["jokeText"] as? String {
jokeText = joke
}
if let user = dictionary["author"] as? String {
username = user
} else {
username = ""
}
// The above properties are assigned to their key.
jokeReference = DataService.dataService.JOKE_REF.childByAppendingPath(jokeKey)
}
}
However, if the properties should never be nil, you need something like this:
class Joke {
private let jokeReference: Firebase
let jokeKey: String
let jokeText: String
let jokeVotes: Int?
let username: String
// Initialize the new Joke
init?(key: String, dictionary: Dictionary<String, AnyObject>) {
jokeKey = key
guard let votes = dictionary["votes"] as? Int,
joke = dictionary["jokeText"] as? String else {
return nil
}
jokeText = joke
jokeVotes = votes
if let user = dictionary["author"] as? String {
username = user
} else {
username = ""
}
// The above properties are assigned to their key.
jokeReference = DataService.dataService.JOKE_REF.childByAppendingPath(jokeKey)
}
}

How to create a data model

I've recently started programming with swift and am currently creating an application, which receives json data from a server. I was wondering about the best way to create the data model. First I tried creating a class and if leting all of the properties, but I found it to be too cumbersome (I have objects with 10-12 proeprties):
class Favourite {
var mArtikelText: String = ""
var mRid: String = ""
init(object: [String: AnyObject]) throws {
if let text = object["text"] as? String {
self.mArtikelText = text
}
if let id = object["id"] as? String {
self.mRid = id
}
}
}
Then I read about the new guard keyword and decided to try with it:
enum JSONResponseError: ErrorType {
case NilResponseValue
}
class Favourite {
var mArtikelText: String
var mRid: String
init(object: [String: AnyObject]) throws {
guard let text = object["text"] as? String else {
self.mArtikelText = ""
throw JSONResponseError.NilResponseValue
}
guard let rid = object["id"] as? String else {
self.mRid = ""
throw JSONResponseError
}
self.mArtikelText = text
self.mRid = rid
}
}
But I get All stored properties of a class instance must be initialized before throwing from an initializer. I guess I could silence the error if I initialize the properties, when I declare them, as I did in the first example, but (correct me if I'm wrong) I thought the guard is used to avoid exactly this approach.
Then I tried guarding all the properties at once:
class Favourite {
var mArtikelText: String
var mRid: String
init(object: [String: AnyObject]) throws {
guard case let (text as String, rid as String) = (object["text"], object["id"]) else {
self.mArtikelText = ""
self.mRid = ""
throw JSONResponseError.NilResponseValue
}
self.mArtikelText = text
self.mRid = rid
}
}
But I don't like this approach, because I want to leave only the faulty values and work with the rest (sometimes I receive nil values, that I won't be using anyway, so there is no point in throwing the whole answer away).
So, my question is, what is the best way to create a reliable model class from a json dictionary?

Error handling parsing JSON in Swift

I'm using Alamofire and am parsing the returned JSON into an object as shown below:
final class User: NSObject, ResponseObjectSerializable {
var id: Int
var facebookUID: String?
var email: String
var firstName: String
var lastName: String
var phone: String?
var position: String?
var timeCreated: CVDate
init?(response: NSHTTPURLResponse, var representation: AnyObject) {
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
representation = dataRepresentation
}
if let id = representation.valueForKeyPath("id") as? Int {
self.id = id
} else {
self.id = 0
}
if let facebookUID = representation.valueForKeyPath("facebook_UID") as? String {
self.facebookUID = facebookUID
}
if let email = representation.valueForKeyPath("email") as? String {
self.email = email
} else {
self.email = ""
}
if let firstName = representation.valueForKeyPath("first_name") as? String {
self.firstName = firstName
} else {
self.firstName = ""
}
if let lastName = representation.valueForKeyPath("last_name") as? String {
self.lastName = lastName
} else {
self.lastName = ""
}
if let phone = representation.valueForKeyPath("phone") as? String {
self.phone = phone
}
if let position = representation.valueForKeyPath("position_name") as? String {
self.position = position
}
if let timeCreated = representation.valueForKeyPath("time_created") as? String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
if let date = formatter.dateFromString(timeCreated) {
self.timeCreated = CVDate(date: date)
} else {
self.timeCreated = CVDate(date: NSDate())
}
} else {
self.timeCreated = CVDate(date: NSDate())
}
}
}
My question is, is this style the best way to decode JSON and set the non-optional instance variables? For example, in this statement:
if let id = representation.valueForKeyPath("id") as? Int {
self.id = id
}
I am required by the compiler to add an else clause and set the id to something otherwise xCode throws an error saying: self.id is not initialized at implicitly generated super.init call.
But at the same time, intializing self.id with a value of 0 is wrong and doesn't help me at all.
But at the same time, intializing self.id with a value of 0 is wrong and doesn't help me at all.
If having a default value for self.id feels wrong, then you should make this property an Optional. That way you wouldn't have to add an else clause:
final class User: NSObject, ResponseObjectSerializable {
var id: Int?
var facebookUID: String?
var email: String
var firstName: String
var lastName: String
var phone: String?
var position: String?
var timeCreated: CVDate
init?(response: NSHTTPURLResponse, var representation: AnyObject) {
if let dataRepresentation = ((representation as! NSDictionary).valueForKey("data") as? [String: AnyObject]) {
representation = dataRepresentation
}
if let id = representation.valueForKeyPath("id") as? Int {
self.id = id
}
...
Update
You said in the comments:
I always need to have an id for the user object though.
If you have to have this id property then the question is moot, you just have to do
let id = representation.valueForKeyPath("id") as! Int
and guarantee earlier that this value will exist.
Because if your object needs an ID, then you can't initialize it anyway if this value doesn't exist and if you don't want a default value.
You could use ?? to provide default values like this:
self.id = (representation.valueForKeyPath("id") as? Int) ?? 0
While the ResponseObjectSerializable code is a great example from the Alamofire project, it's really a better idea to use a dedicated JSON parsing library that has actual error states. This is far better than using optionals to represent error states, or having to provide a default value for every field just in case the response isn't correctly formed.
Although it has a bit of learning curve, I prefer to use Argo for my JSON parsing. Once you get the hang of it it makes JSON parsing practically bulletproof. Better yet, it's easy to integrate with Alamofire, especially version 3 that was released today.
To address your concern about not having an ID being an error condition, you could use a failable initializer. I did that in a recent project. Looks something like this:
let id: Int!
init? (inputJson: NSDictionary) {
if let id = inputJson["id"] as? Int {
self.id = id
} else {
// if we are initing from JSON, there MUST be an id
id = nil
cry(inputJson) // this logs the error
return nil
}
}
Of course, this means your code will need to accept that the initialization of your entire object may fail ..

Resources