Multiple methods to init object? - ios

I have a model which uses init with JSON, so that i can create objects directly from an API response.
However in this instance I want to init the model and manually enter its properties, hence it wont be JSON.
How can i have another init method that allows me to not use the standard JSON method and instead manually enter my params?
The model looks like this...
class Conversation: NSObject {
var id: String
var index: String
var image: String
var firstname: String
var lastname: String
var withuserid: String
var badgeCount: String
init?(_ json: JSON) {
guard let id = json["id"].string,
let index = json["index"].string,
let image = json["image"].string,
let firstname = json["firstname"].string,
let lastname = json["lastname"].string,
let withuserid = json["withuserid"].string,
let badgeCount = json["badgeCount"].string
else { return nil }
self.id = id
self.index = index
self.image = image
self.firstname = firstname
self.lastname = lastname
self.withuserid = withuserid
self.badgeCount = badgeCount
}
}

Just add another init since you can several ones for a struct and class Then just call the desired one:
class Conversation: NSObject {
var id: String
var index: String
var image: String
var firstname: String
var lastname: String
var withuserid: String
var badgeCount: String
init?(_ json: JSON) {
guard let id = json["id"].string,
let index = json["index"].string,
let image = json["image"].string,
let firstname = json["firstname"].string,
let lastname = json["lastname"].string,
let withuserid = json["withuserid"].string,
let badgeCount = json["badgeCount"].string
else { return nil }
self.id = id
self.index = index
self.image = image
self.firstname = firstname
self.lastname = lastname
self.withuserid = withuserid
self.badgeCount = badgeCount
}
init(id: String, index: String, image: String, firstName: String, lastName: String, withUserId: String, badgeCount: String) {
self.id = id
self.index = index
self.image = image
self.firstName = firstName
self.lastName = lastName
self.withUserId = withUserId
self.badgeCount = badgeCount
}
}

