Realm Append managed object to unmanaged object - swift

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
}
}

Related

#ObservedObject not updating after successful network call

I've hit a brick wall in my widget extension. I'm using AlamoFire and ObjectMapper to match the networking we have in the main app. I can tell that my AlamoFire network call is getting triggered and that I'm getting results back, and in the correct, expected format. However, saving the response of that network call to a #Published var doesn't seem to be working. My view and models/structs are below:
struct WidgetEntryView: View {
var entry: ResourceCategoryEntry
#ObservedObject var viewModel = WidgetResourcesView(widgetSize: .medium)
var body: some View {
if UserDefaults.forAppGroup.object(forKey: "sessionToken") as? String == nil {
PleaseLogIn()
} else if viewModel.mediumResources.count < 1 {
ErrorScreen()
} else {
MediumResourcesView(resources: viewModel.mediumResources)
}
}
}
class WidgetResourcesView: ObservableObject {
#Published var resourceGroups: [WidgetResouceGroup] = [WidgetResouceGroup]()
var widgetSize: WidgetSize = .small
var selectedCategory: String?
init(widgetSize: WidgetSize) {
self.widgetSize = widgetSize
self.selectedCategory = UserDefaults.forAppGroup.string(forKey: ResourceCategoryEntry.userDefaultKey)
getResources()
}
func getResources() {
WidgetNetworkService.getResources(widgetSize: self.widgetSize.rawValue, selectedCategory: self.selectedCategory) { resourceGroups in
DispatchQueue.main.async {
self.resourceGroups = resourceGroups
}
} failure: { _ in
print("Error Received")
}
}
var mediumResources: [WidgetResource] {
var resources = [WidgetResource]()
if let featuredResourceGroup = resourceGroups.featuredResourceGroup {
for resource in featuredResourceGroup.resources { resources.append(resource) }
}
if let nonFeaturedResourceGroup = resourceGroups.nonFeaturedResourceGroup {
for resource in nonFeaturedResourceGroup.resources { resources.append(resource) }
}
return resources
}
}
class WidgetResouceGroup: NSObject, Mappable, Identifiable {
var id = UUID()
var widgetCategory: WidgetCategory = .featured
var resources = [WidgetResource]()
required init?(map: Map) {}
func mapping(map: Map) {
id <- map["section"]
widgetCategory <- map["section"]
resources <- map["resources"]
}
}
typealias WidgetResourceGroupCollection = [WidgetResouceGroup]
extension WidgetResourceGroupCollection {
var featuredResourceGroup: WidgetResouceGroup? {
return first(where: {$0.widgetCategory == .featured})
}
var nonFeaturedResourceGroup: WidgetResouceGroup? {
return first(where: {$0.widgetCategory != .featured})
}
}
class WidgetResource: NSObject, Mappable, Identifiable {
enum ResourceType: String {
case text = "text"
case audio = "audio"
case video = "video"
}
var id = 0
var title = ""
var imageInfo: WidgetImageInfo?
var resourceType: ResourceType = .text
required init?(map: Map) {}
func mapping(map: Map) {
id <- map["object_id"]
title <- map["title"]
imageInfo <- map["image_info"]
resourceType <- map["content_type"]
}
}
You can use the objectWillChange - Property in your observable object to specifiy when the observable object should be refreshed.
Apple Dev Doku
Example by Paul Hudson
WidgetEntryView instantiates WidgetResourcesView using the ObservedObject wrapper. This causes a new instance of WidgetResourcesView to be instantiated again on every refresh. Try switching that to StateObject, and the original object will be kept in memory between view updates. I believe this is the only change needed, but I’m away so can’t test it!

Swift MVVM Bind with Boxing

