Is it possible to use enums in RealmSwift? - ios

I want to do something like this:
enum WeekDay {
case Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
class Person: Object {
dynamic var birthday: WeekDay? = .Monday
dynamic var id: String? = nil
dynamic var birthdayRaw: String? = nil
override static func primaryKey() -> String? {
return "id"
}
}
But, I'm getting an error:
Property cannot be marked dynamic because its type cannot be
represented in Objective-C
How can I solve this ? Thanks for any help.

Realm doesn't have a direct way do it. Github issue.
But you can consider this trick
enum WeekDay: String {
case Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
class Person: Object {
private var _day: WeekDay?
var birthday: WeekDay? {
get {
if let resolTypeRaw = birthdayRaw {
_day = WeekDay(rawValue: resolTypeRaw)
return _day
}
return .Sunday
}
set {
birthdayRaw = newValue?.rawValue
_day = newValue
}
}
dynamic var id: String? = nil
dynamic var birthdayRaw: String? = nil
override static func primaryKey() -> String? {
return "id"
}
}

As of Realm 3.x you can use Int-based enums (apparently, by side-effect).
As of Realm 4.1 you can use any RawRepresentable enum (Int, Float, String) by complying with the "RealmEnum" protocol. Details in the pull request

i've create an extension, i hope it will help you
import RealmSwift
protocol RealmPersistableEnum: RawRepresentable, _OptionalPersistable { }
extension RealmPersistableEnum where RawValue: _OptionalPersistable {
static func _rlmGetProperty(_ obj: ObjectBase, _ key: PropertyKey) -> Self {
Self(rawValue: RawValue._rlmGetProperty(obj, key)) ?? Self()
}
static func _rlmGetPropertyOptional(_ obj: ObjectBase, _ key: PropertyKey) -> Self? {
guard let value = RawValue._rlmGetPropertyOptional(obj, key) else { return nil }
return Self(rawValue: value)
}
static func _rlmSetProperty(_ obj: ObjectBase, _ key: PropertyKey, _ value: Self) {
RawValue._rlmSetProperty(obj, key, value.rawValue)
}
}
Use example
enum SomeEnumInt: Int, RealmPersistableEnum {
case none = 0
case test = 1
case debug = 2
init() {
self = .none
}
}
enum SomeEnumString: String, RealmPersistableEnum {
case none
case test
case debug
init() {
self = .none
}
}
class Foo: Object {
#Persisted var v1: String
#Persisted var v2: SomeEnumInt
#Persisted var v3: SomeEnumString
}

Related

Swift - What is the difference between implementing Hashable and Equatable and NSObject hash and isEqual overrides

I'm trying to wrap my head around the new DiffableDataSource way of handling data in tableviews/collectionviews and during testing I came across a strange crash.
I would expect that the two implementations below would work exactly the same:
Implementation 1:
class DiffableSection {
var id: String
var items: [AnyDiffable]
init(id: String,
items: [AnyDiffable] = []) {
self.id = id
self.items = items
}
}
extension DiffableSection: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension DiffableSection: Equatable {
static func == (lhs: DiffableSection, rhs: DiffableSection) -> Bool {
return lhs.id == rhs.id
}
}
Implementation 2:
class DiffableSection: NSObject {
var id: String
var items: [AnyDiffable]
init(id: String,
items: [AnyDiffable] = []) {
self.id = id
self.items = items
}
// MARK: - Hashable and Equatable
public override var hash: Int {
var hasher = Hasher()
hasher.combine(id)
return hasher.finalize()
}
public override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? DiffableSection else { return false }
return id == object.id
}
}
but apparently they are not - Implementation 2 works with the code below and Implementation 1 does not.
func apply(_ sections: [DiffableSection]) {
var snapshot = self.snapshot()
for section in sections {
snapshot.appendSectionIfNeeded(section)
snapshot.appendItems(section.items, toSection: section)
}
apply(snapshot)
}
(...)
extension NSDiffableDataSourceSnapshot {
mutating func appendSectionIfNeeded(_ identifier: SectionIdentifierType) {
if sectionIdentifiers.contains(identifier) { return }
appendSections([identifier])
}
}
The crash message I get when running with Implementation 1:
'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: section != NSNotFound'
Can someone explain me what are the differences in those implementations? How could I fix Implementation 1 to work same as Implementation 2??

Сannot convert value of type "Obj" to expected argument type 'Obj'

I have an Array that comes to me from the server.
My problem is an error that appears when I want to add objects to an
array. How to solve this problem and work with an array of objects
that comes from the server ??
EXAMPLE OF RETURNED DATA
[
{
"empId": 1970083,
"empCode": "2007",
"empName": "Emp Test",
"monthClosed": 0,
"monthApproved": 0,
"approvedDate": 0,
"employerName": "Name",
"employerApproval": 1,
"employerApprovalDate": "2020-09-02 17:22:51.843"
},
]
This structure to receive this data
struct GetMonthSummaryObj: Codable {
var empId: Int?
var empCode: String?
var empName: String?
var monthClosed: Int?
var monthApproved: Int?
var approvedDate: Int?
var employerName: String?
var employerApproval: Int?
var employerApprovalDate: String?
}
This is a method in ViewModel to add data that came to an array and use it for a table. For example, to display or the number of cells in a table
func setEmployees(employees: [GetMonthSummaryObj?]) {
employeesList = []
employees.forEach {_ in
employeesList.append(CloseMonthEmpListItem(employee: employees))
//Error - Cannot convert value of type '[GetMonthSummaryObj?]' to expected argument type 'GetMonthSummaryObj'
}
}
Here I create an array object to work with it
class CloseMonthEmpListItem: Equatable, NSCopying {
var employee: GetMonthSummaryObj
init(employee: GetMonthSummaryObj) {
self.employee = employee
}
static func == (lhs: CloseMonthEmpListItem, rhs: CloseMonthEmpListItem) -> Bool {
return lhs.employee.empId == rhs.employee.empId
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = CloseMonthEmpListItem(employee: employee)
return copy
}
}
This init(employee: GetMonthSummaryObj) accepts GetMonthSummaryObj type while you pass [GetMonthSummaryObj?] which won't work
You need to replace
employees.forEach {_ in
employeesList.append(CloseMonthEmpListItem(employee: employees))
}
With
let res = employees.compactMap{ $0 }
res.forEach { item in
employeesList.append(CloseMonthEmpListItem(employee:item))
}
or
let res = employees.compactMap{ $0 }
employeesList = res.map { CloseMonthEmpListItem(employee:$0) }

How to override NumberFormatter string and number methods

NumberFormatter has a couple methods, number and string which seem to imply they are responsible for converting the value to and from each type. However, when trying to override these methods I can't get them to fire.. (the print statements are never seen).
Am I missing something? The only progress I made was with the getObjectValue(_:for:range:) method in setting the numerical value, but never the string.
import SwiftUI
class NumberProxy : NumberFormatter {
override func string(from number: NSNumber) -> String? {
print("hello from", number)
return "HELLO!"
}
override func number(from string: String) -> NSNumber? {
print("to number...", string)
return NSNumber(value: 123)
}
// override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, range rangep: UnsafeMutablePointer<NSRange>?) throws {
// try super.getObjectValue(obj, for: string, range: rangep)
// print(obj)
// obj?.pointee = NSNumber(value: 4.0) // this worked
// }
}
struct Test: View {
#State private var myNumber: Int = 0
var body : some View {
TextField("Current Balance", value: $myNumber, formatter: NumberProxy())
}
}
What you need to override is Formatter's string(for:) method:
override func string(for obj: Any?) -> String? {
class NumberProxy: NumberFormatter {
override func string(for obj: Any?) -> String? {
guard let value = (obj as? NSNumber)?.intValue else { return nil }
return .init(value)
}
}
let number = NSNumber(value: 123.456)
let string = NumberProxy().string(from: number) // "123"
edit/update:
To input Double as integers:
override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, range rangep: UnsafeMutablePointer<NSRange>?) throws {
try super.getObjectValue(obj, for: string, range: rangep)
obj?.pointee = Int(string.filter(\.isWholeNumber)) as AnyObject?
}
let integer = NumberProxy().number(from: "123.45")!.intValue // 12345

Class that subclasses PFObject and conforms to JSQMessageData

I currently have a JSQMessagesViewController, but am running to the problem of storing my JSQMessages in Parse.com, since Parse.com won't allow this type of object. I've been trying to research ways of creating a class that subclasses PFObject while conforming to JSQMessageData so that I can create messages that are of type PFObject and thus able to be stored in Parse.com.
Below is my Messages class. In my JSQMessagesViewController I would typically call
var newMessage = JSQMessage(senderId: "user", displayName: "user", text: "testing chat")
But now how would I do this with my Messages class?
import Foundation
import UIKit
import Parse
class Message: PFObject, PFSubclassing, JSQMessageData {
var senderId_ : String!
var senderDisplayName_ : String!
var date_ : NSDate
var isMediaMessage_ : Bool
var hash_ : Int = 0
var text_ : String
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String {
return "Messages"
}
convenience init(senderId: String?, text: String?) {
self.init(senderId: senderId!, senderDisplayName: senderId, isMediaMessage: false, hash: 0, text: text!)
}
init(senderId: String, senderDisplayName: String?, isMediaMessage: Bool, hash: Int, text: String) {
self.senderId_ = senderId
self.senderDisplayName_ = senderDisplayName
self.date_ = NSDate()
self.isMediaMessage_ = isMediaMessage
self.hash_ = hash
self.text_ = text
super.init()
}
func senderId() -> String? {
return senderId_;
}
func senderDisplayName() -> String? {
return senderDisplayName_;
}
func date() -> NSDate? {
return date_;
}
func isMediaMessage() -> Bool {
return isMediaMessage_;
}
func hash() -> UInt? {
return UInt(hash_);
}
func text() -> String! {
return text_;
}
func messageHash() -> UInt {
return UInt(hash_)
}
}
And my implementation in my ChatView that is a JSQMessagesViewController:
override func viewDidLoad(){
super.viewDidLoad()
var myObject = PFObject(className: "messages")
var myMessage = Message(senderId: "User", text: "Some text")
var messageArray:NSMutableArray = [myMessage]
myObject["myArray"] = messageArray
myObject.save()
}
Currently receiving error
fatal error: use of unimplemented initializer 'init()' for class Message

Swift: Sort array of objects alphabetically

I have this:
class Movies {
Name:String
Date:Int
}
and an array of [Movies]. How do I sort the array alphabetically by name? I've tried:
movieArr = movieArr.sorted{ $0 < $1 }
and
movieArr = sorted(movieArr)
but that doesn't work because I'm not accessing the name attribute of Movies.
In the closure you pass to sort, compare the properties you want to sort by. Like this:
movieArr.sorted { $0.name < $1.name }
or the following in the cases that you want to bypass cases:
movieArr.sorted { $0.name.lowercased() < $1.name.lowercased() }
Sidenote: Typically only types start with an uppercase letter; I'd recommend using name and date, not Name and Date.
Example, in a playground:
class Movie {
let name: String
var date: Int?
init(_ name: String) {
self.name = name
}
}
var movieA = Movie("A")
var movieB = Movie("B")
var movieC = Movie("C")
let movies = [movieB, movieC, movieA]
let sortedMovies = movies.sorted { $0.name < $1.name }
sortedMovies
sortedMovies will be in the order [movieA, movieB, movieC]
Swift5 Update
channelsArray = channelsArray.sorted { (channel1, channel2) -> Bool in
let channelName1 = channel1.name
let channelName2 = channel2.name
return (channelName1.localizedCaseInsensitiveCompare(channelName2) == .orderedAscending)
}
With Swift 3, you can choose one of the following ways to solve your problem.
1. Using sorted(by:​) with a Movie class that does not conform to Comparable protocol
If your Movie class does not conform to Comparable protocol, you must specify in your closure the property on which you wish to use Array's sorted(by:​) method.
Movie class declaration:
import Foundation
class Movie: CustomStringConvertible {
let name: String
var date: Date
var description: String { return name }
init(name: String, date: Date = Date()) {
self.name = name
self.date = date
}
}
Usage:
let avatarMovie = Movie(name: "Avatar")
let titanicMovie = Movie(name: "Titanic")
let piranhaMovie = Movie(name: "Piranha II: The Spawning")
let movies = [avatarMovie, titanicMovie, piranhaMovie]
let sortedMovies = movies.sorted(by: { $0.name < $1.name })
// let sortedMovies = movies.sorted { $0.name < $1.name } // also works
print(sortedMovies)
/*
prints: [Avatar, Piranha II: The Spawning, Titanic]
*/
2. Using sorted(by:​) with a Movie class that conforms to Comparable protocol
However, by making your Movie class conform to Comparable protocol, you can have a much concise code when you want to use Array's sorted(by:​) method.
Movie class declaration:
import Foundation
class Movie: CustomStringConvertible, Comparable {
let name: String
var date: Date
var description: String { return name }
init(name: String, date: Date = Date()) {
self.name = name
self.date = date
}
static func ==(lhs: Movie, rhs: Movie) -> Bool {
return lhs.name == rhs.name
}
static func <(lhs: Movie, rhs: Movie) -> Bool {
return lhs.name < rhs.name
}
}
Usage:
let avatarMovie = Movie(name: "Avatar")
let titanicMovie = Movie(name: "Titanic")
let piranhaMovie = Movie(name: "Piranha II: The Spawning")
let movies = [avatarMovie, titanicMovie, piranhaMovie]
let sortedMovies = movies.sorted(by: { $0 < $1 })
// let sortedMovies = movies.sorted { $0 < $1 } // also works
// let sortedMovies = movies.sorted(by: <) // also works
print(sortedMovies)
/*
prints: [Avatar, Piranha II: The Spawning, Titanic]
*/
3. Using sorted() with a Movie class that conforms to Comparable protocol
By making your Movie class conform to Comparable protocol, you can use Array's sorted() method as an alternative to sorted(by:​).
Movie class declaration:
import Foundation
class Movie: CustomStringConvertible, Comparable {
let name: String
var date: Date
var description: String { return name }
init(name: String, date: Date = Date()) {
self.name = name
self.date = date
}
static func ==(lhs: Movie, rhs: Movie) -> Bool {
return lhs.name == rhs.name
}
static func <(lhs: Movie, rhs: Movie) -> Bool {
return lhs.name < rhs.name
}
}
Usage:
let avatarMovie = Movie(name: "Avatar")
let titanicMovie = Movie(name: "Titanic")
let piranhaMovie = Movie(name: "Piranha II: The Spawning")
let movies = [avatarMovie, titanicMovie, piranhaMovie]
let sortedMovies = movies.sorted()
print(sortedMovies)
/*
prints: [Avatar, Piranha II: The Spawning, Titanic]
*/
let sortArray = array.sorted(by: { $0.name.lowercased() < $1.name.lowercased() })
For those using Swift 3, the equivalent method for the accepted answer is:
movieArr.sorted { $0.Name < $1.Name }
Most of these answers are wrong due to the failure to use a locale based comparison for sorting. Look at localizedStandardCompare()
Sorted array
Swift 4.2
arrayOfRaces = arrayOfItems.sorted(by: { ($0["raceName"] as! String) < ($1["raceName"] as! String) })
*import Foundation
import CoreData
extension Messages {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Messages> {
return NSFetchRequest<Messages>(entityName: "Messages")
}
#NSManaged public var text: String?
#NSManaged public var date: Date?
#NSManaged public var friends: Friends?
}
//here arrMessage is the array you can sort this array as under bellow
var arrMessages = [Messages]()
arrMessages.sort { (arrMessages1, arrMessages2) -> Bool in
arrMessages1.date! > arrMessages2.date!
}*

Resources