Getting "Must have a uuid if no _objectID" exception when inserting object into dictionary - swift

I'm writing a unit-test to a class that uses PHAsset type. I mocked it as below:
class PHAssetMock: PHAsset {
let date: Date
let uuid: UUID
init(dateStr: String) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
self.date = dateFormatter.date(from: dateStr)!
self.uuid = UUID()
}
override var creationDate: Date? {
return date
}
override var hash: Int {
let hash = Int(self.date.timeIntervalSinceNow)
return hash
}
static func ==(lhs: PHAsseMock, rhs: PHAsseMock) -> Bool {
return lhs.date.timeIntervalSinceNow == rhs.date.timeIntervalSinceNow
}
}
When a function that uses mocked objects tries to insert it in a dictionary I'm getting an exception:
func foo(assets: [PHAsset]) {
var label: [T: String]()
for asset in assets {
label[asset] = "undefined" // Exception: "NSInternalInconsistencyException", "Must have a uuid if no _objectID"
}
}
When debugging, the override hash var is being called.

I had the same issue with the PHAsset when unit testing Photos framework. Overriding isEqual function helped to get rid of the exception.
class Mock : PHAsset {
let _localIdentifier: String = UUID().uuidString
let _hash: Int = UUID().hashValue
override var localIdentifier: String {
return _localIdentifier
}
override var hash: Int {
return _hash
}
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Mock else {
return false
}
return self.localIdentifier == object.localIdentifier
}
}

Related

Realm accessed from incorrect thread occasional

I have this function
class func addCVals(_ criteres: [[AnyHashable: Any]], _ type: String) {
DispatchQueue.global(qos: .background).async {
autoreleasepool {
if criteres.count > 0 {
if let realm = DBTools.getRealm() {
do {
try realm.transaction {
let oldValues = CriteresVal.objects(in: realm, where: "type = '\(type)'")
if oldValues.count > 0 {
realm.deleteObjects(oldValues)
}
for critere in criteres {
let cval = CriteresVal(critere, type)
if let c = cval {
realm.addOrUpdate(c)
}
}
}
} catch {
DebugTools.record(error: error)
}
realm.invalidate()
}
}
}
}
}
The request that get oldValues occasionally cause an error
Realm accessed from incorrect thread
I don't understand why as I get a new Realm before with this lines:
if let realm = DBTools.getRealm()
My function getRealm:
class func getRealm() -> RLMRealm? {
if !AppPreference.lastAccount.elementsEqual("") {
let config = RLMRealmConfiguration.default()
do {
return try RLMRealm(configuration: config)
} catch {
DebugTools.record(error: error)
DispatchQueue.main.async {
Notifier.showNotification("", NSLocalizedString("UNKNOWN_ERROR_DB", comment: ""), .warning)
}
}
}
return nil
}
CriteresVal is an RLMObject that is composed of this:
#objcMembers
public class CriteresVal: RLMObject {
dynamic var cvalId: String?
dynamic var type: String?
dynamic var text: String?
dynamic var compositeKey: String?
override public class func primaryKey() -> String {
return "compositeKey"
}
private func updatePrimaryKey() {
self.compositeKey = "\(self.cvalId ?? "")/\(self.type ?? "")"
}
required init(_ cvalue: [AnyHashable: Any]?, _ type: String) {
super.init()
if let values = cvalue {
if let cvalId = values["id"] as? String {
self.cvalId = cvalId
} else if let cvalId = values["id"] as? Int {
self.cvalId = "\(cvalId)"
}
self.type = type
if let text = values["text"] as? String {
self.text = text
}
}
updatePrimaryKey()
}
func generateDico() -> [String: Any] {
var dicoSortie = [String: Any]()
if let realm = self.realm {
realm.refresh()
}
if let value = cvalId {
dicoSortie["id"] = value
}
if let value = type {
dicoSortie["type"] = value
}
if let value = text {
dicoSortie["text"] = value
}
return dicoSortie
}
}
compositeKey is the primary key which included cvalId and type
Thanks for help.

