Realm/Swift: compilation error: Missing argument for parameter 'forPrimaryKey' in call - swift

I'm trying to get my head around swift & realm, so I've created some kind of a test pad programme.
My model is defined like so
class RealmRecord: Object {
// properties
dynamic var id: Int = 0;
dynamic var text: String = ""
dynamic var var1: Double = 0.0
dynamic var var2: Int = 0
dynamic var var3: Double = 0.0
dynamic var var4: Int = 0
dynamic var cdate: Date = Date()
dynamic var cusr: String = ""
dynamic var mdate: Date = Date.distantPast
dynamic var musr: String = ""
dynamic var mcnt: Int = 0
// methods
convenience init(id: Int? = 0, text: String? = "", var1: Double? = 0.0,
var2: Int? = 0, var3: Double? = 0.0, var4: Int? = 0,
cusr: String? = "") {
self.init()
self.id = id!
self.text = text!
self.var1 = var1!
self.var2 = var2!
self.var3 = var3!
self.var4 = var4!
self.cdate = Date()
self.cusr = cusr!
self.mdate = Date.distantPast
self.musr = ""
self.mcnt = 0
} // init
override static func primaryKey() -> String? {
return "id"
} // primaryKey
} // RealmRecord
Persisting the data is accomplished by
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
But when adding the data retrieval via
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
I'm receiving an error message while compiling the code, reading
DataRealmRecord.swift:84:39: Missing argument for parameter 'forPrimaryKey' in call
Looking at the Realm documentation reveals only retrieving all persisted data--apparently without having a primary key defined--, or, alternatively, a single object, specified by the primary key.
Sifting through The Net brings up pretty much the same.
Given the model above, how can I retrieve all persisted data?
-- Sil68
EDIT
I've also defined a class facilitating this Realm model of mine, which basically carries out the following steps:
generate some random data;
persist data via the Realm model;
read data in again;
compare generated with read data.
The code
import Foundation
import RealmSwift
class DataRealmRecord {
// properties
private(set) var recDBPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
private(set) var recDBSubPath = "Persistency"
private(set) var recDBFile = "data.realm"
private(set) var recRealm: Realm?
private(set) var recRealmCfg: Realm.Configuration?
private(set) var rec = List<RealmRecord>()
private(set) var startTime = 0.0
private(set) var stopTime = 0.0
private(set) var runTime = 0.0
private(set) var outLog = ""
private(set) var realmOk = false
// methods
init() {
// assemble destination folder/database name
do {
try FileManager.default.createDirectory(atPath: recDBPath + "/" +
recDBSubPath,
withIntermediateDirectories: true,
attributes: nil)
recDBFile = recDBPath + "/" + recDBSubPath + "/" + recDBFile
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
// configure realm database
if (realmOk) {
self.recRealmCfg = Realm.Configuration(fileURL: Foundation.URL(string: self.recDBFile))
do {
self.recRealm = try Realm(configuration: self.recRealmCfg!)
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
} // if
} // init
// generate test data
func generateData(noRecs: Int? = 1000, simDat: SimulateData?) {
for i in 1...noRecs! {
let realmRec = RealmRecord(id: i,
text: String(format: "Record #%04d", i),
var1: simDat?.datnorm[i - 1] ?? 1.1,
var2: simDat?.datpois[i - 1] ?? 2,
var3: simDat?.datunif[i - 1] ?? 3.3,
var4: simDat?.datbern[i - 1] ?? 4,
cusr: "me")
self.rec.append(realmRec)
} // for
} // generateData
// retrieve test data from persistent storage
func loadData() -> List<RealmRecord> {
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
} // loadData
// save test data to persistent storage
func saveData() {
do {
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
} catch let error as NSError {
outLog += error.localizedDescription
} // do/try/catch
} // saveData
// compare two data sets
func compareData(rec1: List<RealmRecord>, rec2: List<RealmRecord>) -> Bool {
var rc = false
if rec1.count == rec2.count {
rc = true
for i in 0..<rec1.count {
rc = rc && (rec1[i] == rec2[i])
} // for
} // if
return rc
} // compareData
// run a full evaluation cycle
// (1) generate test data;
// (2) save test data to persistant storage;
// (3) retrieve test data from persistant storage;
// (4) compare generated data with retrieved data.
func fullCycle(noRecs: Int? = 1000, simDat: SimulateData?, prnData: Bool? = false) {
// start execution time measurement
self.startTime = Double(CFAbsoluteTimeGetCurrent())
// execute the full cycle
self.generateData(noRecs: noRecs, simDat: simDat) // (1)
self.saveData() // (2)
let rec2 = self.loadData() // (3)
let cmpRec = compareData(rec1: self.rec, rec2: rec2) // (4)
// stop execution time measurement & calculate elapsed time
self.stopTime = Double(CFAbsoluteTimeGetCurrent())
self.runTime = self.stopTime - self.startTime
} // fullCycle
} // DataRealmRecord
Issue at hand is, this code fails to compile due to the error message mentioned above (in method loadData()).

The Swift compiler is telling you that it thinks you're trying to call Realm.object(ofType:forPrimaryKey:), which retrieves a single object based on the value of its primary key. It sounds like you really want to call Realm.objects(_:) to retrieve all objects of a given type. Note that this returns a Results<T>, not a List<T>.

Related

firebase firestore not accepting my write function from swift encoded json

this is my struct
struct CustomerDetail: Codable {
var customerID: String = ""
var firstname: String = ""
var lastname: String = ""
var age: Int = 0
var birthday: Int = 0
var country: String = ""
var pound: Bool = true
var feetboolean: Bool = true
var currentweight: Int = 0
var desiredweight: Int = 0
var sex: Bool = false
var feet: Int = 0
var inches: Int = 0
var cm: Float = 0.0
}
this is my write function
struct writetofirebase {
var customerdetails: CustomerDetail = CustomerDetail()
let db = Firestore.firestore()
var delegate: writefirebase?
func write(){
print(customerdetails)
do {
if let data = try? JSONEncoder().encode(customerdetails){
try db.collection("collection name").document("document name").setData(from: data)
delegate?.didSuccessfulWrite(true)
}else{
print("failed to encode")
}
} catch let error {
delegate?.didFailWithError(error)
}
}
}
this is my error
https://i.stack.imgur.com/cOpQK.png
this is my firebase structured data
https://i.stack.imgur.com/cOpQK.png
The error is saying that the data it is receiving inside the setData method is not compatible and therefore malformed for the request. You should be updating or setting the data appropriately by defining a custom dictionary or passing the object directly.
db.collection("collection name").document("document name").setData(data, merge: true)
Source: https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document

deinitialize() was obsoleted in swift 5.0

I was using a pod for ftp picture upload issue. But it is giving an error after I build the app with Swift 5.0.
Here is the error:
deinitialize()' is unavailable: the default argument to deinitialize(count:) has been removed, please specify the count explicitly.
Here is the Swift file belongs to pod:
import Foundation
/* Resource type, values defined in `sys/dirent.h`. */
public enum ResourceType: String {
case Unknown = "Unknown" // DT_UNKNOWN
case Directory = "Directory" // DT_DIR
case RegularFile = "RegularFile" // DT_REG
case SymbolicLink = "SymbolicLink" // DT_LNK
case NamedPipe = "NamedPipe" // DT_FIFO
case CharacterDevice = "CharacterDevice" // DT_CHR
case BlockDevice = "BlockDevice" // DT_BLK
case LocalDomainSocket = "LocalDomainSocket" // DT_SOCK
case Whiteout = "Whiteout" // DT_WHT
}
open class ResourceItem: CustomStringConvertible {
open var type: ResourceType = .Unknown
open var name: String = ""
open var link: String = ""
open var date: Date = Date()
open var size: Int = 0
open var mode: Int = 0
open var owner: String = ""
open var group: String = ""
open var path: String = "/"
open var description: String {
get {
return "\nResourceItem: \(name), \(type.rawValue)"
}
}
}
private let _resourceTypeMap: [Int:ResourceType] = [
Int(DT_UNKNOWN): ResourceType.Unknown,
Int(DT_FIFO): ResourceType.NamedPipe,
Int(DT_SOCK): ResourceType.LocalDomainSocket,
Int(DT_CHR): ResourceType.CharacterDevice,
Int(DT_DIR): ResourceType.Directory,
Int(DT_BLK): ResourceType.BlockDevice,
Int(DT_REG): ResourceType.RegularFile,
Int(DT_LNK): ResourceType.SymbolicLink,
Int(DT_WHT): ResourceType.Whiteout
]
/** Operation for resource listing. */
internal class ResourceListOperation: ReadStreamOperation {
fileprivate var inputData: NSMutableData?
var resources: [ResourceItem]?
override func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) {
var offset = 0
let bytes = self.inputData!.bytes.bindMemory(to: UInt8.self, capacity: (self.inputData?.length)!)
let totalBytes = CFIndex(self.inputData!.length)
var parsedBytes = CFIndex(0)
let entity = UnsafeMutablePointer<Unmanaged<CFDictionary>?>.allocate(capacity: 1)
var resources = [ResourceItem]()
repeat {
parsedBytes = CFFTPCreateParsedResourceListing(nil, bytes.advanced(by: offset), totalBytes - offset, entity)
if parsedBytes > 0 {
let value = entity.pointee?.takeUnretainedValue()
if let fptResource = value {
resources.append(self.mapFTPResources(fptResource))
}
offset += parsedBytes
}
} while parsedBytes > 0
self.resources = resources
entity.deinitialize()
return (true, nil)
}
fileprivate func mapFTPResources(_ ftpResources: NSDictionary) -> ResourceItem {
let item = ResourceItem()
if let mode = ftpResources[kCFFTPResourceMode as String] as? Int {
item.mode = mode
}
if let name = ftpResources[kCFFTPResourceName as String] as? String {
// CFFTPCreateParsedResourceListing assumes that teh names are in MacRoman.
// To fix it we create data from string and read it with correct encoding.
// https://devforums.apple.com/message/155626#155626
if configuration.encoding == String.Encoding.macOSRoman {
item.name = name
} else if let nameData = name.data(using: String.Encoding.macOSRoman) {
if let encodedName = NSString(data: nameData, encoding: self.configuration.encoding.rawValue) {
item.name = encodedName as String
}
}
item.path = self.path! + item.name
}
if let owner = ftpResources[kCFFTPResourceOwner as String] as? String {
item.owner = owner
}
if let group = ftpResources[kCFFTPResourceGroup as String] as? String {
item.group = group
}
if let link = ftpResources[kCFFTPResourceLink as String] as? String {
item.link = link
}
if let size = ftpResources[kCFFTPResourceSize as String] as? Int {
item.size = size
}
if let type = ftpResources[kCFFTPResourceType as String] as? Int {
if let resourceType = _resourceTypeMap[type] {
item.type = resourceType
}
}
if let date = ftpResources[kCFFTPResourceModDate as String] as? Date {
item.date = date
}
return item
}
override func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) {
if let inputStream = aStream as? InputStream {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1024)
let result = inputStream.read(buffer, maxLength: 1024)
if result > 0 {
if self.inputData == nil {
self.inputData = NSMutableData(bytes: buffer, length: result)
} else {
self.inputData!.append(buffer, length: result)
}
}
buffer.deinitialize()
}
return (true, nil)
}
}
Can you help me how can I fix these 2 below lines:
buffer.deinitialize()
entity.deinitialize()
And is it okay if we fix these two lines? I mean does the pod work after we fix these two lines?
deinitialize now requires a count parameter indicating how many values you want to deinitialise.
From the context, the code is probably trying to deinitialise everything the pointer references, so the number of values we deinitialise will be equal to the number of values we allocate. This will be 1024 for buffer and 1 for entity.
You should replace those lines with:
buffer.deinitialize(count: 1024)
// and
entity.deinitialize(count: 1)
respectively
However, since this is code from a pod that you are modifying, make sure to check the terms in the licence of the pod to make sure you are not violating anything.
You should also inform the author of the pod that the pod needs updating. This API change is made in Swift 4.1, I think, so it's quite old.

