Realm map clustering - swift

To create your own data to present in realm map you need Model which looks like from example :
public class ABFRestaurantObject: Object {
public dynamic var businessId: String?
public dynamic var name: String?
public dynamic var address: String?
public dynamic var city: String?
public dynamic var state: String?
public dynamic var postalCode: String?
public dynamic var latitude: Double = 37.7859547
public dynamic var longitude: Double = -122.4024658
public dynamic var phoneNumber: String?
public let violations = List<ABFViolationObject>()
public let inspections = List<ABFInspectionObject>()
override public static func primaryKey() -> String? {
return "businessId"
}
}
What means this primaryKey ? Then how could I load my own data into it ? Of course omitting fact that you have to create your own model with custom propertiese ?
Thanks in advance!
EDIT
I am using Realm map kit clustering
I've created model :
class Test: Object {
public dynamic var id = 0
public dynamic var latitude = 45.0889
public dynamic var longitude = 54.1565
override static func primaryKey() -> String? {
return "id"
}
}
Then in my map controller I've added following functions and declarations :
var positions = try! Realm().objects(Test.self)
var position:Test!
override func viewDidLoad() {
super.viewDidLoad()
addNewVehicle()
populateMap()
}
func addNewPosition() {
let realm = try! Realm() // 1
try! realm.write { // 2
let newPos = Test() // 3
newPos.latitude = 50.060363
newPos.longitude = 19.939983
realm.add(newPos) // 5
self.position = newPos // 6
}
}
func populateMap() {
mapView.removeAnnotations(mapView.annotations) // 1
let positions = try! Realm().objects(Test.self) // 2
// Create annotations for each one
for pos in positions { // 3
let coord = CLLocationCoordinate2D(latitude: pos.latitude, longitude: pos.longitude);
let pin = MapPin(coordinate: coord, title: "asd", subtitle: "asd")
mapView.addAnnotation(pin) // 4
}
}
And at the end simple class fro creating pins :
class MapPin : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
I would like to create few random pins and see if clustring works.
Atm i am getting an error in line :
safeObject->_coordinate = coordinate;
Thread 9: EXC_BAD_ACCESS(code=1,address=0x40)
po positions
Results<Test> (
[0] Test {
latitude = 50.060363;
longitude = 19.939983;
},
[1] Test {
latitude = 50.060363;
longitude = 19.939983;
},
[2] Test {
latitude = 50.060363;
longitude = 19.939983;
}
)
Then I've added as well to my AppDelegate.swift to the method didFinishLaunchingWithOptions :
let config = RLMRealmConfiguration.default()
config.schemaVersion = 1
config.migrationBlock = { (migration, oldSchemaVersion) in
}
RLMRealmConfiguration.setDefault(config)
Error: "Primary key property 'Test.id' has duplicate values after migration."

From the REALM documentation.
Override Object.primaryKey() to set the model’s primary key. Declaring
a primary key allows objects to be looked up and updated efficiently
and enforces uniqueness for each value. Once an object with a primary
key is added to a Realm, the primary key cannot be changed.
If you would like to create own Realm model simply create class with Object extension.
Example:
class User: Object {
dynamic var id = 0
dynamic var name = ""
dynamic var email = ""
}

Related

Realm Append managed object to unmanaged object