A single class or struct can have several designated initializers in Swift.
Then you can call the non-JSON init method like this:
let conv = Conversation(id: "asbe", index: "1", "image: "img", firstName: "John", lastName: "Smith", withUserId: "21", badgeCount: "5"
class Conversation {
var id: String
var index: String
var image: String
var firstName: String
var lastName: String
var withUserId: String
var badgeCount: String
init(id: String, index: String, image: String, firstName: String, lastName: String, withUserId: String, badgeCount: String){
self.id = id
self.index = index
self.image = image
self.firstName = firstName
self.lastName = lastName
self.withUserId = withUserId
self.badgeCount = badgeCount
}
init?(_ json: JSON) {
guard let id = json["id"].string,
let index = json["index"].string,
let image = json["image"].string,
let firstname = json["firstname"].string,
let lastname = json["lastname"].string,
let withuserid = json["withuserid"].string,
let badgeCount = json["badgeCount"].string
else { return nil }
self.id = id
self.index = index
self.image = image
self.firstname = firstname
self.lastname = lastname
self.withuserid = withuserid
self.badgeCount = badgeCount
}
}
Some general advice: I don't see any reason for the class to inherit from NSObject. Swift classes don't need to inherit from any class, so unless you explicitly need a method from another class, don't make your custom classes inherit from others for no reason.
Please make sure you conform to the Swift naming convention, which is lower-camelCase for variable names. I would also rethink the types of some of the variables, without more context, it seems to me, some of them should be of type Int instead of String.

Related

Why can i not access attributes from another class in swift?

Im making a userProfile class in swift which includes the following code:
class userProfile {
var firstname: String!
var username: String!
var lastname: String!
var uid: String!
init(uid: String, dictionary: Dictionary<String, AnyObject>) {
self.uid = uid
if let username = dictionary["username"]as? String {
self.username = username
}
if let firstname = dictionary["firstname"]as? String {
self.firstname = firstname
}
if let lastname = dictionary["lastname"]as? String {
self.lastname = lastname
}
}
}
and i am trying to access the the attributes in another class with the following code:
var user: userProfile? {
didSet {
let fullName = userProfile?.firstname
firstname.text = fullName
}
}
But when i do this i get the following error and i don't know why:
Type 'userProfile?' has no member 'firstname'
how di i fix this?
Here is a fix (you tried to access class, but should be instance) :
var user: userProfile? {
didSet {
let fullName = user?.firstname // << here !!
firstname.text = fullName // I can't say if this valid
}
}
To avoid such ambiguity you should follow a rule to name classes in UpperCase, ie. in you case UserProfile.

Type 'MenuItem' does not conform to protocol 'Decodable'

Why am I getting the error: Type 'MenuItem' does not conform to protocol 'Decodable'? It was working before. One of the things I changed was modifiers from [String]? to [Modifier]? Is that producing the error? If so, why? Stackoverflow wants me to keep saying things because it's too little description relative to the amount of code that I displayed below. Still needing to add text.
struct MenuItem: Codable {
let itemId: String
let name: String
var modifiers: [Modifier]?
var photoUrl: String?
var quantity: Int
var unitPrice: Int
var sizeAddOnPrice: Int
var toppingsAddOnPrice: Int
var totalPrice: Int
var totalModifiersPrice: Int
let description: String
var size: String
var toppings: [String]?
let category: String
init(itemId: String, name: String, modifiers: [Modifier]?, photoUrl: String?, quantity: Int, unitPrice: Int, sizeAddOnPrice: Int, toppingsAddOnPrice: Int, totalPrice: Int, totalModifiersPrice: Int, description: String, size: String, toppings: [String]?, category: String) {
self.itemId = itemId
self.name = name
self.modifiers = modifiers
self.photoUrl = photoUrl
self.quantity = quantity
self.unitPrice = unitPrice
self.sizeAddOnPrice = sizeAddOnPrice
self.toppingsAddOnPrice = toppingsAddOnPrice
self.totalPrice = totalPrice
self.totalModifiersPrice = totalModifiersPrice
self.description = description
self.size = size
self.toppings = toppings
self.category = category
}
init?(itemId: String, payload: JSON) {
guard
let name = payload[ParamKey.name].string,
let photoUrl = payload[ParamKey.photoUrl].string,
let description = payload[ParamKey.description].string,
let category = payload[ParamKey.categoryName].string,
let unitPrice = payload[ParamKey.basePrice].int,
let size = payload[ParamKey.size].string
else { return nil }
self.itemId = itemId
self.name = name
self.photoUrl = photoUrl
self.description = description
self.category = category
self.unitPrice = unitPrice
self.size = size
self.sizeAddOnPrice = 0
self.toppings = nil
self.toppingsAddOnPrice = 0
self.totalPrice = (unitPrice + sizeAddOnPrice + toppingsAddOnPrice) * quantity
self.totalModifiersPrice = (sizeAddOnPrice + toppingsAddOnPrice) * 2
self.quantity = 1
self.modifiers = payload[ParamKey.modifiers].arrayObject as? [Modifier]
}
}
You need to make the inner structs conform to Decodable/Codable also and remove struct init it'll be automatically generated
struct Modifier: Codable {
// add properties
}
struct MenuItem: Codable {
let itemId: String
let name: String
var modifiers: [Modifier]?
var photoUrl: String?
var quantity: Int
var unitPrice: Int
var sizeAddOnPrice: Int
var toppingsAddOnPrice: Int
var totalPrice: Int
var totalModifiersPrice: Int
let description: String
var size: String
var toppings: [String]?
let category: String
init?(itemId: String, payload: JSON) {
guard
let name = payload[ParamKey.name].string,
let photoUrl = payload[ParamKey.photoUrl].string,
let description = payload[ParamKey.description].string,
let category = payload[ParamKey.categoryName].string,
let unitPrice = payload[ParamKey.basePrice].int,
let size = payload[ParamKey.size].string
else { return nil }
self.itemId = itemId
self.name = name
self.photoUrl = photoUrl
self.description = description
self.category = category
self.unitPrice = unitPrice
self.size = size
self.sizeAddOnPrice = 0
self.toppings = nil
self.toppingsAddOnPrice = 0
self.totalPrice = (unitPrice + sizeAddOnPrice + toppingsAddOnPrice) * quantity
self.totalModifiersPrice = (sizeAddOnPrice + toppingsAddOnPrice) * 2
self.quantity = 1
self.modifiers = payload[ParamKey.modifiers].arrayObject as? [Modifier]
}
}
You can also use
try content.rawData() // for payload: JSON) {
to JSON to data and then supply it to the Decodable class

returnsObjectsAsFaults not working as expected

I created a Core Data object as follows:
#objc(Gates)
public class Gates : NSManagedObject {
public class func getFetchRequest() -> NSFetchRequest<Gates> {
let request = NSFetchRequest<Gates>(entityName: "Gates")
request.returnsObjectsAsFaults = false
return request
}
#NSManaged var updatedAt: String
#NSManaged var objectId: String
#NSManaged var identifier: String
#NSManaged var name: String
#NSManaged var address: String
#NSManaged var dueDate: String
#NSManaged var productionCode: String
#NSManaged var locationCountry: String
#NSManaged var locationCity: String
#NSManaged var locationBuilding: String
#NSManaged var locationLevel: String
#NSManaged var locationRoom: String
#NSManaged var locationRange: String
#NSManaged var isFavorite: Bool
public func setGateData(gateDict: [String: Any]) {
updatedAt = gateDict["updatedAt"] as? String ?? ""
objectId = gateDict["objectId"] as? String ?? ""
identifier = gateDict["identifier"] as? String ?? ""
name = gateDict["name"] as? String ?? ""
isFavorite = gateDict["isFavorite"] as? Bool ?? false
address = gateDict["address"] as? String ?? ""
dueDate = gateDict["dueDate"] as? String ?? ""
productionCode = gateDict["productionCode"] as? String ?? ""
locationCountry = gateDict["locationCountry"] as? String ?? ""
locationCity = gateDict["locationCity"] as? String ?? ""
locationBuilding = gateDict["locationBuilding"] as? String ?? ""
locationLevel = gateDict["locationLevel"] as? String ?? ""
locationRoom = gateDict["locationRoom"] as? String ?? ""
locationRange = gateDict["locationRange"] as? String ?? ""
}
}
I also set this up in the xcdatamodeld:
Now, after I have saved the object in core data and I'm using the getFetchRequest() method that is part of the class which sets
request.returnsObjectsAsFaults = false on the request but I still getting the following result when I try to print the fetched objects:
<Gates: 0x60c0000959a0> (entity: Gates; id: 0xd000000005e40000 <x-
coredata://B9C33A5D-BF96-433A-9186-F51AA253F488/Gates/p377> ; data: <fault>)
As you can see in this case the data is still data: <fault>.
Why is the object parameters are not retrieved even though I set request.returnsObjectsAsFaults = false? What am I missing?
I'm having this issue and I found in my case instead of using the objects value in line, I initialize a variable with it first and then use that variable.
I would love to know if this is a Core Data bug or if I'm doing something wrong.
public class Person: NSManagedObject, Identifiable {
#NSManaged public var firstName: String
#NSManaged public var lastName: String
#NSManaged public var emailAddress: String
}
This does not work all the time:
CoreDataManager.shared.persistentContainer.performBackgroundTask{ context in
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
let email = "123Tester#gmail.com"
let request = Person.getPersonWith(email: email)
request.returnsObjectsAsFaults = false //DOES NOT WORK
if let person = try context.fetch(request).first{
print(person.fullName)
}
} catch{
fatalError()
}
}
However this does
CoreDataManager.shared.persistentContainer.performBackgroundTask{ context in
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
let email = "123Tester#gmail.com"
let request = Person.getPersonWith(email: email)
if let person = try context.fetch(request).first{
let fullName = person.fullName
print(fullName)
}
} catch{
fatalError()
}
}

