I am trying to use Parse-Swift SDK, and specifically in the app database I have some properties which are dictionaries, like this one:
elements: Dictionary<String, Any>
["test_string": "hello", "test_number": 22.2] // an example of a value for this property
Now I tried to write this ParseObject in Swift:
import Foundation; import ParseSwift
struct TestObject: ParseObject {
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?
var originalData: Data?
var elements: [String: Any]?
}
But doing this, I get these errors:
Type 'TestObject' does not conform to protocol 'Decodable'
Type 'TestObject' does not conform to protocol 'Hashable'
Type 'TestObject' does not conform to protocol 'Encodable'
Type 'TestObject' does not conform to protocol 'Equatable'
What should I do? Thank you for your help
Any isn’t Codable meaning it doesn't conform to the Codable protocol and can't be sent to a Parse Server using JSON which is why the compiler is complaining.
If you want something similar to Any, you can add the AnyCodable package via SPM to your project and use the following types: AnyCodable, AnyEncodable, AnyDecodable. See the notes in the documentation and more info here and here. So your object will look like:
struct TestObject: ParseObject {
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?
var originalData: Data?
var elements: [String: AnyCodable]?
}
To access the value of AnyCodable, AnyEncodable, AnyDecodable; you use the .value property and attempt to cast it to the expected type. You can check the ParseSwift test cases for examples. Using your TestObject type:
var object = TestObject()
object.elements["myKey"] = AnyCodable("My value")
guard let storedValue = object.elements["myKey"]?.value as? String else {
print("Should have casted to String")
return
}
print(storedValue) // Should print "My value" to console
Related
I'm trying to remove "songDict" from "libraryArray" but it triggers an error.
var libraryArray = UserDefaults.standard.value(forKey: "LibraryArray") as! [Dictionary<String, Any>]
var songDict = Dictionary<String, Any>()
var arr = libraryArray.filter {$0 != songDict}
And here's the error.
Value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols
As the error says, you cannot compare two dictionaries like that as they dont conform to Equatable protocol. It will be better to use a struct for your data model instead of Dictionary.
struct Library: Equatable {
let id: String
...
}
But if you don't want to do that, you can still check for equality with your dictionaries by equating the value of any keys in it.
var arr = libraryArray.filter { (dict) -> Bool in
dict["id"] as? String == songDict["id"] as? String
}
I need to save a list of images as NSData in Realm. I tried using Realm optional but realmOptional<NSdata> can't be used because realmOptional does not conform to type NSDate.
Is there a way to do it?
Edit: Basically all I want is to be able to store a list of NSData but optional
something like:
#objc dynamic var photos: List<NSData>?
Solution for optional List types when using Decodable
In my case I needed an optional List because I'm decoding json into Realm objects, and it's possible that the property might not exist in the json data. The typical workaround is to manually decode and use decodeIfPresent(). This isn't ideal because it requires boilerplate code in the model:
class Driver: Object, Decodable {
var vehicles = List<Vehicle>()
var name: String = ""
// etc
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.vehicles = try container.decodeIfPresent(List<Vehicle>.self, forKey: .vehicles) ?? List<Vehicle>()
self.name = try container.decode(String.self, forKey: .name)
// etc
}
}
However, we can avoid the boilerplate by extending KeyedDecodingContainer with an overload for List types. This will force all lists to be decoded using decodeIfPresent():
Realm v5:
class Driver: Object, Decodable {
var vehicles = List<Vehicle>()
var name: String = ""
//etc
}
class Vehicle: Object, Decodable {
var drivers = List<Driver>()
var make: String = ""
// etc
}
extension KeyedDecodingContainer {
// This will be called when any List<> is decoded
func decode<T: Decodable>(_ type: List<T>.Type, forKey key: Key) throws -> List<T> {
// Use decode if present, falling back to an empty list
try decodeIfPresent(type, forKey: key) ?? List<T>()
}
}
Realm v10+:
class Driver: Object, Decodable {
#Persisted var vehicles: List<Vehicle>
#Persisted var name: String = ""
// etc
}
class Vehicle: Object, Decodable {
#Persisted var drivers: List<Driver>
#Persisted var make: String = ""
// etc
}
extension KeyedDecodingContainer {
// This will be called when any #Persisted List<> is decoded
func decode<T: Decodable>(_ type: Persisted<List<T>>.Type, forKey key: Key) throws -> Persisted<List<T>> {
// Use decode if present, falling back to an empty list
try decodeIfPresent(type, forKey: key) ?? Persisted<List<T>>(wrappedValue: List<T>())
}
}
Edit: Clarified code examples, fixed typo
The List cannot be optional, but the Objects in the list can be optional, you have to declare it like:
#Persisted var value: List<Type?>
Here is the link with the Supported data types
https://www.mongodb.com/docs/realm/sdk/swift/data-types/supported-property-types/
according to https://realm.io/docs/swift/latest/#property-cheatsheet you can not define optional lists in realm
I'm trying to to access the description key in my APIKeyObjects, but I have been getting multiple issues through the syntax.
struct NewsApiObject: Codable
{
let status: String?
let totalResults: Int?
let articles: [APISKeyObjects]
}
struct Source: Codable
{
var id: String?
var int: Int?
}
struct APISKeyObjects: Codable
{
let source: Source?
let author: String?
let title: String?
let description: String?
let url: String?
let urlToImage: String?
let publishedAt: String?
let content: String?
}
I received the following error message when I attempt to assign "description" which is an optional String in my Struct named "APIKeyObjects"
Error message: "Cannot subscript a value of type [APIKeyObjects] with an index of type 'String'"
let financesNewsData = try JSONDecoder().decode(NewsApiObject.self, from: data)
Right after I'm trying to assign the variable to a UITextView Property named UITextDisplay which only accepts String..
UITextDisplay.text = financesNewsData.articles[description]
Error message: "Cannot subscript a value of type [APIKeyObjects] with an index of type 'String'"
So my question is; what's the proper way to cast it in order to assign the string value to a string property?
This is wrong String var, it should be var variableName = ...
And in your scenario, it should be:
// To get all articles:
var articles = financesNewsData.articles
// If you want to get a description of an article then
let description = articles[index].desccription
Is there a way to access the variable in a class using a String? E.g. I have the following struct Person.swift:
struct Person: Codable {
let name: String?
let id: String?
}
Can I access the variable name and change the variable using a some sort of string-reflection method, like Java, instead of a call to the Person.name = "" method?
You can access props as strings using KVC
The only downside is It's originally from Objects c so you can not use structs, and your props have to be Objective C dynamic types:
class Person: NSObject, Codable {
#objc dynamic var name: String = ""
}
let person = Person.init()
person.name = "Mat" // Name value "Mat"
person.setValue("Josh", forKey: "name") // Name value "Josh"
I have a class that inherits from AWSDynamoDBModel and adheres to AWSDynamoDBModeling protocol. Example:
class ProfileDatabaseModel : AWSDynamoDBModel, AWSDynamoDBModeling {
var userid: String
var firstName: String
var lastName: String
var someOtherStuff: [String: String] // IS THIS OK?
// assume some other details here, like initializations
class func dynamoDBTableName() -> String! {
return "atable"
}
class func hashKeyAttribute() -> String! {
return "userid"
}
}
With a class like this, I can perform the following few lines that update the DynamoDB with the data in an instantiation of this class:
var db = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var model = ProfileDatabaseModel()
// fill in model data
let task: BFTask = db.save(model)
My question is: Can I have that dictionary in the object? Will that work with the object mapper? If not as a Swift dictionary, would it work as an NSDictionary? Or, do I need to translate it to a JSON format?
Currently, AWSDynamoDBObjectMapper supports:
NSNumber
NSString
NSData
NSArray of the above three datatypes
NSDictionary is not supported, but we are evaluating how we can support the map datatype in the coming releases.