I have a Swift Realm database that I’m attempting to find a specific record which will occupy a number of Labels in a UIViewController - no Tableview. In essence, I want to search the database for a record based on a String variable consisting of a date and time. The string format looks like this “Feb19,21-15:47” but changes with each new record added - hence why I need to use a string variable as a search parameter.
Once the record is found I want to then grab the entire record associated with the search string and parse out each field to fill the five Labels on the VC. I’ve been trying for hours to get this to work but I’m just not getting the result I need.
My questions are:
How do I format the search parameter to find the record using tempPhotoTitle?
Once the search finds the object property (tempPhotoTitle) in the database what code needs to be employed to grab all the associated properties in the same record/row so I can bind each property to its associated Label in the VC.
A couple notes: I did employ an auto updating primary key in each record named ‘id’. When using a Tableview I can access each record by using indexPath.row but since I’m not using a TV this isn’t available. The tempPhotoTitle string value is fed from another VC via a segue. Also only one record in the DB will have the search value. Here is some abbreviated code to provide the gist of my issue. The search doesn’t work (parsing issue) and as a result I can’t test the remaining code. I sure would appreciate some assistance on this. Many thanks and frustrated Roger
import UIKit
import RealmSwift
class Snapshot: Object {
#objc dynamic var id = 0
#objc dynamic var date: String = ""
#objc dynamic var cTime: String = ""
#objc dynamic var airTemp: String = ""
#objc dynamic var humidity: String = ""
#objc dynamic var photoTitle: String = ""
override class func primaryKey() -> String {
return "id"
}
convenience init( ….)
}
class RecordsVC: UIViewController {
var tempPhotoTitle: String = ""
var tempImage = UIImage()
#IBOutlet weak var eDateHolder: UILabel!
#IBOutlet weak var eTimeHolder: UILabel!
#IBOutlet weak var eAirTempHolder: UILabel!
#IBOutlet weak var eHumidityHolder: UILabel!
var editSnapShotItems: Results<Snapshot>?
override func viewDidLoad() {
super.viewDidLoad()
queryRecords()
}}
func queryRecords() {
let realm = try! Realm()
let allRecords = realm.objects(editSnapShotItems.self)
let recordResult = allRecords.filter("photoTitle CONTAINS[cd] %#", tempPhotoTitle)
let recordResults = allRecords.filter(tempPhotoTitle)
for record in recordResults {
eDateHolder.text = record.date
eTimeHolder.text = record.cTime
eAirTempHolder.text = record.airTemp
eHumidityHolder.text = record.humidity
}}}
Here's a simplified version of your object for this answer
class Snapshot: Object {
#objc dynamic var date: String = ""
}
if you create an object
let snap = Snapshot()
snap.date = "Feb19,21-15:47"
and then store it in Realm
let realm = try! Realm()
realm.write {
realm.add(snap)
}
and then you want to find that later
let snapResults = realm.object(Snapshot.self).filter("date == %#", "Feb19,21-15:47")
for snap in snapResults {
print(snap.date)
}
the console output will be
Feb19,21-15:47
But...
There are a number of issues with storing dates in that format. For example, what if you want to sort three dates Jan20,21-15:57, Jan21,21-15:57, Feb19,21-15:57. Logically they are in order but because they are strings, they will sort with Feb19 first (F is before J in the alphabet).
There are a number of solutions: Realm fully supports NSDate so you can just store them as an actual date or if you really want to use strings, store them in a sortable format
202102191547
would be Feb 19th 2021 at 15:47, making sure single digits are padded with a 0. This allows them to be ordered, filtered etc correctly
Related
I'm learning Swift and I'm wondering how can I create a data structure with multiple values and pass descriptions values from UITableViewController to another viewController? I have tried like this
struct faculty {
var name = String()
var descriptions = (String)[]
}
let faculties = [name: "Faculties", description: ["Study1", "Study2"]]
I have successfully managed to list an array ["Test1", "Test2"] in tableView.
There are a couple of issues
An empty string array is [String]().
description is not equal to descriptions.
An instance must be created with Type(parameter1:parameter2:).
And structs are supposed to be named with starting capital letter.
struct Faculty {
var name = String()
var descriptions = [String]()
}
let faculties = [Faculty(name: "Faculties", descriptions: ["Study1", "Study2"])]
However default values are not needed. This is also valid
struct Faculty {
let name : String
var descriptions : [String]
}
I started trying Realm for IOS, so I created two classes:
Files Model
import Foundation
import RealmSwift
class FilesModel: Object {
#objc dynamic var id : Int = 0
#objc var fileName = ""
#objc dynamic var dateOfCreation = Date()
#objc dynamic var dateOfModification = Date()
#objc dynamic var type = ""
var file = List<Data>()
}
Groups Model
import Foundation
import RealmSwift
class GroupsModel: Object {
#objc dynamic var id : Int = 0
#objc dynamic var name = ""
#objc dynamic var dateOfCreation = Date()
#objc dynamic var dateOfModification = Date()
#objc dynamic var filesCount = Int()
var files = List<FilesModel>()
override static func primaryKey() -> String? {
return "id"
}
}
Now the thing is I am copying files into groups model file Object but I need to delete the parent object.
think of it as a move I am moving files into the folder.
what I have done is I save a copy of the file into the folder and delete the file from outside the folder.
Problem
when I delete the file outside the folder it will also delete the file inside.
My understanding of the problem
classes is a reference type so I am copying reference. So when I delete the reference it will delete the file from the whole project.
I have tried many solutions like deep copy and detached.
Thanks in Advance.
The issue is this statement
I am moving files into the folder
While that may be what you want to do, the objects you're moving aren't actually being moved. When you add a managed object to a a List property, it adds a reference to the object, not the object itself.
In other words, when a FilesModel is added to a GroupsModel files List property, it's a reference to the object. If you delete the FilesModel, it will also be gone from the List. But, removing it from the List does not change the original object.
However, Lists can also contain Embedded Objects - an embedded object only ever exists within it's parent object. Perhaps that would be an option?
So like this. Here's a couple of models
class FileModel: EmbeddedObject {
#Persisted var fileName = ""
}
class GroupsModel: Object {
#Persisted var files = List<FilesModel>()
}
Then instantiate a group and add a couple of file objects to it
let group = GroupsModel()
let f0 = FileModel()
f0.fileName = "some filename"
let f1 = FileModel()
f1.fileName = "another filename"
group.files.append(objectsIn: [f0, f1])
results in a group with two files that only ever appear within that group.
Another option is to simply make a copy of the object
let objectCopy = MyObject(value: objectToCopy)
and then add the objectCopy to your Groups model. But again, by doing that Realm will instantiate the objectCopy and actually add it back to Realm (possibly overwriting the original object).
I am using the following code in XCode 12/Swift 5to try to do the following:
Receive a variable passed from one view controller to this one (passedVerb, and this works)
Look up items in a dictionary based on the "passedVerb" variable that is passed. This will change depending on which verb the user was on in the main view controller, so I need this to be able to switch it out.
When I run the below code in playground it works well
import UIKit
var yekol = [0: "yekol",
1: "bekol",
2: "btekol"]
var youm = [0: "youm",
1: "boum",
2: "btoum"]
var yekol2 = ["inf": "yekol",
"ana": "bekol",
"enta": "btekol"]
var verbs = [yekol, youm]
var randVerb = verbs.randomElement()
//depending on which random verb is chosen, look up in the right dictionary above
var anaForm = randVerb![1]!
var entaForm = randVerb![2]!
print(anaForm as Any)
print(entaForm as Any)
But when I try to do this within the view controller in my main project, I'm not able to used the "passedVerb" in the same way that I could use the "randVerb" to look up in the right dictionary:
import UIKit
class AboutViewController: UIViewController {
//variables
var anaForm = ""
var entaForm = ""
var nehnaForm = ""
//a variable to hold the data from the first ViewController
var passedVerb = ""
let yekol = [0: "yekol",
1: "bekol",
2: "btekol",
3: "mnekol"]
let edros = [0: "edros",
1: "bedros",
2: "btedros",
3: "mnedros"]
func updateTable() {
//below works, but only because I'm explicitly calling the right dictionary
var anaForm = yekol[1]
//neither of the below forms work in the same way as in playground. i get a 'subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead. i can't for the life of me figure out how to fix this despite the warning
var entaForm = passedVerb![2]!
var nehnaForm = passedVerb[3]
anaLabel.text = anaForm
entaLabel.text = entaForm
nehnaLabel.text = nehnaForm
}
}
#IBOutlet weak var currentVerb: UILabel!
#IBOutlet weak var anaLabel: UILabel!
#IBOutlet weak var entaLabel: UILabel!
#IBOutlet weak var nehnaLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
currentVerb.text = passedVerb
updateTable()
}
}
Any insight or help would be really appreciated. I've been learning Xcode/Swift as my first coding language and so hope this isn't(maybe is?) too obvious an issue.
Thanks in advance!
you are probably trying to pass a [Int : String]? to passedVerb. But you declare var passedVerb = "" as a String in AboutViewController.
So the code in AboutViewController is trying to find an index into a String, which you cannot do.
Hence the error message you get. Try declaring "passedVerb" like this:
var passedVerb: [Int : String]? = [:]
and use it like this:
var entaForm = ""
if let vb = passedVerb?[2] {
entaForm = vb
}
similarly for "nehnaForm".
If you want help passing a dictionary containing multiple verb forms from one view controller to the next, you need to post your current code that hands the verb to the other view controller.
Are you using a segue and prepare(for:sender:)? Add your code that invokes the second view controller and passes the verb.
One thing that I notice: In AboutViewController you declare passedVerb as an empty String. However, in your playground code, your verbs are of type [Int:String] (a dictionary with Int keys and String values.
You should change the declaration of passedVerb to be:
var passedVerb = [Int:String]()
Edit:
That is why you are getting the error that you are getting:
subscript(_:)' is unavailable: cannot subscript String with an Int, use a String.Index instead.
You declared your passedVerb as a String, and then you try to index into it as if it is a dictionary.
You should also stop using ! force unwrap operators. Those will crash your code if the dictionary doesn't contain the specified key.
Here is my code for my object that I am storing in Realm Database
class Accomp2: Object {
#objc dynamic var title: String = ""
#objc dynamic var date: Date!
#objc dynamic var month:String = ""
required init() {
}
#objc dynamic var body:String = ""
#objc dynamic var type:String = "Professional"
#objc dynamic var identifier:String = ""
}
I want to be able to store the attachment in a variable called attachment, but I am not sure what type of variable that is.
With the object, I want to be able to show this attachment on a view controller as a picture.
If this is not possible with Realm Database, is there another way to store attachments?
See the Realm property Cheat Sheet
What you want is the NSData() e.g. Swift Data() object - he's some simple code as a conceptual example.
let url = //your file
let myData = Data(contentsOf: url) //please handle the optional correctly
class Accomp2: Object {
#objc dynamic var acc_id = UUID().uuidString
#objc dynamic var name = ""
#objc dynamic var value = Data()
}
let x = Accomp2()
x.name = "Some name"
x.value = myData
IMPORTANT!
While you can do this, you probably shouldn't.
Realm is not well suited for handling documents within objects. If it's a thumbnail or a couple hundred kb, it will be ok. But PDF's can get HUGE and Realm is not the right solution.
You should store files in another source that's designed for storing files - Firebase Cloud Storage is one such option and then store the URL to the file within your Realm object.
I am trying to set up an NSOutlineView using a TreeController.
I have the following class for each node in the tree:
#objc class EdLevel: NSObject{
#objc dynamic var isLeaf: Bool { return children.count == 0}
#objc dynamic var childCount: Int { return children.count}
#objc dynamic var children: [EdLevel] = []
#objc dynamic var name: String
#objc init(name n: String){
name = n
}
}
In my Xcode story board I have done the following settings:
I currently have the following property in my view controller (called Admin in IB)
#objc dynamic var eddingtonNumberLevels: [EdLevel] = []
When I run this I get an exception at my AppDelegate with no information about where it failed. I get the same thing if I remove the “Count” and “Leaf” Key paths (which I understand to be optional anyway as they can be derived from “Children”]. When I remove the Children key path it all runs (with no content of course) but I get “childrenKeyPath cannot be nil. To eliminate this log message, set the childrenKeyPath attribute in Interface Builder”
I’ve searched for answers but they all just say set the childrenKeyPath with explaining what form that property should take. I’ve tried changing children to be an NSArray but that didn’t help.
Any suggestions on what the problem ?