How to handle array in model class?

How can make model class for this json data
{
total: 41,
totalPages: 4,
valueData: [
{
id: "23",
lastLogin: "0-Jul-2011 11:27:36 AM",
name: "varii"
},
{
id: "24",
lastLogin: "0-Jul-2015 11:27:36 AM",
name: "sarii"
},
{
id: "25",
lastLogin: "0-Jul-2018 11:27:36 AM",
name: "narii"
} ]
}
class OnResponse {
var total: Int?
var totalPages: Int?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
}
}
It's not working how can make it ready for work
and how to pass values to controller to model and how to get value from model
Follow the below class structure
class Response {
var total: Int?
var totalPages: Int?
var valueData: [LoginData]?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
var items:[LoginData] = ()
for (data in response["valueData"]) {
let login = LoginData(name: data["name"], lastLogin: data["lastLogin"])
items.append(login)
}
self.valueData = items
}
}
class LoginData {
var name: String?
var lastLogin: String?
init(name: String, lastLogin: String) {
self.name = name
self.lastLogin = lastLogin
}
}
you should use "reverseMatches" to retrieve the array, not the "data". May be you can use a third library to convert your json data to a model, such as Unbox, https://github.com/JohnSundell/Unbox.
Model for Your response :
struct ModelName {
var total: Int?
var totalPage: Int?
var reverseMatches: [LoginDetails]?
}
struct LoginDetails {
var id: String?
var lastLogin: String?
var name: String?
}
Parse the api response and assign the values on the appropriate fields. I have made, all the variables are optional.
Assign values like below.
var obj = Model()
obj.total = response["total"] as? Int
obj should be var, because you are going to mutate the struct values. because struct is value based, not reference based
class DataModel{
var total : Int?
var totalPages : Int?
var valueData : [UserModel]?
init(JSON: [String:Any?]){
self = parser.doParse(JSON: JSON)
}
}
class UserModel{
var id : String?
var lastLogin : String?
var name : String?
}
class parser : NSObject{
class func doParse(JSON: [String:Any?])->DataModel{
let dataModel = DataModel()
dataModel.total = JSON["total"] as? Int
dataModel.totalPages = JSON["totalPages"] as? Int
var userInfo : [UserModel] = []
let valueData : [String:String?]? = JSON["valueData"] as? [String:String?]
if let valueData = valueData{
for dataDict : [String:String?] in valueData{
let itemModel = UserModel()
itemModel.id = dataDict["id"] as? String
itemModel.lastLogin = dataDict["lastLogin"] as? String
itemModel.name = dataDict["name"] as? String
userInfo.append(itemModel)
}
dataModel.valueData = userInfo
}
return dataModel
}
}
class LoginData: NSObject {
let total: Int = 0
let totalPages: Int = 0
let valueData: Array<ValueData> = Array<ValueData>()
override init(total: Int!, totalPages: Int, valueData: Array<ValueData>!) {
self.total = total
self.totalPages = totalPages
self.valueData = valueData
}
}
struct ValueData {
let id: int?
let lastLogin: String?
let name: String?
init(id: int!, lastLogin: string, name: string!)
self.id = id ?? 0
self.lastLogin = lastLogin ?? ""
self.name = name ?? ""
}
}
you should use struct instead of class for creating model object...
advantages of struct over class refer
Why Choose Struct Over Class?
class/24232845
use two struct for holding your data one is for your single total count
and other is for last login detail
struct lastLogin {
let totalCount: (total: Int, totalPages: Int)
let valueArray: [lastLoginDetail]
}
struct lastLoginDetail {
let valueData: (id: String, lastLogin: String,name: String)
}
extension lastLoginDetail {
init(json: [String : String]) throws {
let id = json["id"]
let lastLogin = json["lastLogin"]
let name = json["name"]
let value = (id,lastLogin,name)
self.valueData = value as! (id: String, lastLogin: String, name: String)
}
}
extension lastLogin {
init(json: [String : Any]) throws {
let total = (json["total"] as! NSNumber).intValue
let totalPages = (json["totalPages"] as! NSNumber).intValue
let totalCounts = (total,totalPages)
var userInfo : [lastLoginDetail] = []
// Extract and validate valueData
let valueData = json["valueData"] as? NSArray
if let valueData = valueData{
for dataDict in valueData{
let dic : [String : String] = dataDict as! [String : String]
let lastLoginDeatails = try! lastLoginDetail(json: dic)
userInfo.append(lastLoginDeatails)
}
}
self.valueArray = userInfo
self.totalCount = totalCounts
}
}
func HowToUseModelClass(){
let jsonDic = NSDictionary()
// jsonDic // your json value
let dataValue = try! lastLogin(json: (jsonDic as! [String : Any])) // parsing the data
print(dataValue.totalCount.total)
print(dataValue.totalCount.totalPages)
print(dataValue.valueArray[0].valueData.id)
}

