how to filter data from object mapper class - swift

i want to implement search functionality in my app but i get data from services. i have an array like this in object mapper
class Country : Mappable {
var countryName:String = ""
var countryID:Int = 0
var countryImage:String = ""
var countryColor:String = ""
required init?(_ map: Map) {
}
func mapping(map: Map) {
countryID <- map["id"]
countryName <- map["name"]
countryColor <- map["color"]
countryImage <- map["image"]
}
}
from here i want to filter my data for search functionality how to do this.
here i am filtering only country names but i want to filter whole array how i can do that
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredData = self.countryNames.filter { (country:String) -> Bool in
if country.lowercaseString.containsString(self.searchController.searchBar.text!.lowercaseString) {
return true
} else {
return false
}
}
print(filteredData)
// update results table view
self.resultController.tableView.reloadData()
}

You can filter your array like this way.
let filter = countries.filter { $0.countryName.lowercaseString.containsString(self.searchCon‌​troller.searchBar.te‌​xt!.lowercaseString) }
self.resultController.tableView.reloadData()

Related

Realm object predicate filter is invalid

I use predicate to filter the converstions of models. I got the wrong result with Realm filter (0 models though there should be one). After that, I did another check and she showed me that there is one model with these criteria. Please tell me what may be wrong here.
let checkConversations = Array(realm.objects(ChatConversationModel.self)).filter({ $0.lastMessage != nil && $0.toDelete == false })
debugPrint("checkConversations", checkConversations.count) received one model (this is the right result).
var conversations = Array(realm.objects(ChatConversationModel.self).filter("lastMessage != nil && toDelete == false"))
debugPrint("conversations", conversations.count) I did not receive any models at all
Models:
class ChatConversationModel: Object, Mappable {
/// oneToOne = friend
enum ChatType {
case oneToOne, group, nonFriend
var index: Int {
switch self {
case .oneToOne:
return 0
case .group:
return 1
case .nonFriend:
return 2
}
}
}
#objc dynamic var id = ""
#objc dynamic var typeIndex = ChatType.oneToOne.index
#objc dynamic var lastMessage: ChatMessageRealmModel?
#objc dynamic var lastActivityTimeStamp = 0.0
#objc dynamic var modelVersion = AppStaticSettings.versionNumber
let createTimestamp = RealmOptional<Double>()
// for group chat
#objc dynamic var groupConversationOwnerID: String?
/// for group chat equal card photos
#objc dynamic var cardInfo: ChatConversationCardInfoModel?
// Local
#objc dynamic var toDelete = false
override class func primaryKey() -> String? {
return "id"
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
if map.mappingType == .fromJSON {
id <- map["id"]
typeIndex <- map["typeIndex"]
lastMessage <- map["lastMessage"]
lastActivityTimeStamp <- map["lastActivityTimeStamp"]
createTimestamp.value <- map["createTimestamp"]
modelVersion <- map["modelVersion"]
// for group chat
cardInfo <- map["cardInfo"]
groupConversationOwnerID <- map["groupConversationOwnerID"]
} else {
id >>> map["id"]
typeIndex >>> map["typeIndex"]
lastMessage >>> map["lastMessage"]
lastActivityTimeStamp >>> map["lastActivityTimeStamp"]
createTimestamp.value >>> map["createTimestamp"]
modelVersion >>> map["modelVersion"]
// for group chat
cardInfo >>> map["cardInfo"]
groupConversationOwnerID >>> map["groupConversationOwnerID"]
}
}
}
Update: When I receive actual conversations, I start comparing which already exists in the application. And looking for ids which are not in the result. Next, I find these irrelevant conversations and put toDelete = false, in order to "safely" delete inactive conversations. Since I listened to a podcast with one of Realm's developers, he advised not to delete an object that can be used. And since when receiving results with backend, any inactive conversation can be active again, so I chose this method. You can view the code for these functions.
private func syncNewConversations(_ conversations: [ChatConversationModel], userConversations: [ChatUserPersonalConversationModel], completion: ((_ error: Error?) -> Void)?) {
DispatchQueue.global(qos: .background).async {
let userConversationsIDs = userConversations.map { $0.id }
DispatchQueue.main.async {
do {
let realm = try Realm()
let userConversationPredicate = NSPredicate(format: "NOT id IN %#", userConversationsIDs)
let notActualUserConversations = realm.objects(ChatUserPersonalConversationModel.self).filter(userConversationPredicate)
let filteredNotActualUserConversations = Array(notActualUserConversations.filter({ $0.lastActivityTimeStamp < $0.removedChatTimeStamp }))
let notActualConversationIDs = filteredNotActualUserConversations.map { $0.id }
let notActualConversationPredicate = NSPredicate(format: "id IN %#", notActualConversationIDs)
let notActualConversations = realm.objects(ChatConversationModel.self).filter(notActualConversationPredicate)
let notActualMessagesPredicate = NSPredicate(format: "conversationID IN %#", notActualConversationIDs)
let notActualMessages = realm.objects(ChatMessageRealmModel.self).filter(notActualMessagesPredicate)
try realm.write {
realm.add(userConversations, update: true)
realm.add(conversations, update: true)
for notActualUserConversation in notActualUserConversations {
notActualUserConversation.toDelete = true
}
for notActualConversation in notActualConversations {
notActualConversation.toDelete = true
}
for notActualMessage in notActualMessages {
notActualMessage.toDelete = true
}
completion?(nil)
}
} catch {
debugPrint(error.localizedDescription)
completion?(error)
}
}
}
}