I want to append managed object to unmanaged object in realm.
Here is the codes
class Schedule3: Object, ObjectKeyIdentifiable {
#objc dynamic var _id: String = UUID().uuidString
#objc dynamic var _partition: String = ""
let scheduleTags = RealmSwift.List<ScheduleTag3>()
#objc dynamic var title: String = ""
override static func primaryKey() -> String? {
return "_id"
}
}
class ScheduleTag3: Object, ObjectKeyIdentifiable {
#objc dynamic var _id: String = UUID().uuidString
#objc dynamic var _partition: String = ""
#objc dynamic var name: String = ""
override static func primaryKey() -> String? {
return "_id"
}
}
When I add managed ScheduleTag3 object to unmanaged Schedule3 object and append #ObsevedResults(Schedule3.self), I get error 'Object is already managed by another Realm. Use create instead to copy it into this Realm.'
Here is the code of append,
struct SchedulePreview: View {
#ObservedResults(Schedule3.self) var schedules
#ObservedResults(ScheduleTag3.self) var tags
#EnvironmentObject var scheduleModel:ScheduleIDModel
var scheduleTitle:String
var scheduleBudget:Int
var areaTag:ScheduleTag3?
#StateRealmObject var thisSchedule = Schedule3()
var body: some View {
TabView(selection: self.$selection) {
...
.navigationBarItems (
trailing: Text("make")
.onTapGesture {
thisSchedule = scheduleModel.addSchedule(scheduleTitle: scheduleTitle, scheduleBudget: scheduleBudget, areaTag: areaTag)
let scheduleId = thisSchedule._id
let areTagId = areaTag?._id
let thisAreaTag = tags.filter("_id == %#", areTagId!).first
thisSchedule.scheduleTags.append(thisAreaTag!)
$schedules.append(thisSchedule)
}
)
}
}
class ScheduleIDModel: ObservableObject {
...
func addSchedule(scheduleTitle:String, scheduleBudget:Int, areaTag:ScheduleTag3?) -> Schedule3 {
let schedule = Schedule3()
if scheduleTitle != "" {
schedule.title = scheduleTitle
}
schedule.budget = scheduleBudget
schedule._partition = "Public"
return schedule
}
}

saving array into realm database swift

I have an application which uses realm database to persist data and everything works fine but the issue I have now is I want to save photo: Data array to the database and I tried using the standard swift array format but got an error at build time. below is my code
class TodoListModel: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var photo: Data? = nil
#objc dynamic var createdDate: Date?
override static func primaryKey() -> String? {
return "id"
}
let parentCategory = LinkingObjects(fromType: CategoryModel.self, property: "items")
}
how do I now make the photo into an array
this is the way I create my List
func createTodoList(createdDate: Date, photo: Data) -> Void {
let todoList = TodoListModel()
todoList.createdDate = createdDate
todoList.photo = photo
TodoListFunctions.instance.addData(object: todoList)
}
update model
func updateTodoList(update: TodoListModel, createdDate: Date, photo: Array<Data>) -> Void {
update.createdDate = createdDate
update.photo.append(objectsIn: photo)
}
To be able to store several objects of type Data in a single property of an Object subclass, you need to use List.
class TodoListModel: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var createdDate: Date?
let photos = List<Data>()
let parentCategory = LinkingObjects(fromType: CategoryModel.self, property: "items")
override static func primaryKey() -> String? {
return "id"
}
}
Then you can create a TodoListModel instance like this:
func createTodoList(createdDate: Date, photos: Array<Data>? = nil) -> Void {
let todoList = TodoListModel()
todoList.createdDate = createdDate
if let photos = photos {
todoList.photos.append(objectsIn: photos)
}
TodoListFunctions.instance.addData(object: todoList)
}

Swift: CoreData Conforming to MKAnnotation