How to know if the append function is filling the properties of a NSObject

Swift 2.0 Alamofire 2.0 Xcode 7 IOS 9
I have the next function which calls the API and retrieves the friend list in JSON format, convert the list in dictionary and append it to the Friendship NSObject
func GetFriends(completionHandler: ([FriendShip]?, NSError?) -> ()) {
Alamofire.request(Router.GetFriends(Test().getUserId())).responseJSON { (_, _, result) in
var friends = [FriendShip]()
switch result {
case .Success(let json):
if let responseObject = json as? [String: AnyObject], let hits = responseObject["hits"] as? [[String: AnyObject]] {
for dictionary in hits {
friends.append(FriendShip(dictionary: dictionary))
print(friends)
}
completionHandler(friends, nil)
}
case .Failure(_, let error):
completionHandler(nil, error as NSError)
}
}
the result of print(dictionary) is :
["_id": 546a6ef98e6df97032266, "friend": {
"_id" = 546a4b3e1f8d2c2630dd2;
name = "Daniela";
profileImageUrl = "https://api-static/profile/546a4b3e1f8d2c2630dd2.1.jpg";
statusTxt = "";
}]
["_id": 546a6f988e6df97032266, "friend": {
"_id" = 546a4ba51f8d2c2630dd2;
name = "Mara";
profileImageUrl = "https://api-static/profile/546a4ba51f8d2c2630dd2.1.jpg";
statusTxt = undefined;
}]
["_id": 546a70a18e6df97032266, "friend": {
"_id" = 546a4bd61f8d2c2630dd2;
name = "Alejandro";
profileImageUrl = "https://api-static/profile/546a4bd61f8d2c2630dd2.1.jpg";
statusTxt = "Marty";
}]
["_id": 546a715d8e6df97032266, "friend": {
"_id" = 546a4be01f8d2c2630dd2;
name = "Pedro";
profileImageUrl = "https://api-static/profile/546a4be01f8d2c2630dd1.1.jpg";
}]
classes Friendship and User
class FriendShip: NSObject{
var id: String?
var userId: String?
var user: User?
var friendId: String?
var friend: User?
var date: NSDate?
init(dictionary: [String: AnyObject]){
id = dictionary["id"] as? String
userId = dictionary["userId"] as? String
user = dictionary["user"] as? User
friendId = dictionary["friendId"] as? String
friend = dictionary["friend"] as? User
date = dictionary["date"] as? NSDate
}
override var description : String {
let friendString = friend!.name != nil ? friend!.name! : "nil"
let urlString = friend!.profileImageUrl != nil ? friend!.profileImageUrl! : "nil"
return "Friendship:\nfriend = \(friendString),\nurlString = \(urlString)"
}
}
class User: NSObject{
var id: String?
var name: String?
var statusTxt: String?
var profileImageUrl: String?
init(dictionary: [String: AnyObject]){
id = dictionary["id"] as? String
name = dictionary["name"] as? String
statusTxt = dictionary["statusTxt"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
}
override var description : String {
let nameString = name != nil ? name! : "nil"
let profileImageUrlString = profileImageUrl != nil ? profileImageUrl! : "nil"
return "Friendship:\nname = \(nameString),\nprofileImageUrl = \(profileImageUrlString)"
}
}
How can I know/view/print if the friends.append function is working properly and its filling OK all the properties of the Friendship NSObject?
If I understand you correctly you need a way to print / debug the contents of your Friendship class.
NSObject implements the NSObjectProtocol which has a computed property description. That property returns a String that represents the contents of the class. It is the text you see when you print an object. So your Friendship class already inherits this description protocol from NSObject. But in your case it only prints the class name, because that is the default implementation.
So if you want to have a more meaningful description of your class, you have to override the description property:
class Friendship: NSObject {
var name: String?
var age: Int?
override var description : String {
let nameString = name != nil ? name! : "nil"
let ageString = age != nil ? String(age!) : "nil"
return "Friendship:\nname = \(nameString),\nage = \(ageString)"
}
}
I don't know what your actual Friendship class looks like, so I made my own, very simple class that only contains a name and an age.
So now when you have 2 instances of Friendship (one filled and one empty) and you print them, you can see the contents of that objects:
let friendship1 = Friendship()
friendship1.name = "John"
friendship1.age = 34
print(friendship1)
let friendship2 = Friendship()
print(friendship2)
Prints:
Friendship:
name = John,
age = 34
Friendship:
name = nil,
age = nil
UPDATE
In your case the overridden description vars should look like this:
class FriendShip: NSObject{
var id: String?
var userId: String?
var user: User?
var friendId: String?
var friend: User?
var date: NSDate?
init(dictionary: [String: AnyObject]){
id = dictionary["id"] as? String
userId = dictionary["userId"] as? String
user = dictionary["user"] as? User
friendId = dictionary["friendId"] as? String
friend = dictionary["friend"] as? User
date = dictionary["date"] as? NSDate
}
override var description : String {
let friendDescription = friend != nil ? friend!.description : "nil"
let userIdString = userId != nil ? userId! : "nil"
return "Friendship:\nfriend = \(friendDescription),\nuserId = \(userIdString)"
}
}
class User: NSObject{
var id: String?
var name: String?
var statusTxt: String?
var profileImageUrl: String?
init(dictionary: [String: AnyObject]) {
id = dictionary["id"] as? String
name = dictionary["name"] as? String
statusTxt = dictionary["statusTxt"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
}
override var description : String {
let nameString = name != nil ? name! : "nil"
let profileImageUrlString = profileImageUrl != nil ? profileImageUrl! : "nil"
return "User:\nname = \(nameString),\nprofileImageUrl = \(profileImageUrlString)"
}
}
You can test it:
// test empty object
let friendship = FriendShip(dictionary: [String: AnyObject]())
print(friendship.description)
// test correct object
let user = User(dictionary: ["name": "John", "profileImageUrl": "http://image.com"])
let friendship2 = FriendShip(dictionary: ["friend": user, "userId": "1"])
print(friendship2.description)
Prints
Friendship:
friend = nil,
userId = nil
Friendship:
friend = User:
name = John,
profileImageUrl = http://image.com,
userId = 1

Resources