Using Object Mapping with Kinvey

I have an array of objects I'm trying to get out of one of my collections. I've followed along using their docs and also some Googling and I believe I'm close to the solution, however not close enough. Here's what I have:
class Clothing: Entity {
var categories: [Category]!
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "categories"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- map["clothing"]
gender <- map["gender"]
}
}
class Category: NSObject, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- map["category"]
image <- map["image"]
}
}
I'm able to get the right gender, but the array of categories doesn't seem to get mapped to the Category object. Any thoughts?
your model actually have one issue, as you can see at https://devcenter.kinvey.com/ios/guides/datastore#Model you should use let categories = List<Category>() instead of var categories: [Category]!. Here's the model that and test and worked:
import Kinvey
class Clothing: Entity {
let categories = List<Category>()
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "clothing"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- ("categories", map["categories"])
gender <- ("gender", map["gender"])
}
}
class Category: Object, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- ("category", map["category"])
image <- ("image", map["image"])
}
}
and here's a sample code how to save a new Clothing object
let casualCategory = Category()
casualCategory.title = "Casual"
let shirtCategory = Category()
shirtCategory.title = "Shirt"
let clothing = Clothing()
clothing.gender = "male"
clothing.categories.append(shirtCategory)
clothing.categories.append(casualCategory)
dataStore.save(clothing) { (result: Result<Clothing, Swift.Error>) in
switch result {
case .success(let clothing):
print(clothing)
case .failure(let error):
print(error)
}
}

RealmSwift + ObjectMapper managing String Array (tags)

What I need to represent in RealmSwift is the following JSON scheme:
{
"id": 1234,
"title": "some value",
"tags": [ "red", "blue", "green" ]
}
Its a basic string array that I'm stumbling on. I'm guessing in Realm I need to represent "tags" as
dynamic id: Int = 0
dynamic title: String = ""
let tags = List<MyTagObject>()
making tags its own table in Realm, but how to map it with ObjectMapper? This is how far I got...
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
tags <- map["tags"]
}
... but the tags line doesn't compile of course because of the List and Realm cannot use a [String] type.
This feels like a somewhat common problem and I'm hoping someone who has faced this can comment or point to a post with a suggestion.
UPDATE 1
The MyTagObject looks like the following:
class MyTagObject: Object {
dynamic var name: String = ""
}
UPDATE 2
I found this post which deals with the realm object but assumes the array has named elements rather than a simple string.
https://gist.github.com/Jerrot/fe233a94c5427a4ec29b
My solution is to use an ObjectMapper TransformType as a custom method to map the JSON to a Realm List<String> type. No need for 2 Realm models.
Going with your example JSON:
{
"id": 1234,
"title": "some value",
"tags": [ "red", "blue", "green" ]
}
First, create an ObjectMapper TransformType object:
import Foundation
import ObjectMapper
import RealmSwift
public struct StringArrayTransform: TransformType {
public init() { }
public typealias Object = List<String>
public typealias JSON = [String]
public func transformFromJSON(_ value: Any?) -> List<String>? {
guard let value = value else {
return nil
}
let objects = value as! [String]
let list = List<String>()
list.append(objectsIn: objects)
return list
}
public func transformToJSON(_ value: Object?) -> JSON? {
return value?.toArray()
}
}
Create your 1 Realm model used to store the JSON data:
import Foundation
import RealmSwift
import ObjectMapper
class MyObjectModel: Object, Mappable {
#objc dynamic id: Int = 0
#objc dynamic title: String = ""
let tags = List<MyTagObject>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
tags <- (map["tags"], StringArrayTransform())
}
}
Done!
This line is the magic: tags <- (map["tags"], StringArrayTransform()). This tells ObjectMapper to use our custom StringArrayTransform I showed above which takes the JSON String array and transforms it into a Realm List<String>.
First of all we should assume that our model extends both Object and Mappable.
Let's create a wrapper model to store the primitive (String) type:
class StringObject: Object {
dynamic var value = ""
}
Then we describe corresponding properties and mapping rules for the root model (not the wrapper one):
var tags = List<StringObject>()
var parsedTags: [String] {
var result = [String]()
for tag in tags {
result.append(tag.value)
}
return result
}
override static func ignoredProperties() -> [String] {
return ["parsedTags"]
}
func mapping(map: Map) {
if let unwrappedTags = map.JSON["tags"] as? [String] {
for tag in unwrappedTags {
let tagObject = StringObject()
tagObject.value = tag
tags.append(tagObject)
}
}
}
We need a tags property to store and obtain the data about tags from Realm.
Then a parsedTags property simplifies extraction of tags in the usual array format.
An ignoredProperties definition allows to avoid some failures with Realm while data savings (because of course we can't store non-Realm datatypes in the Realm).
And at last we are manually parsing our tags in the mapping function to store it in the Realm.
It will work if your tags array will contains a Dictionary objects with a key: "name"
{
"id": 1234,
"title": "some value",
"tags": [ ["name" : "red"], ... ]
}
If you cannot modify JSON object, I recommend you to map json to realm programmatically.
for tagName in tags {
let tagObject = MyTagObject()
tagObject.name = tagName
myObject.tags.append(tagObject)
}
Follow this code
import ObjectMapper
import RealmSwift
//Create a Model Class
class RSRestaurants:Object, Mappable {
#objc dynamic var status: String?
var data = List<RSData>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
status <- map["status"]
data <- (map["data"], ListTransform<RSData>())
}
}
//Use this for creating Array
class ListTransform<T:RealmSwift.Object> : TransformType where T:Mappable {
typealias Object = List<T>
typealias JSON = [AnyObject]
let mapper = Mapper<T>()
func transformFromJSON(_ value: Any?) -> Object? {
let results = List<T>()
if let objects = mapper.mapArray(JSONObject: value) {
for object in objects {
results.append(object)
}
}
return results
}
func transformToJSON(_ value: Object?) -> JSON? {
var results = [AnyObject]()
if let value = value {
for obj in value {
let json = mapper.toJSON(obj)
results.append(json as AnyObject)
}
}
return results
}
}