I am attempting to implement my custom CoreData Carpark entity to conform to MKAnnotation like how we could make a class object conform to MKAnnotation.
I adapted my implementation from the following posts: this, this.
My implementation so far:
//At Carpark+CoreDataClass.swift
import Foundation
import CoreData
import Mapkit
#objc(Carpark)
public class Carpark: NSManagedObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: Double(self.lat), longitude: Double(self.long))
}
var title: String {
return self.carparkName
}
}
//At Carpark+CoreDataProperties.swift
import Foundation
import CoreData
import Mapkit
extension Carpark {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Carpark> {
return NSFetchRequest<Carpark>(entityName: "Carpark")
}
#NSManaged public var carparkName: String?
#NSManaged public var sundayAndPublicHolRate: String?
#NSManaged public var saturdayRate: String?
#NSManaged public var weekdayRate1: String?
#NSManaged public var weekdayRate2: String?
#NSManaged public var lat: Double
#NSManaged public var long: Double
}
//At VC where I addAnnotations
var allCarparks: [Carpark]?
var nearbyCarparks = [Carpark]()
var searchedCarpark: Carpark!
func filterCarparksWithinRadius() {
var allAnnotations = [Carpark]()
let searchRadius = UserDefaults.standard.object(forKey: Constants.UserDefaults.SearchRadius) as! Double
let searchedCarparkLocation = CLLocation(latitude: searchedCarpark.lat, longitude: searchedCarpark.long)
allCarparks?.forEach({ (carpark) in
let carparkLocation = CLLocation(latitude: carpark.lat, longitude: carpark.long)
let distance = carparkLocation.distance(from: searchedCarparkLocation)
if distance <= searchRadius && distance != 0 {
nearbyCarparks.append(carpark)
allAnnotations.append(carpark)
}
})
allAnnotations.append(searchedCarpark)
mapView.addAnnotations(allAnnotations as! [MKAnnotation]) //Problem here
let zoomCoordinate = CLLocationCoordinate2D(latitude: searchedCarpark.lat, longitude: searchedCarpark.long)
zoomToLocation(coordinate: zoomCoordinate)
}
The above code would throw the error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Carpark coordinate]:
unrecognized selector sent to instance 0x60400009e690'
Is there a way that we could make CoreData conform to MKAnnotation like how we could do it for a class object? Similar to something like this?:
class Carpark: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var carparkName: String?
var lat: Double?
var long: Double?
init(carparkName: String?, lat: Double?, long: Double?) {
self.title = carparkName
self.carparkName = carparkName
self.lat = lat
self.long = long
self.coordinate = CLLocationCoordinate2DMake(lat!, long!)
}
}
Any advice is much appreciated, thanks.
You need to make your coordinate property public to be seen outside your module (outside like in the MapView framework). Maybe this is also the reason you need to cast your array.
This is my definition
public class Location: NSManagedObject, MKAnnotation {
public var coordinate: CLLocationCoordinate2D {
return CLLocationCoordinate2DMake(latitude, longitude)
}
//....more code
}
And in my view controller I have a property
var locations = [Location]()
And I fetch into it and adds to map view like this
private func updateLocations() {
mapView.removeAnnotations(locations)
let entity = Location.entity()
let fetchRequest = NSFetchRequest<Location>()
fetchRequest.entity = entity
do {
locations = try managedObjectContext.fetch(fetchRequest)
} catch {
fatalCoreDataError(error)
}
mapView.addAnnotations(locations)
}
I managed to solve it by setting Codegen to Manual/None, and properties are then accessible.

Filter is returning all children for parent with matching child

