I am trying to cast a PFObject to a custom class in Swift and I read a lot of post about it and in all of then I need to inherit my class from PFObject. The problem is that my class is already inherited from NSObject and there is a conflict between then.
Is there another way to cast a PFObject to a custom class?
Usuario.swift
class Usuario: NSObject, NSCoding {
//MARK: Propriedades
var nome: String?
var foto: String?
var dataNascimento: Date?
var numeroTelefone: String?
var pais: PaisCodigo?
var telefoneE164: String?
var objectId: String?
var created: Date?
var updated: Date?
override init() {}
required init(coder aDecoder: NSCoder) {
nome = aDecoder.decodeObject(forKey: "nome") as? String
foto = aDecoder.decodeObject(forKey: "foto") as? String
dataNascimento = aDecoder.decodeObject(forKey: "dataNascimento") as? Date
numeroTelefone = aDecoder.decodeObject(forKey: "numeroTelefone") as? String
pais = aDecoder.decodeObject(forKey: "pais") as? PaisCodigo
telefoneE164 = aDecoder.decodeObject(forKey: "telefoneE164") as? String
objectId = aDecoder.decodeObject(forKey: "objectId") as? String
created = aDecoder.decodeObject(forKey: "created") as? Date
updated = aDecoder.decodeObject(forKey: "updated") as? Date
}
func encode(with aCoder: NSCoder) {
if let nomeUsuario = nome {
aCoder.encode(nomeUsuario, forKey: "nome")
}
if let fotoUsuario = foto {
aCoder.encode(fotoUsuario, forKey: "foto")
}
if let dataNascimentoUsuario = dataNascimento {
aCoder.encode(dataNascimentoUsuario, forKey: "dataNascimento")
}
if let numeroTelefoneUsuario = numeroTelefone {
aCoder.encode(numeroTelefoneUsuario, forKey: "numeroTelefone")
}
if let paisUsuario = pais {
aCoder.encode(paisUsuario, forKey: "pais")
}
if let telefoneE164Usuario = telefoneE164 {
aCoder.encode(telefoneE164Usuario, forKey: "telefoneE164")
}
if let objectIdUsuario = objectId {
aCoder.encode(objectIdUsuario, forKey: "objectId")
}
if let createdUsuario = created {
aCoder.encode(createdUsuario, forKey: "created")
}
if let updatedUsuario = updated {
aCoder.encode(updatedUsuario, forKey: "updated")
}
}
}
The Parse result using an objectId returns me this result:
<Usuario: 0x6080000abb20, objectId: 7NwpmD81w3, localId: (null)> {
nome = "Pablo Cavalcante";
numeroTelefone = 67992497386;
pais = "<PaisCodigo: 0x6080000abb80, objectId: rA5wdIWEFt, localId: (null)>";
telefoneE164 = "+5567992497386"; }
So it returns an Usuario object and I need to cast it.
You can use PFSubclassing. I see that you're declaring a user class so you could just subclass PFUser and then write something like this:
class User: PFUser, PFSubclassing {
//MARK: Propriedades
dynamic var nome: String?
dynamic var foto: String?
dynamic var dataNascimento: Date?
dynamic var numeroTelefone: String?
dynamic var pais: PaisCodigo?
dynamic var telefoneE164: String?
dynamic var objectId: String?
dynamic var created: Date?
dynamic var updated: Date?
}
Of course if you're using the init(with:) and encode(with:) you have to implement it...
Related
I have structure that consists of an a few variables.
struct Contact {
var id:String = "Contact - \(UUID())"
var fullname: String
var exercises : [Exercise]
}
The part i am interest in is the exercises section. This variable takes the following class:
class Exercise : NSObject , NSSecureCoding{
static var supportsSecureCoding: Bool = true
var excerciseName: String
var excerciseReps: String
var excerciseSets: String
var excerciseWeights: String
init(Name : String, Reps : String, Sets : String, Weights : String) {
excerciseName = Name
excerciseReps = Reps
excerciseSets = Sets
excerciseWeights = Weights
}
func encode(with aCoder: NSCoder) {
aCoder.encode(excerciseName, forKey: "excerciseName")
aCoder.encode(excerciseReps, forKey: "excerciseReps")
aCoder.encode(excerciseSets, forKey: "excerciseSets")
aCoder.encode(excerciseWeights, forKey: "excerciseWeights")
}
required convenience init?(coder aDecoder: NSCoder) {
let excerciseName = aDecoder.decodeObject(forKey: "excerciseName") as! String
let excerciseReps = aDecoder.decodeObject(forKey: "excerciseReps") as! String
let excerciseSets = aDecoder.decodeObject(forKey: "excerciseSets") as! String
let excerciseWeights = aDecoder.decodeObject(forKey: "excerciseWeights") as! String
self.init(Name: excerciseName, Reps: excerciseReps, Sets: excerciseSets, Weights: excerciseWeights)
}
}
In the view controller i want to access these variables in i have activated it:
var contacts = [Contact]()
My problem is when i am trying to access it it doesn't give me the option. when i type self.contacts. then nothing appears. i was expecting self.contacts.excercises to be there on auto fill. it that option isn't there. what am i missing?
This is my data in Firestore:
My comment model:
class CommentModel {
var commentText: String?
var commentDate: NSObject?
var uid: String?
var username : String?
var profileImageUrl: String?
init(dictionary: [String: Any]) {
commentText = dictionary["commentText"] as? String
commentDate = dictionary["commentDate"] as? NSObject
uid = dictionary["uid"] as? String
username = dictionary["username"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
}
}
My comments dictionary contains all documents of "comments".
XCode gives me the following suggestion, but I am unsure how to use it.
self.comments.sorted { (CommentModel, CommentModel) -> Bool in
}
How can I sort my dictionary by the commentDate?
You need
self.comments.sort { $0.commentDate < $1.commentDate }
make sure comments is declared as var , also cast it as a Date or String
commentDate = dictionary["commentDate"] as? Date
OR
commentDate = dictionary["commentDate"] as? String
Here is my code from my app called Reminders:
Folder Class:
class Folder
{
var name: String
var labelName: String
var imageName : String
var colour : ColourTheme
var colourArray : Array<UIColor>
var maxNumOfPages : Int
var text : Array<String>
var touchID : Bool
var isNewFolder : Bool
init (name: String, labelName: String, imageName: String, colour: ColourTheme, maxNumOfPages: Int, text: Array<Any>, touchID: Bool, isNewFolder: Bool)
{
self.name = name
self.labelName = labelName
self.imageName = imageName
self.colour = colour
self.colourArray = []
self.maxNumOfPages = maxNumOfPages
self.text = text as! Array<String>
self.touchID = touchID
self.isNewFolder = isNewFolder
}
Main VC:
func resetData ()
{
folderArray.removeAll()
let folder1 = Folder.init(name: "reminders", labelName: "reminders", imageName: "reminders(bell)(notifications)", colour: .light, maxNumOfPages: 10, text: [""], touchID: false, isNewFolder: false)
folderArray.append(folder1)
}
func saveData()
{
let myData = NSKeyedArchiver.archivedData(withRootObject: folderArray)
UserDefaults.standard.set(myData, forKey: "folderArrayD")
print("place saved")
}
func loadData()
{
let foldersData = UserDefaults.standard.object(forKey: "folderArrayD") as? NSData
if let foldersData = foldersData
{
let folderArray = NSKeyedUnarchiver.unarchiveObject(with: foldersData as Data) as? [Folder]
if let folderArray = folderArray
{
print(folderArray)
}
}
I am trying to save and load my folder objects array at different various points in my program.
At the moment it is giving this error:
NSForwarding: warning: object 0x280e3c600 of class 'Reminder.Folder' does not implement methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[Reminder.Folder replacementObjectForKeyedArchiver:]
I have looked everywhere to try fix this please could someone help, thank you!
Your Folder Model class need Inherits from NSObject and to adopt the protocol and its required methods for store data in UserDefaults.
like this:
class Folder: NSObject {
var name: String
var labelName: String
.....
init (name: String, labelName: String .....)
{
self.name = name
self.labelName = labelName
......
}
}
Your Folder class needs to be inherits from NSObject and NScoding and protocol required with encoder an Decoder
class Folder: NSObject, NSCoding {
var name: String
var labelName: String
.....
struct keys {
static let name = "name"
static let labelName = "labelName"
}
init (name: String, labelName: String .....)
{
self.name = name
self.labelName = labelName
......
}
required init(coder aDecoder: NSCoder) {
self.name = aDecoder.decodeObject(forKey: keys.name) as? String ?? ""
self. labelName = aDecoder.decodeObject(forKey: keys. labelName) as? String ?? ""
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.emailId, forKey: keys.name)
aCoder.encode(self.userId, forKey: keys.labelName)
}
}
Getting several "Cannot convert value of type Bool to expected argument type String" errors. The method for encoding expects a string but it is getting a Bool?
Here is the code. See the attached image for errors.
import Foundation
class Restaurant {
var name = ""
var item = ""
var location = ""
var image = ""
var isVisited = false
var phone = ""
var rating = ""
init(name: String, item: String, location: String, phone: String, image: String, isVisited: Bool) {
self.name = name
self.item = item
self.location = location
self.phone = phone
self.image = image
self.isVisited = isVisited
}
class func makeNewsItem(_ notificationDictionary: [String: AnyObject]) -> Restaurant? {
if let name = notificationDictionary["name"] as? String,
let phone = notificationDictionary["phone"] as? String,
let location = notificationDictionary["location"] as? String {
let date = Date()
let image = ""
let visited = false
let item = ""
let newsItem = Restaurant(name: name, item: item, location: location, phone: phone, image: image, isVisited: visited)
NotificationCenter.default.post(name: Notification.Name(rawValue: RestaurantTableViewController.RefreshNewsFeedNotification), object: self)
return newsItem
}
return nil
}
}
extension Restaurant: NSCoding {
struct CodingKeys {
static var Name = "name"
static var Item = "item"
static var Location = "location"
static var Image = "image"
static var IsVisited:Bool = false
static var Phone = "phone"
static var Rating = "rating"
}
convenience init?(coder aDecoder: NSCoder) {
if let name = aDecoder.decodeObject(forKey: CodingKeys.Name) as? String,
let location = aDecoder.decodeObject(forKey: CodingKeys.Location) as? Date,
let phone = aDecoder.decodeObject(forKey: CodingKeys.Phone) as? String {
let date = Date()
let image = aDecoder.decodeObject(forKey: CodingKeys.Image) as? String
let visited:Bool = aDecoder.decodeBool(forKey: CodingKeys.IsVisited) as? String
let item = aDecoder.decodeObject(forKey: CodingKeys.Item) as? String
self.init(name: name, item: item, location: location, phone: phone, image: image, isVisited: visited)
} else {
return nil
}
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: CodingKeys.Name)
aCoder.encode(location, forKey: CodingKeys.Location)
aCoder.encode(phone, forKey: CodingKeys.Phone)
aCoder.encode(item, forKey: CodingKeys.Item)
aCoder.encode(image, forKey: CodingKeys.Image)
aCoder.encode(isVisited, forKey: CodingKeys.IsVisited)
aCoder.encode(rating, forKey: CodingKeys.Rating)
}
}
You canĀ“t add a bool value to the forKey. This has to be a string value, so change it from:
aCoder.encode(isVisited, forKey: CodingKeys.IsVisited)
To:
aCoder.encode(isVisited, forKey: "IsVisited")
Same for:
let visited:Bool = aDecoder.decodeBool(forKey: CodingKeys.IsVisited) as? String
To:
let visited:Bool = aDecoder.decodeBool(forKey: "IsVisited") // note, no need for as? String here
Is it possible to create optional initialization parameters in Swift so I can create an object from JSON with the values returned from the API call, but then when I'm saving that object later I can also save the downloaded UIImage for one of the urls I got before.
Example:
class Story: NSObject, NSCoding {
var id: Int?
var title, coverImageURL: String?
var coverImage: UIImage?
required init?(anId: Int?, aTitle: String?, aCoverImageURL: String?) {
self.id = anId
self.title = aTitle
self.coverImageURL = aCoverImageURL
}
convenience init?(json: [String: Any]) {
let id = json["id"] as? Int
let title = json["title"] as? String
let coverImageURL = json["cover_image"] as? String
self.init(
anId: id,
aTitle: title,
aCoverImageURL: coverImageURL,
)
}
Then Later I want to save objects to memory
//MARK: Types
struct PropertyKey {
static let id = "id"
static let title = "title"
static let coverImageURL = "coverImageURL"
static let coverImage = "coverImage"
}
//MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: PropertyKey.id)
aCoder.encode(title, forKey: PropertyKey.title)
aCoder.encode(coverImageURL, forKey: PropertyKey.coverImageURL)
aCoder.encode(coverImage, forKey: PropertyKey.coverImage)
}
required convenience init?(coder aDecoder: NSCoder) {
guard let id = aDecoder.decodeObject(forKey: PropertyKey.id) as? Int else {
os_log("Unable to decode the id for a Story object.", log: OSLog.default, type: .debug)
return nil
}
guard let title = aDecoder.decodeObject(forKey: PropertyKey.title) as? String else {
os_log("Unable to decode the title for a Story object.", log: OSLog.default, type: .debug)
return nil
}
let coverImageURL = aDecoder.decodeObject(forKey: PropertyKey.coverImageURL) as? String
let coverImage = aDecoder.decodeObject(forKey: PropertyKey.coverImage) as? UIImage
self.init(
anId: id,
aTitle: title,
aCoverImageURL: coverImageURL,
coverImage: coverImage,
)
}
Does this make sense? I want to be able to save a Story object as soon as I get the response from the API, but later when I save the story to memory, I want to be able to save the fetched UIImage for the coverImage.
How would I do that?
I'm not sure why no one took the easy points on this answer, but the answer is to simply make your properties optionals, and then you can set them with a value, or nil. You can also create convenience initializers that automatically set certain values to nil if you want. So, using my app as an example, I have a model that gets built from an API call. that model has values like id, created_at, etc that don't exist until a record is saved to the server, but I create objects locally, store them, and eventually send them to the server, so I need to be able to set the above values only when creating an object from JSON, so here is what I did:
class Story: NSObject, NSCoding {
var id: Int?
var title, coverImageURL: String?
var coverImage: UIImage?
required init?(anId: Int?, aTitle: String?, aCoverImageURL: String?) {
self.id = anId
self.title = aTitle
self.coverImageURL = aCoverImageURL
}
convenience init?(json: [String: Any]) {
let id = json["id"] as? Int
let title = json["title"] as? String
let coverImageURL = json["cover_image"] as? String
self.init(
anId: id,
aTitle: title,
aCoverImageURL: coverImageURL,
)
}
convenience init?(aTitle: String, aCoverImage: UIImage?) {
let title = aTitle
let subtitle = aSubtitle
let coverImage = aCoverImage
let isActive = activeStatus
self.init(
anId: nil,
aTitle: title,
aCoverImageURL: nil,
aCoverImage: coverImage,
)
}
As you can see, I only set two of the values when I'm creating an object locally, and the other values are just set to nil. To allow a value to be set to nil, just make it an optional when setting it. Simple!