Save my own class as an array in Parse - arrays

I have a class named Pet. I collect it in an array. Then I try to save it like below code. I also tried inheriting NSObject to the Pet class with no use. Is it not possible to save my own class of arrays in Parse or am I doing something really wrong? Note that the current user exists and valid.
Somewhere in code defined:
var pets : [Pet] = [Pet]()
populated:
pets.append(newPet)
and tried to save:
#IBAction func saveTapped(sender: UIBarButtonItem) {
PFUser.currentUser().setObject(pets, forKey: "pets")
user.saveInBackgroundWithBlock({ (success, error) -> Void in
if error == nil {
DLog("Suceess saving")
} else {
displayAlertWithTitle(self, nil, error.description)
DLog(error)
}
})
}
The error I am getting is:
2015-02-17 18:28:59.906 Patikoy[38292:2121589] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Patikoy.Pet)'
p.s. I am a beginner in Swift as well as Parse. Any help is appreciated.

Parse is likely trying to serialize the "pets" array into JSON. However, your "Pet" class is not serializable to JSON.
From the NSJSONSerialization Class Reference:
An object that may be converted to JSON must have the following
properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
You need to create a method on your Pet class that converts it to a Dictionary, as described in this Stack Overflow answer.

Related

Swift: Cannot add element in Array from another object