I'm using Realm for Swift and I have a structure as follows:
class Navigation: Object {
dynamic var key = 0
dynamic var title: String?
let companies = List<Companies>()
override static func primaryKey() -> String? {
return "key"
}
}
class Companies: Object {
dynamic var key = 0
dynamic var name: String?
let locations = List<Locations>()
override static func primaryKey() -> String? {
return "key"
}
}
class Locations: Object {
dynamic var key = 0
...
dynamic var zip: String?
let contacts = List<Contacts>()
override static func primaryKey() -> String? {
return "key"
}
}
class Contacts: Object {
dynamic var key = 0
dynamic var firstName: String?
dynamic var lastName: String?
...
override static func primaryKey() -> String? {
return "key"
}
}
I'm trying to filter out locations by zip code, so that only locations that match the given zip code are displayed. I'm doing that like this
companies = realm.objects(Navigation.self).filter("key = 4").first!.companies.filter(predicate).sorted(byKeyPath: "key")
The key = 4 bit is because the filter is only supposed to search in companies under one specific category.
The problem that I'm having is that it returns all locations for a company that has a matching location. So if my zip to find is 12345, and companyA has a location that matches, all the locations under companyA are returned, even if they aren't a match.
How can I limit the results to be only locations with a match?
How can I limit the results to be only locations with a match?
Right now you're returning a Results<Companies>, but it seems like you want locations. I accomplished what I think you're looking for by adding some inverse relationship properties to the Companies and Locations models, and then queried the Realm for Locations matching zip == '12345' && ANY parentCompanies.parentNavigation.key == 4.
Here's a sample app that demonstrates this:
import UIKit
import RealmSwift
class Navigation: Object {
dynamic var key = 0
dynamic var title: String?
let companies = List<Companies>()
override static func primaryKey() -> String? {
return "key"
}
}
class Companies: Object {
dynamic var key = 0
dynamic var name: String?
let locations = List<Locations>()
let parentNavigation = LinkingObjects(fromType: Navigation.self, property: "companies")
override static func primaryKey() -> String? {
return "key"
}
}
class Locations: Object {
dynamic var key = 0
// ...
dynamic var zip: String?
let contacts = List<Contacts>()
let parentCompanies = LinkingObjects(fromType: Companies.self, property: "locations")
override static func primaryKey() -> String? {
return "key"
}
}
class Contacts: Object {
dynamic var key = 0
dynamic var firstName: String?
dynamic var lastName: String?
// ...
override static func primaryKey() -> String? {
return "key"
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
_ = try? FileManager.default.removeItem(at: Realm.Configuration.defaultConfiguration.fileURL!)
let realm = try! Realm()
// Objects Matching Query
try! realm.write {
// Locations
let locations = Locations()
locations.zip = "12345"
// Companies
let companies = Companies()
companies.name = "Companies A"
companies.locations.append(locations)
// Nav
let nav = Navigation()
nav.key = 4
nav.companies.append(companies)
// Add to Realm
realm.add(nav)
}
let locationsIn12345AndNavigationKey4 = realm.objects(Locations.self)
.filter("zip == '12345' && ANY parentCompanies.parentNavigation.key == 4")
print(locationsIn12345AndNavigationKey4)
return true
}
}
This prints:
Results<Locations> (
[0] Locations {
key = 0;
zip = 12345;
contacts = RLMArray <0x6000000f2100> (
);
}
)

How to use Realm Swift Init

I am trying to use the init for Realm in Swift. I have tried the following
override init(value: AnyObject) {
super.init()
println("this is not called")
}
required init() {
super.init()
println("this is called")
}
I want to be able to pass an object into the initializer, however, I can't get the first function to be called.
My solution in Swift 3
Custom initializer:
class Branches: Object {
dynamic var key: String = NSUUID().uuidString
dynamic var formattedAddress: String = ""
dynamic var latitude: Double = 0.0
dynamic var longitude: Double = 0.0
convenience init(formattedAddress: String, latitude: Double, longitude: Double) {
self.init()
self.formattedAddress = formattedAddress
self.latitude = latitude
self.longitude = longitude
}
override static func primaryKey() -> String? {
return "key"
}
}
Overriding Object.init() and Object.init(_:) are not yet supported in Realm Swift, but you can follow https://github.com/realm/realm-cocoa/issues/1849 for updates!
Here's my answer for iOS 13, Swift 5.
Being totally honest with you, I had no idea what I was doing, but I managed it to work by doing so:
class Account: Object {
#objc dynamic var name: String = ""
#objc dynamic var balance: Double = 0.0
#objc dynamic var savings: Double = 0.0
#objc dynamic var available: Double = 0.0
init(name: String, balance: Double){
self.name = name
self.balance = balance
self.savings = 0.0
self.available = balance
}
required init() {
//fatalError("init() has not been implemented")
}
}
Keep in mind that I'm totally new with Swift (about 3 months top), and I don't know the consequences of doing that. But my project is working just fine. Everything is being saved and retrieved perfectly.