Unable to infer closure type in the current context in APIClient

I'm trying to migrate code from Swift 3.2 to Swift 4.2. When put in Xcode 10.1 I get this error.'Unable to infer closure type in the current context '. This is using YALAPIClient. Please help.
Unable to infer closure type in the current context
This I found on stack overflow. But I'm not using any try method here.Please help.
private func presentIndustrySearch() {
let dataProvider = RequestDataProvider { return IndustriesRequest() } /*error comes here*/
}
public class RequestDataProvider<Representation, Request>: DataProvider, NetworkClientInjectable
where
Request: SerializeableAPIRequest,
Request.Parser.Representation == [Representation]
{
public typealias Item = Representation
public typealias RequestConstructor = () -> Request
public private(set) var data = [Item]()
private let requestConstructor: RequestConstructor
public init(_ requestConstructor: #escaping RequestConstructor) {
self.requestConstructor = requestConstructor
}
public func loadData(before: () -> Void, after: #escaping (Error?) -> Void) {
let request: Request = self.requestConstructor()
before()
networkClient.execute(request: request, parser: request.parser, completion: { [weak self] task in
guard let `self` = self else { return }
if let data = task.value, data.count != 0 {
self.data.append(contentsOf: data)
after(nil)
} else if let error = task.error {
after(error as NSError)
} else {
let error = NSError.reachedEndOfPage()
after(error)
}
})
}
}
public protocol SerializeableAPIRequest: APIRequest {
associatedtype Parser: ResponseParser
var parser: Parser { get }
}
public struct IndustriesRequest: SerializeableAPIRequest, TokenizedAPIRequest, StubAPIRequest {
public private(set) var method = APIRequestMethod.get
public private(set) var path = "industries"
public private(set) var parser = KeyPathMappableArrayParser<[Industry]>(keyPath: "data")
public private(set) var parameters: [String: String]? = [:]
public private(set) var authenticationTokenRequired = true
public init(value: String = "") {
parameters!["term"] = value
}
}

How to overcome the error of "Generic parameter 'T' is not used in function signature"?

