0
votes

I'm trying to persist in a table view cell, the result of a quiz test with questions and I needed the array of answers given (String Array) so I decided to use RealmSwift.

I created this class and of course I created also a RealmString object in the same file to handle the possibility to persist arrays of String in Realm in this way:


class RealmString: Object {
    dynamic var stringValue = ""
}

class Test: Object {
    
    @objc dynamic var ID = UUID().uuidString
    @objc dynamic var testScore : String = String()
    
    @objc dynamic var testTitle : String = String()
    @objc dynamic var testSubTitle : String = String()
    
    @objc dynamic var dateOfExecution: String = String()
    
    @objc dynamic var answersGiven: [String] {
            
            get {
                return _backingAnswersGiven.map { $0.stringValue }
            }
            set {
                _backingAnswersGiven.removeAll()
                _backingAnswersGiven.append(objectsIn: (newValue.map({ RealmString(value: [$0]) })))
          }
       
            
    }
    
    let _backingAnswersGiven = List<RealmString>()
    
    override static func ignoredProperties() -> [String] {
           return ["answersGiven"]
    }
    
    
    override static func primaryKey() -> String? {
        return "ID"
    }
    

Now in the view controller:

I have a variable that stores the result (is an Int array that will take ten answers with values from 0 to 5, and these will later be converted to String) i.e.: [0,2,2,3,4,5,2,1,0,2] -> ["0","2","2","3","4","5","2","1","0","2"]

and when an option is selected in a question the value is set with this function, everything works fine.

public var questionResults: [Int] = []

func setValueToQuestion(questionNumber: Int) {
   questionResults[questionNumber] = optionChosen
}

When the test is completed successfully everything is saved in this way:

  let test = Test()
  test.ID = currentTest?.ID ?? UUID().uuidString
  test.testTitle = testTitleLabel.text!
  test.testScore = resultNumberLabel.text!
  test.testSubTitle = resultLabel.text! 
  test.dateOfExecution = dateTimeString
  test.answersGiven = questionResults.map({String($0)})
       
  DBManager.sharedInstance.addData(object: test)
    

I tried the code separately also adding breakpoints and everything works in the flow, expect this line:

test.answersGiven = questionResults.map({String($0)})

that raises the error shown in the title: "Invalid array input: more values (1) than properties (0)."

I guess it can be an error of mapping maybe?

This value is then treated in the rest of flow as a simple swift array of String = [String]

1

1 Answers

0
votes

There are a few issues which may be leading to that error.

First the RealmString property is not persisted because it needs @objc

dynamic var stringValue = ""

should be

@objc dynamic var stringValue = ""

Secondly, and this is important, Realm does not support primitives in Lists. Well, it kinda does but not very well.

EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.

See my answer to this question but in a nutshell, you need another class to store the string in - kind of like your RealmString class.

class StringClass: Object {
    @objc dynamic var myString = ""
}

and then change the Test object property to use the StringClass property

@objc dynamic var answersGiven = [StringClass]()

and then I see you're trying to use a backed var and computed property but I am not sure why. It may be simpler to use use the var itself

let _backingAnswersGiven = List<RealmString>()

since the List collection already handles what's being computed.

For example, if you set the list you can set it to another list (which wipes out the current list). Or when you get the list let myStringList = test._backingAnswersGiven, gets all of the StringClasses in the list without having to .map over them.