I am struggeling with swift syntax . I want to add objects to an array but I have syntax errors.
The array is located in class Document, and the class that should add objects is in class Viewcontroller.
The array is of type Content:
public class Content: NSObject {
#objc var bankAccSender: String?
#objc var bankAccReceiver: String?
Declaration snippest in Document:
class Document: NSDocument {
var content=[Content]()
override init() {
super.init()
self.content = [Content]()
// force one data record to insert into content
content += [Content (… )] // checked with debugger
The ViewController has assigned the represented Object
contentVC.representedObject = content
But adding data in ViewController gives a compiler error „Type of expression is ambiguous without more context“:
var posting = Content(…)
self.representedObject.append(posting)
Hope you can help..
You can't append an element to an object of type Any. What you need is to replace the existing value with a new collection:
representedObject = (representedObject as? [Content] ?? []) + CollectionOfOne(posting)
representedObject is of type Any?, which is a very difficult type to work with in Swift. Since you already have a content property, I would probably adjust that, and then re-assign it to representedObject.
You can also try this (untested), as long as you are certain that the type is always [Content]:
(self.representedObject as! [Content]).append(posting)
It's possible you'll need some more complex like this:
(self.representedObject as! [Content]?)!.append(posting)
As I said, Any? is an incredibly obnoxious type. You probably want to wrap this up into a function. Or I you can avoid using representedObject, then I would recommend that. In many cases you don't need it. It's often just a convenience (in ObjC; in Swift, it's very hard to use).

Filtering an array of objects by class not working

I have a UIStackView that contains UIViews or objects of a class I have created called MyView.
MyView is a subclass of UIView.
I want to extract from that array, all objects of class MyView.
This is what I have tried and the respective errors:
let views = Array< MyView >(allViews).filter { $0 is MyView }
type of expression is ambiguous without more context
I love these messages that say nothing.
let views = Array<Any>(allViews).filter { $0 is MyView }
I love how this compiles with Any.
No error in this case, but views contains the same objects as myViews, nothing is being filtered.
I understand that MyView is a subclass of UIView, so what swift is testing here is if the object is of class UIView. If this is true, why bothering allowing programmers to specify any class on the filter, if it can only filter some classes?
Is there a way to test for subclasses?
I'm guessing allViews is an array of UIView.
You should use compactMap, which will map to an array of the subclass, throwing away any nil values (which result from the as?):
let views = allViews.compactMap { $0 as? MyView }
Note here views is already of type [MyView]; take a look at compactMap's method signature to understand:
func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

Swift and NSCoding: Encoding an array of objects conforming to a class-only protocol

I have a class, StandardObject, that conforms to the protocol Object. Another class, ObjectManager, has a property called objects which is an array containing instances of Object. Both StandardObject and ObjectManager are subclasses of NSObject and conform to NSCoding.
When I try to encode the objects property of ObjectManager in encodeWithCoder:, I receive an error:
cannot convert value of type '[Object]' to expected argument type 'AnyObject?'
Here is my code:
ObjectManager:
class ObjectManager: NSObject, NSCoding {
var objects = [Object]()
required init?(coder aDecoder: NSCoder) {
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(objects, forKey: "Objects") //// ERROR HERE
}
}
Object protocol:
protocol Object: class, NSCoding {
// Currently completely empty
}
StandardObject:
class StandardObject: NSObject, Object {
required init?(coder aDecoder: NSCoder) {
}
func encodeWithCoder(aCoder: NSCoder) {
}
}
I know it has to do with the fact that you can only encode objects (or arrays of them), not structs, enums, or in this case, protocols. However, the Object protocol's declaration is:
protocol Object: class, NSCoding
which means that only classes can conform to this protocol.
Shouldn't that mean that there are only instances of classes in the objects array? Why can't it be encoded?
I have also tried casting the array to an NSArray before encoding, but I get this error:
cannot convert value of type '[Object]' to type 'NSArray' in coercion
So here are my two questions:
If I made the Object protocol only have objects conform to it, shouldn't an array of Object be an array of objects?
If question 1 isn't possible for some reason, how do you convert the array of Object to be able to be encoded with NSCoding?
You need to declare your protocol as #objc, to place it into the Objective-C world:
#objc protocol Object: class, NSCoding {
The compiler will then know he will be able to toll-free bridge the Swift array with a NSArray, as you'll be able to build-up the array only with instances of classes derived from NSObject.

Swift Array extension with optionals

I've created simple extension for Array which will append only unique elements. It works fine until I want to work with an array of optionals. Since that I'm keep getting error
Type 'Event?' does not conform to protocol 'Equatable'
Event class
import RealmSwift
class Event: Object,Equatable {
dynamic var id = ""
}
func ==(lhs: Event, rhs: Event) -> Bool {
return lhs.id == rhs.id
}
Extension
extension Array where Element : Equatable {
mutating func appendUniqueContentOf(elements:[Element]){
for ele in elements {
if (!contains(ele)){
append(ele)
}
}
}
}
Usage
var newEvents:[Event] = someEvents()
var events = [Event?]()
events.appendUniqueContentOf(newEvents)
Question
I don't understand this situation. Event class conform that protocol. I've also tried adding other combination of == function, but without success.
I don't know how to approah this issue. Is it matter of my extension? How I should properly approach it? Could you show me right track?
Event? is syntactic sugar for Optional<Event>. Since Optional does not conform to Equatable, neither will Optional<Event>.
Though possible, I highly discourage implementing Equatable for Optional. This being said, you should probably rethink and try using just Array<Event>.

SWIFT - How to get the type of an Array?

i'm writing a generic method who take a Dictionary and a given type in parameters for build an object.
For example, if you make a SOAP request for get a movie and put away the response in Dictionary you can make:
var movie : Movie = myGenericMethod(dic : Dictionary, objectToIntrospect : Movie()) as Movie
It's work with:
Simple Object
Complex Object
But i have a problem if you have an array of object.
So imagine your movie object contains an Array of Actors...
With reflection, i get all type of my class's attributes.
With this, i build an Array of Any object which contains my types.
For example, an object contained in other object (Actor in a Movie):
//All type of attributes of my movie object, at index [i] i have my "Actor" object
var anyType : Any = typesOfProperties[i]
//I cast it in object
var objectType : NSObject = anyType as NSObject
//Dont worry about this method, it's just for get the dictionary
var otherDico : NSDictionary = ConverterUtilities.extractDictionaryFromOtherDictionary(dict, dicoName: property, soapAction: soapAction) as NSDictionary
//I build the Actor object with the data of the dictionary. objectType.self give the Actor type
var propertyObject: NSObject = self.buildAnyObjectWithDictionary(otherDico, soapAction: "", objectToIntrospect:objectType.self) as NSObject
//I set the property Actor in my Movie object (myObjectToReturn)... The "property" parameter is the key
ConverterUtilities.setPropertyValue(property, value: propertyObject, objectToReturn : myObjectToReturn, isObject : true)
It's work perfectly... If i have just one actor in my movie object, the "propertyObject" will be an Actor type, and this cause objectType is an Actor object.
But, if i have an array, i'm redirect in method who treat Array, and my objectType return "Swift._NSSwiftArrayImpl" and my anyType object return "([myproject.Actor])".
I dont need to know this is just an Array, cause i know it. But i need to know that's an Array of Actor for build some Actor object dynamicly !
This is what i have for the moment:
var objToAdd: NSObject = self.buildAnyObjectWithDictionary(newDic, soapAction: "", objectToIntrospect: Actor()) as NSObject
arraySpecific.append(objToAdd)
As you can see, this work perfectly if i hardcode the type. But i need to make it like the previous example ! Like that:
var objToAdd: NSObject = self.buildAnyObjectWithDictionary(newDic, soapAction: "", objectToIntrospect: anObjectWithActorType) as NSObject
arraySpecific.append(objToAdd)
(Difference between first and second version is the objectToIntrospect parameter)
Do you know how can i use my Any object (contains: ([myproject.Actor]) for build an instance of one Actor ?
I really need ur help for this ! Regards !
PS: sorry for my bad english, i hope u understand me :)
Ok guys, i come back on my post cause i found a solution:
i create a master class, and all of my classes herit of this class.
This super-class have a method name getArrayType.
If one of my children-class need to be used with my generic method and if she contain a Array, they need to override the getArrayType.
Like that:
override func getArrayType(anyType : Any) -> String {
var className = " "
if(anyType as? NSObject == rows){
className = Properties.PROJECT_NAME + ".SimpleRow"
}
return className
}
And i call this method like that (TObject is my super-class):
var className = (objectToIntrospect as TObject).getArrayType(anyType)
var obj: AnyClass! = NSClassFromString(className)
This work perfectly, but you need to override a method. I hope this help u if u got the same problem !

Resources