I'm trying to convert the following to be generic.
extension RLMOrganization: DataProvider {
func getLastSyncToken() -> String {
let lastUpdated: RLMOrganization? = self.findAll(sortedBy: "syncToken").last
if let syncToken = lastUpdated?.syncToken {
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
}
And have tried this:
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll<T: Object>(sortedBy key: String) -> [T]
}
extension DataProvider {
func findAll<T: Object>(sortedBy key: String) -> [T] {
let database = self.getDatabase()
if let allObjects = database?.objects(T.self) {
let results = allObjects.sorted(byKeyPath: key, ascending: true)
return Array(results)
}
return []
}
func getLastSyncToken<T: Object>() -> String {
let lastUpdated = self.findAll(sortedBy: "syncToken").last as? T
if let value = lastUpdated?.value(forKey: "syncToken") { // get value from object by string name
let syncToken = value as! String
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
...
But can't seem to overcome the error of:
Generic parameter 'T' is not used in function signature
I would think the compiler has everything it needs to determine type usage.
Below works for me, I don't know how findAll is defined but the problem is the reference to self as I see it so you need to define T there using associatedtype.
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll(sortedBy: String) -> T?
}

Swift Get all the properties in generic class

I'm trying to get all the members of a generic class T, I can get the properties based on a specific class.
But, how I can do it using Mirror ?
let mirrored_object = Mirror(reflecting: user)
for (index, attr) in mirrored_object.children.enumerated() {
if let propertyName = attr.label as String! {
print("Attr \(index): \(propertyName) = \(attr.value)")
}
}
I added this as extension
extension NSObject {
public func GetAsJson() -> [[String:Any?]] {
var result:[[String: Any?]] = [[String: Any?]]()
for item in self {
var dict: [String: Any?] = [:]
for property in Mirror(reflecting: self).children {
dict[property.label!] = property.value
}
result.append(dict)
}
return result
}
}

Swift and ObjectMapper: NSDate with min value

I'm using ObjectMapper to cast json into objects. My problem is that the NSDate property is not being mapped correctly. Here is the json:
{
"Id":4775,
"Cor":{
"Id":2,
"Nome":"Amarelo",
"HTMLCode":"FFFB00"
},
"Data":"2016-07-25T09:35:00",
"Texto":"test test test",
"Kilometro":547.0
}
And here is my mappable class
class RoadWarning : Mappable {
var id: Int?
var color: RoadWarningColor?
var date: NSDate?
var text: String?
var kilometer: Float?
required init?(_ map: Map){
}
func mapping(map: Map) {
id <- map["Id"]
color <- map["Cor"]
text <- map["Texto"]
kilometer <- map["Kilometro"]
date <- (map["Data"], DateTransform())
}
}
The problem is that the date property is always 1970-01-01. I can't see yet what I am missing. Can you see what is wrong in this mapping?
Thanks
ObjectMapper not convert from String to NSDate properly you have to make a workaround like this to specify the type of NSDate format it need to convert from the String :
func mapping(map: Map) {
id <- map["Id"]
color <- map["Cor"]
text <- map["Texto"]
kilometer <- map["Kilometro"]
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
if let dateString = map["Data"].currentValue as? String, let _date = dateFormatter.dateFromString(dateString) {
date = _date
}
}
I hope this help you.
You could just create a TransformType class allowing a dateFormat as parameter:
// DateFormatTransform.swift
import Foundation
import ObjectMapper
public class DateFormatTransform: TransformType {
public typealias Object = NSDate
public typealias JSON = String
var dateFormat = NSDateFormatter(dateFormat: "yyyy-MM-dd HH:mm:ss")
convenience init(dateFormat: String) {
self.init()
self.dateFormat = NSDateFormatter(dateFormat: dateFormat)
}
public func transformFromJSON(value: AnyObject?) -> Object? {
if let dateString = value as? String {
return self.dateFormat.dateFromString(dateString)
}
return nil
}
public func transformToJSON(value: NSDate?) -> JSON? {
if let date = value {
return self.dateFormat.stringFromDate(date)
}
return nil
}
}
And use it like this:
func mapping(map: Map) {
id <- map["Id"]
color <- map["Cor"]
text <- map["Texto"]
kilometer <- map["Kilometro"]
date <- (map["Data"], DateFormatTransform(dateFormat: "yyyy-MM-dd"))
}
RodolfoAntonici answer rewrited to Swift 4 with usage of SwiftDate library
import SwiftDate
import Foundation
import ObjectMapper
public class DateFormatTransform: TransformType {
public typealias Object = Date
public typealias JSON = String
public func transformFromJSON(_ value: Any?) -> Object? {
if let dateString = value as? String {
return dateString.toDate()?.date
}
return nil
}
public func transformToJSON(_ value: Date?) -> JSON? {
if let date = value {
return date.toString()
}
return nil
}
}
import Foundation
import ObjectMapper
public class DateFormatTransform: TransformType {
public typealias Object = Date
public typealias JSON = Double
var dateFormat = DateFormatter()
convenience init(_ format: String) {
self.init()
self.dateFormat.dateFormat = format
}
open func transformFromJSON(_ value: Any?) -> Date? {
if let timeInt = value as? Double {
return Date(timeIntervalSince1970: TimeInterval(timeInt))
}
if let timeStr = value as? String {
return self.dateFormat.date(from: timeStr)
}
return nil
}
open func transformToJSON(_ value: Date?) -> Double? {
if let date = value {
return Double(date.timeIntervalSince1970)
}
return nil
}
}