I am simply trying to create a weather application with WeatherViewController displaying the tableView with cells, and when the cell is tapped leads to WeatherDetailsViewController.
I am using the boxing way for binding and I am confused if I set Dynamic type in both the model and viewModel in the example below. You will know what I mean.
This is the Boxing Class
class Dynamic<T>: Decodable where T: Decodable {
typealias Listener = (T) -> ()
var listener: Listener?
var value: T {
didSet {
listener?(value)
}
}
func bind(listener: #escaping Listener) {
self.listener = listener
self.listener?(self.value)
}
init(_ value: T) {
self.value = value
}
private enum CodingKeys: CodingKey {
case value
}
}
This is the Weather Model Struct
struct Weather: Decodable {
let date: Dynamic<Int>
let description: Dynamic<String>
let maxTemperature: Dynamic<Double>
private enum CodingKeys: String, CodingKey {
case date = "time"
case description = "summary"
case maxTemperature = "temperatureMax"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
date = try Dynamic(container.decode(Int.self, forKey: .date))
description = try Dynamic(container.decode(String.self, forKey: .description))
maxTemperature = try Dynamic(container.decode(Double.self, forKey: .maxTemperature))
}
}
Here is my WeatherListViewModel & WeatherViewModel
Inside my WeatherViewModel I have assigned the type to be Dynamic but also in the model in order to bind in my WeatherDetailsViewController, is that right?
class WeatherListViewModel {
var weatherViewModels: [WeatherViewModel]
private var sessionProvider: URLSessionProvider
init(sessionProvider: URLSessionProvider) {
self.sessionProvider = sessionProvider
self.weatherViewModels = [WeatherViewModel]()
}
func numberOfRows(inSection section: Int) -> Int {
return weatherViewModels.count
}
func modelAt(_ index: Int) -> WeatherViewModel {
return weatherViewModels[index]
}
func didSelect(at indexPath: Int) -> WeatherViewModel {
return weatherViewModels[indexPath]
}
}
This is WeatherListViewModel Extension for network fetching where I initialize the WeatherViewModel
func fetchWeatherLocation(withLatitude latitude: CLLocationDegrees, longitude: CLLocationDegrees, completion: #escaping handler) {
sessionProvider.request(type: WeatherWrapper.self, service: WeatherService.specificLocation, latitude: latitude, longitude: longitude) { [weak self] result in
switch result {
case let .success(weatherWrapper):
let weathers = weatherWrapper.daily.weathers
self?.weatherViewModels = weathers.map {
return WeatherViewModel(weather: $0)
}
completion()
case let .failure(error):
print("Error: \(error)")
}
}
}
This is WeatherViewModel
struct WeatherViewModel {
private(set) var weather: Weather
var temperature: Dynamic<Double>
var date: Dynamic<Int>
var description: Dynamic<String>
init(weather: Weather) {
self.weather = weather
self.temperature = Dynamic(weather.maxTemperature)
self.date = Dynamic(weather.date)
self.description = Dynamic(weather.description)
}
}
Here is my WeatherDetailsViewController
Here I assign the binding to the labels respectively to get the changes
class WeatherDetailsViewController: UIViewController {
#IBOutlet private var imageView: UIImageView!
#IBOutlet private var cityLabel: UILabel!
#IBOutlet private var dateLabel: UILabel!
#IBOutlet private var descriptionLabel: UILabel!
#IBOutlet private var temperatureLabel: UILabel!
var viewModel: WeatherViewModel?
override func viewDidLoad() {
super.viewDidLoad()
setupVMBinding()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationItem.largeTitleDisplayMode = .never
}
private func setupVMBinding() {
if let viewModel = viewModel {
viewModel.date.bind {
self.dateLabel.text = $0.toString()
}
viewModel.temperature.bind {
self.temperatureLabel.text = "\($0)"
}
viewModel.description.bind {
self.descriptionLabel.text = $0.description
}
}
}
}
Question is, did I just repeat writing the type Dynamic in both model and viewModel? Is there a better way of doing this or am I on the right track. Sorry for the long code example.
I think you repeat writing Dynamic inside your Weather Model.
It does not need to be Dynamic type.
You can create a GenericDataSource
class GenericDataSource<T>: NSObject {
var data: Dynamic<T>?
}
Inside your View Model. This will Reference to your Weather Model without the need for creating dynamic type.
class WeatherViewModel {
var dataSource: GenericDataSource<Weather>?
....
}
Inside your View Controller
class WeatherDetailsViewController {
var viewModel: WeatherViewModel?
override func viewDidLoad() {
viewModel = ViewModel()
var dataSource = GenericDataSource<Weather>()
dataSource.data = Dynamic(Weather)
viewModel.dataSource = dataSource
setupVMBinding()
}
private func setupVMBinding() {
viewModel?.dataSource?.data?.bind {
self.dateLabel.text = $0.date
self.temperatureLabel.text = "\($0.maxTemperature)"
self.descriptionLabel.text = $0.description
}
}
}

Get notified when value in List is updated using realm