Filter Realm Results array returns elements that should be filtered

Any ideas why this filter is not working correctly ?
for item in activeItems {
print("item.product: \(item.product), \(item.spaceRequired)")
}
returns
item.product: nil, 40.0
Filtering where product is nil
let f1 = activeItems.filter{$0.product != nil}
print("f1: \(f1)")
print("f1.count: \(f1.count)")
returns a count of ZERO but the array still appears to contain an item
f1: LazyFilterSequence<Results<AssortmentItem>>(_base: Results<AssortmentItem> <0x109ce2c90> (
[0] AssortmentItem {
...
f1.count: 0
And then filtering and mapping just spaceRequired
let f11 = f1.filter{$0.product!.isProduct == true}.map({$0.spaceRequired})
print("f11: \(f11)")
returns the same array with a single item
f11: LazyMapSequence<LazyFilterSequence<Results<AssortmentItem>>, Double>(_base: Swift.LazyFilterSequence<RealmSwift.Results<Merchandise_Manager.AssortmentItem>>(_base: Results<AssortmentItem> <0x109ce2c90> (
[0] AssortmentItem {
And then trying to reduce crashes
let w = f11.reduce(0,+)
This seems to fix the problem
let width = Array(activeItems.filter{$0.product != nil}).filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
Is this a bug in Swift 5 or in Realm ?
EDIT: It looks like this is a bug in Realm's handling of things.
To be a bit cleared below is a more complete set of the Realm objects.
import Foundation
import RealmSwift
let activeDate: NSDate = Date() as NSDate
let defaultWidth: Double = 40.0
class MyObject: Object {
#objc dynamic var number: Int = 0
#objc dynamic var name: String?
let items = List<ChildObject>()
}
extension MyObject {
var activeItems: Results<ChildObject> {
let activeDate = activeDate // Some globally defined value
let active = items.filter("startDate <= %# && (endDate >= %# || endDate == nil)", activeDate, activeDate).sorted(byKeyPath: "number")
return active
}
/// Works Correctly
var totalWidth: Double {
let width = Array(activeItems.filter{$0.product != nil}).filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
let width2 = Array(activeItems.filter{$0.product == nil}.map({$0.spaceRequired})).reduce(0,+)
return width+width2
}
/// Crashes
var totalWidth: Double {
let width = activeItems.filter{$0.product != nil}.filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
let width2 = activeItems.filter{$0.product == nil}.map({$0.spaceRequired}).reduce(0,+)
return width+width2
}
}
class ChildObject: Object {
#objc dynamic var parent: MyObject?
#objc dynamic var number: Int = 0
#objc dynamic var product: Product?
#objc dynamic var name: String?
#objc dynamic var spaceRequired: Double = 40.0
#objc dynamic var startDate: NSDate?
#objc dynamic var endDate: NSDate?
}
extension ChildObject {
var spaceRequired: Double {
if let p = product {
return p.width
} else {
return defaultWidth
}
}
}
class Product: Object {
#objc dynamic var isProduct: Bool = false
#objc dynamic var width: Double = 30.0
}
There's a couple of issues at work here but the main problem is that Realm Results are live updating; while you can filter data using the Swifty
let f1 = activeItems.filter{$0.product != nil}
it's going to give intermittent results since Realm doesn't know which items are filtered or not as .filter { is not a Realm function and Realm won't know what to update within the results.
You should generally use the built in Realm filtering mechanism
let results = realm.objects(ItemClass.self).filter("product != nil")
Those results will be live updating - if an object leaves the filter parameter, the results follow that. If an object matches the filter the results are updated as well.
I believe this Github issue #2138 provides some more light on the issue.
If you absolutely need static data, then I would suggest extending the Results class to return an array; like this
extension Results {
func toArray() -> [Element] {
return compactMap { $0 }
}
}
Keeping in mind this will use more memory as Realm objects are lazy loaded and and array isn't.
EDIT:
There's some additonal information in the question so I crafted up a simple example trying to replicate the issue. There's a HouseClass object which contains a List of RoomClass objects and then the HouseClass is extended to return the total width of all of the rooms in its list.
class RoomClass: Object {
#objc dynamic var room_name = ""
#objc dynamic var width = 0
#objc dynamic var length = 0
#objc dynamic var belongs_to_house: HouseClass!
}
class HouseClass: Object {
#objc dynamic var house_id = NSUUID().uuidString
#objc dynamic var house_name = ""
let rooms = List<RoomClass>()
override static func primaryKey() -> String? {
return "house_id"
}
}
extension HouseClass {
var totalWidth: Int {
let width = Array(rooms).map {$0.width}.reduce(0,+)
return width
}
var anotherTotalWidth: Int {
let width = rooms.map {$0.width}.reduce(0,+)
return width
}
}
and then the code to get all of the houses and output their room widths based on two different functions (see the HouseClass extension)
let houseResults = realm.objects(HouseClass.self)
for house in houseResults {
let w0 = house.totalWidth
print(w0)
let w1 = house.anotherTotalWidth
print(w1)
}
I added 100 houses each with three rooms and ran the above code several times without crash.
Count of f1 is 0 so map is not worked.
You can optimize your width calculation as following
let width = activeItems
.filter { $0.product?.isProduct ?? false }
.map { $0.spaceRequired }
.reduce(0,+)

Problems linking objects in realm swift

I am having problems linking objects in realm. I read the question and answer titled "Nested objects in realm" and tried the answer suggested but it did not work for me.
Here are my object classes:
import Foundation
import RealmSwift
class Job: Object {
dynamic var id = NSUUID().UUIDString
dynamic var name = ""
dynamic var address = ""
dynamic var phone = ""
dynamic var email = ""
dynamic var notes = ""
dynamic var material = 0.0
dynamic var edge = 0.0
dynamic var splash = 0.0
dynamic var discount = 0.0
dynamic var trip = 0.0
let rooms = List<Room>()
override static func primaryKey() -> String {
return "id"
}
}
import Foundation
import RealmSwift
class Room: Object {
dynamic var id = NSUUID().UUIDString
dynamic var name = ""
dynamic var material = 0.0
dynamic var edge = 0.0
dynamic var splash = 0.0
dynamic var sinkType = ""
dynamic var sinkModel = ""
dynamic var numberOfSinks = 0
dynamic var faucet = ""
dynamic var rangeType = ""
dynamic var notes = ""
dynamic var jobs: Job?
let countertops = List<Countertop>()
//var linksToJob: [Job] {
// return linkingObjects(Job.self, forProperty: "rooms")
//}
override static func primaryKey() -> String {
return "id"
}
}
import Foundation
import RealmSwift
class Countertop: Object {
dynamic var id = NSUUID().UUIDString
dynamic var depth = 0.0
dynamic var width = 0.0
dynamic var cabDescription = ""
dynamic var sqFt = 0.0
dynamic var room: Room?
//var linkToRoom: [Room] {
// return linkingObjects(Room.self, forProperty: "countertops")
//}
override static func primaryKey() -> String {
return "id"
}
}
Here are the functions I use to save the data:
#IBAction func saveButton() {
jobs.name = nameTF.text!
jobs.address = addressTF.text!
jobs.phone = phoneTF.text!
jobs.email = emailTF.text!
jobs.notes = notesTV.text!
jobs.discount = Double(discountTF.text!)!
jobs.material = Double(materialTF.text!)!
jobs.edge = Double(edgeTF.text!)!
jobs.splash = Double(splashTF.text!)!
jobs.trip = Double(tripTF.text!)!
do {
try! realm.write {
realm.add(jobs)
}
}
print(jobs)
// print(Realm.Configuration.defaultConfiguration.path!)
}
func saveData(){
rooms.name = nameTF.text!
rooms.material = Double(materialTF.text!)!
rooms.edge = Double(edgeTF.text!)!
rooms.splash = Double(splashTF.text!)!
rooms.notes = notesTV.text
rooms.sinkType = sinkTypeTF.text!
rooms.sinkModel = sinkModelTF.text!
rooms.numberOfSinks = Int(numberSinksTF.text!)!
rooms.faucet = faucetTF.text!
rooms.rangeType = rangeTF.text!
rooms.jobs?.id = keyValueLabel.text!
//rooms.linksToJob
do {
try! realm.write {
realm.add(rooms)
}
}
print(rooms)
}
and:
#IBAction func addNextButton(sender: AnyObject) {
// self.realm.beginWrite()
let realm = try! Realm()
if widthTF.text != "" {
Calculations.depth = Double(depthTF.text!)!
Calculations.width = Double(widthTF.text!)!
let depthAdded = depthTF.text
cabinetDepth.append(Double(depthAdded!)!)
let widthAdded = widthTF.text
cabinetWidth.append(Double(widthAdded!)!)
let descriptionAdded = pickerLabel.text
cabinetDescription.append(descriptionAdded!)
let runningTotal = Calculations.squareFeet()
squareFeetToBeAdded.append(runningTotal)
let sum = squareFeetToBeAdded.reduce(0,combine: {$0 + $1})
let roundedSqFt = Double(round(sum * 10) / 10)
sqFtLabel.text = "\(roundedSqFt)"
countertops.depth = Double(depthTF.text!)!
countertops.width = Double(widthTF.text!)!
countertops.cabDescription = pickerLabel.text!
countertops.sqFt = Double(sqFtLabel.text!)!
//countertops.linkToRoom
do {
try realm.write {
realm.add(countertops)
}
print(countertops)
} catch {
print("an error occurred")
}
widthTF.text = ""
numberTable.reloadData()
widthTF.becomeFirstResponder()
}
}
When I save the data and print the results here is what I get:
Job {
id = 00F95F55-54D8-426E-B483-C990A4171002;
name = Ken;
address = Address;
phone = phone;
email = email;
notes = Notes :;
material = 8;
edge = 0;
splash = 4;
discount = 1;
trip = 0;
rooms = RLMArray <0x7ffef9df29c0> (
);
}
Room {
id = 7D3F86B9-FCD7-4CB7-AD6E-9B8141A7390C;
name = Kitchen;
material = 9;
edge = 0;
splash = 4;
sinkType = Undermount Kitchen;
sinkModel = 50/50 Stainless Steel;
numberOfSinks = 1;
faucet = Single Hole;
rangeType = Free Standing Range;
notes = Notes:;
jobs = (null);
countertops = RLMArray <0x7ffef9df3720> (
);
}
Countertop {
id = 992B8BAE-392F-4513-85DC-CBA191D2AE08;
depth = 25.5;
width = 65;
cabDescription = Cabinet;
sqFt = 11.5;
room = (null);
}
As you can see the links return null.
Please tell me what I am doing wrong here.
Note: I comment out the linkingObjects in the models because it caused a crash. I'm not sure why.
You should use references to existing realm objects when adding them. So rather than this:
func saveData(){
rooms.name = nameTF.text!
rooms.material = Double(materialTF.text!)!
rooms.edge = Double(edgeTF.text!)!
rooms.splash = Double(splashTF.text!)!
rooms.notes = notesTV.text
rooms.sinkType = sinkTypeTF.text!
rooms.sinkModel = sinkModelTF.text!
rooms.numberOfSinks = Int(numberSinksTF.text!)!
rooms.faucet = faucetTF.text!
rooms.rangeType = rangeTF.text!
rooms.jobs?.id = keyValueLabel.text!
//rooms.linksToJob
do {
try! realm.write {
realm.add(rooms)
}
}
print(rooms)
}
You would use something like this:
let realm = try! Realm()
let room = realm.create(Room.self)
room.name = nameTF.text!
room.material = Double(materialTF.text!)!
...
let job = realm.create(Job.self)
job.name = "My job name"
...
room.job = job
Since it looks like you're entering these on separate screens, you will add a room, then you want to add a job:
if let existingJob = realm.objects(Job).filter("name == '\(jobNameToLookup)'").first {
room.job = existingJob
}
For a list, just do room.countertops.append(newCountetop), where newCountertop is the result of realm.create
Because you add each objects into the Realm individually without linking. If you'd like to link the objects, you should add the objects to the List properties, such like rooms and countertops. Also for to-one relationships, you should assign the object to the property.
So you need write the code something like the following:
...
rooms.faucet = faucetTF.text!
rooms.rangeType = rangeTF.text!
...
rooms.countertops.append(countertops)
...
jobs.rooms.append(rooms)
And,
...
countertops.rooms = rooms
...
rooms.jobs = jobs
For more details, see also the documents:
Thanks to Austin who got me on the right track. Here is the code that finally did it.
#IBAction func roomSaveNextBtnPushed(_ sender: UIButton) {
let room = Room()
room.roomName = roomTF.text!
room.material = materialTF.text!
room.edge = edgeTF.text!
room.job = realm.objects(Job.self).last
try! realm.write {
realm.add(room)
print(room)
}
}
The link is made in the ' realm.object(Job.self).last ' line of code. This adds a room to a job as expected.
The suggestion in Austin's example ' let room = realm.create(Room.self) ' caused an error, and the project would not compile.
I am using Xcode 8 with Swift 3 and Realm-swift 1.1.0
Thanks again to those who offered help.

'self' used before super.init call

I'm new to swift and I don't understand how to initialize a class.
Succeeded is initialized in the class definition as false
if (succeeded && (time>1000)){
errormessage += ";connection slow"
}
Time is initialized as
time = data[3].toInt()
Where data is
var data = split(raw_data) {$0 == ","}
And raw_data is a string.
Class Definition:
class geocodeObject: NSObject {
init definition:
init(lat: String,long:String,userstate:String) {
(no super init of any kind)
Full code with things cut way:
class geocodeObject: NSObject {
//A type to store the data from the Reverse Geocoding API
//Not a retriever
//Options
let API_KEY_TEXAS = "9e4797c018164fdcb9a95edf3b10ccfc"
let DEV_MODE = true
//Loading status
var succeeded = false
var errormessage = "Not Initalized" //Not nesscarilly a failure, could be slow connection
var loadstate: String?
//Most important info
var street: String?; var housenumber: String?; var city: String?; var zip: String?
//Metadata
var time: IntegerLiteralType?; var statuscode: String?; var queryid: String?; var zip4: String?
//Other geographical data
var entirestreet: String?; var state: String?
init(lat: String,long:String,userstate:String) {
//userstate: State provided by user
//state: State provided by Reverse Geocoder
var url: String?
var extra: String?
if DEV_MODE{
extra = "&notStore=true"
}
else{
extra = ""
}
url = "http://geoservices.tamu.edu/Services/ReverseGeocoding/WebService/v04_01/HTTP/default.aspx?lat="+lat+"&lon="+long+"&apikey="+API_KEY_TEXAS+"&version=4.01"
if (userstate == "nil"){
url = url! + extra!
println("if")
}
else{
url = url! + "&state="+state!+extra!
println("else")
}
let raw_data = retrieveurl(url!)
var data = split(raw_data) {$0 == ","}
//data[1] is API version used.
statuscode = data[0]; queryid = data[2]; time = data[3].toInt(); entirestreet = data[4]; city = data[5]
state = data[6]; zip = data[7]; zip4 = data[8]
//Do street, housenumber, errormessage, succeeded
if (state != userstate){
println("user state not equal to state")
}
var splittedstreet = split(entirestreet!){$0 == " "}
housenumber = splittedstreet[0]
street = splittedstreet[1]
println(statuscode)
//Error message handling
switch String(statuscode!){
case "200":
errormessage = "Success"
case "400":
errormessage = "Unknown API key error"
case "401":
...
//Time handling
if (succeeded && (time>1000)){
errormessage += ";connection slow"
}
}
println("/GeocodingAPIWrapper.swift/.geocodeObject.init: Not Implemented")
}
}
It had been a while but the answer I found is that you should add super.init() as the first line inside your init block
init(lat: String,long:String,userstate:String) {
super.init()
...
This way I got rid of it and fulfills what the error is asking for.
As I understand this is that your variables are initialized during NSObject.init() so you can use assigned values inside your custom init(_) block
Swift 2.2 (still beta as of writing) currently displays this error if you accidentally forget to return nil from a guard's else:
required init?(dictionary: [String: AnyObject]) {
guard let someValue = dictionary["someValue"] as? Bool else { return /*nil*/ } //Nil should not be commented here
self.someValue = someValue
super.init(dictionary: dictionary) //`self` used before super.init call
}
Hopefully this helps someone