ObjectMapper not serialising new fields

I have an class:
class ChatMessage: Object, Mappable {
dynamic var fromId = ""
dynamic var toId = ""
dynamic var message = ""
dynamic var fromName = ""
dynamic var created: Int64 = 0
required convenience init?(map: Map) {
self.init()
}
func configure(_ fromId:String,toId:String, message:String) {
self.fromId=fromId
self.toId=toId
self.message=message
self.created = Int64((NSDate().timeIntervalSince1970 * 1000.0))
}
func mapping(map: Map) {
created <- map["created"] //a: this was added later
fromId <- map["fromId"]
toId <- map["toId"]
message <- map["message"]
fromName <- map["fromName"]
}
}
I am using ObjectMapper to serialise the object to JSON and Realm to store it in the local database.
I had added the created field later to the mapping when the Realm db was already storing the ChatMessage object.
Now when I am instantiating the ChatMessage object and trying to convert it into JSON object using ObjectMapper. Following is the code:
func sendChatMessage(_ chatMessage:ChatMessage, callback: #escaping DataSentCallback) -> Void {
var chatMessageString:String!
let realm = DBManager.sharedInstance.myDB
try! realm?.write {
chatMessageString = Mapper().toJSONString(chatMessage, prettyPrint: false)!
}
...
}
Now when I print chatMessage, I get:
ChatMessage {
fromId = 14;
toId = 20;
message = 2;
fromName = ;
created = 1477047392597;
}
And when I print chatMessageString, I get:
"{\"toId\":\"20\",\"message\":\"2\",\"fromName\":\"\",\"fromId\":\"14\"}"
How come does the created field not appear in the string?
The problem was in the mapping of Int64 type as mentioned in this issue on github.
By changing the mapping of created to the following form, everything worked fine:
created <- (map["created"], TransformOf<Int64, NSNumber>(fromJSON: { $0?.int64Value }, toJSON: { $0.map { NSNumber(value: $0) } }))

Using Alamofire and Objectmapper the integer value always zero

I am using Alamofire with ObjectMapper and my model class is like that
class Category: Object, Mappable {
dynamic var id: Int = 0
dynamic var name = ""
dynamic var thumbnail = ""
var children = List<Category>()
override static func primaryKey() -> String? {
return "id"
}
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
thumbnail <- map["thumbnail"]
children <- map["children"]
}
}
and I am using Alamofire like that
Alamofire.request(.GET, url).responseArray { (response: Response<[Category], NSError>) in
let categories = response.result.value
if let categories = categories {
for category in categories {
print(category.id)
print(category.name)
}
}
}
the id is always zero, I don't know why?
I fixed it by adding transformation in the mapping function in model class like that
id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
thanks to #BobWakefield
Does the "id" field exist in the JSON file? If it does not, your initial value of zero will remain. Is the value in quotes in the JSON file? If it is, then it's a string. I don't know if ObjectMapper will convert it to Int.
Moved my comment to an answer.