I am trying to write an application in OS X using a Realm database. I want to trigger notification when where there is change in value of List which is inside another object
Below is the Class
final class Profile: Object {
#objc dynamic var gradient1 = ""
#objc dynamic var gradient2 = ""
#objc dynamic var fontColor = ""
#objc dynamic var selectedFont = ""
#objc dynamic var selectedTitleFont = ""
#objc dynamic var fontFamily = ""
#objc dynamic var name = ""
#objc dynamic var shortbio = ""
#objc dynamic var avatarSource = ""
#objc dynamic var userid = ""
#objc dynamic var email = ""
var features = List<Features>()
var socialLinkButtons = List<SocialLinkButtons>()
#objc dynamic var appSelectedMetaData : AppMetaData? = nil
override static func primaryKey() -> String?{
return "userid"
}
}
final class Features: Object {
#objc dynamic var uuid = ""
#objc dynamic var id = ""
#objc dynamic var label = ""
#objc dynamic var screen = ""
#objc dynamic var active = false
override static func primaryKey() -> String?{
return "id"
}
convenience init(id: String, uuid: String, label: String, screen: String, active: Bool) {
self.init()
self.id = id
self.uuid = uuid
self.label = label
self.screen = screen
self.active = active
}
}
I want to trigger notifications whenever value inside feature is updated.
You can use Realm Collection Notifications to achieve your goals. You just need to make sure that you store the returned NotificationToken in a variable that doesn't get deallocated until you don't actually need to receive the notifications anymore and that you call .invalidate() on the token when you no longer want to receive notifications.
func observeFeatureChanges(in profile:Profile) -> NotificationToken {
let notificationToken = profile.features.observe { changes in
switch changes {
case .update(_, deletions: let deletionIndices, insertions: let insertionIndices, modifications: let modIndices):
print("Objects deleted from indices: \(deletionIndices)")
print("Objects inserted to indices: \(insertionIndices)")
print("Objects modified at indices: \(modIndices)")
case .error(let error):
print(error)
case .initial(let type):
print(type)
}
}
return notificationToken
}

Unable to create Singleton

i want to make contact book to i made a class of contact.
there is no compile error but when i run the init i do not get to the "init"pass stage.
what can be the problem ?
here is my code:
import UIKit
public class contact {
var originalNumber: String = ""
var e164Number: String = ""
var contactImage: String = ""
var name: String = ""
init (originalNumber: String, name: String) {
self.originalNumber = originalNumber
self.name = name
}
}
then in my contactDataClass i made:
import UIKit
import AddressBook // imports the framework
class contactsDataClass {
var adbk : ABAddressBook!
var localContacts = [contact]()
init() {
print("get to init")
contactsDataClass.sharedInstance.localContacts.append(contact(originalNumber: "0525222022", name: "try appent"))
print("init pass")
}
struct Static {
static var onceToken : dispatch_once_t = 0
static var instance : contactsDataClass? = nil
}
class var sharedInstance : contactsDataClass {
dispatch_once(&Static.onceToken){
Static.instance = contactsDataClass()
}
return Static.instance!
}
}
You have created infinite recursion
Infact this block
class var sharedInstance : contactsDataClass {
dispatch_once(&Static.onceToken){
Static.instance = contactsDataClass()
}
return Static.instance!
}
calls this
init() {
print("get to init")
contactsDataClass.sharedInstance.localContacts.append(contact(originalNumber: "0525222022", name: "try appent"))
print("init pass")
}
which again calls the first block...
Let's clean up your code
public class Contact {
var originalNumber: String
var e164Number = ""
var contactImage = ""
var name: String
init (originalNumber: String, name: String) {
self.originalNumber = originalNumber
self.name = name
}
}
class ContactManager {
static let sharedInstance = ContactManager()
private init() {}
var adbk : ABAddressBook! // this is dangerous...
var contacts = [Contact]()
}
As you can see defining a Singleton class in Swift is much easier than in Objective-C.
You just need to hide the default initializer and create the static constant sharedInstance as shown above.
Usage
ContactManager.sharedInstance.contacts.append(Contact(originalNumber: "0525222022", name: "Test name"))
print(ContactManager.sharedInstance.contacts[0].name) // "Test name"

How to create my game architecture in a protocol oriented way?

I haven't found any good ways to design a protocol oriented item architecture for games.
Heres the first version with Structs:
protocol Usable {
func useItem()
}
protocol Item {
var name: String { get }
var amount: Int { get }
var image: String { get }
}
struct Sword: Item, Usable {
var name = ""
var amount = 0
var image = ""
func useItem() {
}
}
struct Shield: Item, Usable {
var name = ""
var amount = 0
var image = ""
func useItem() {
}
}
The problem with this is I have to copy paste the variables which are A LOT of code across items.
Heres the second version with Classes:
protocol Usable {
func useItem()
}
class BaseItem {
var name = ""
var amount = 0
var image = ""
}
class SwordClass: BaseItem, Usable {
func useItem() {
}
}
This looks pretty good, but the problem is these are reference types and I would prefer them to be value types.
What is the right way to solve this problem?
You should create a generic struct which conforms to your protocols and which requires initialisation with default values and a 'use' closure:
protocol Usable {
func useItem()
}
protocol Item {
var name: String { get }
var amount: Int { get }
var image: String { get }
}
struct UsableItem: Item, Usable {
var name = ""
var amount = 0
var image = ""
let use: (Void -> Void)
init(name: String, image: String, use: (Void -> Void)) {
self.name = name
self.image = image
self.use = use
}
func useItem() {
self.use()
}
}
Then your JSON processing would create instances with the appropriate logic:
var sword = UsableItem(name: "sword", image: "sword") {
print("cut")
}