I have one function which is having some logic which have 2 foreach loop but i want to make code compact so I am trying to use compactmap
func getData() -> [String] {
var ids = [String]()
self.item?.connections?.forEach { connection in
connection.validLine?.forEach { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
ids += checkinGroups.connections?.compactMap { $0.id } ?? []
}
}
}
return ids
}
so instead of 2 foreach i am trying to make in one by using self.item?.connections?.compactMap({ $0.validline }) but I am getting error saying "Type of expression is ambiguous without more context"
I don't see how you can do it without to forEach or compactMap. Here is a possible solution:
func getData() -> [String] {
return item?.connections?.compactMap { connection in
connection.validLine?.compactMap { line in
guard let _ = line.connection?.links[LinkKey.dataGroups], line.dataGroupsCache?.isContinue == true else { return nil }
return checkinGroups.connections?.compactMap(\.id)
}
}
}
Here's a translation of your post into something that is compilable and a direct translation into a version that doesn't use forEach.
I changed connectionIds to ids in your example because otherwise, you might as well just return [].
class Example {
func getData() -> [String] {
var ids = [String]()
self.item?.connections?.forEach { connection in
connection.validLine?.forEach { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
ids += checkinGroups.connections?.compactMap { $0.id } ?? []
}
}
}
return ids
}
func getDataʹ() -> [String] {
guard let connections = item?.connections else { return [] }
let numberOfProperLines = connections.flatMap { $0.validLine ?? [] }
.filter { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
return true
} else {
return false
}
}
.count
return (0..<numberOfProperLines).flatMap { _ in checkinGroups.connections?.compactMap(\.id) ?? [] }
}
var checkinGroups: CheckInGroups!
var item: Item!
}
enum LinkKey: Int {
case dataGroups
}
struct Item {
let connections: [Connection]?
}
struct Connection {
let id: String?
let validLine: [Line]?
let links: [LinkKey: Void]
}
struct Line {
let dataGroupsCache: DataGroups?
let connection: Connection?
}
struct DataGroups {
let isContinue: Bool
}
struct CheckInGroups {
let connections: [Connection]?
}
Related
In my view controller I have a UISearchController associated with the UITableView. So all my normal table view datasource methods do the old
if isSearching {
// use filteredTableData array
} else {
// use SharedModel.shared.participants
}
I'm not clear on how I'd implement that using RxCocoa as I'm brand new to Rx.
Create a Variable like below
var tableViewOptions = Variable<[String]>([]) // replace String with your own object
bind tableViewOptions to tableview after your view loads.
tableViewOptions
.asObservable()
.bind(to: self.tableView
.rx
.items(cellIdentifier: "cellIdentifier",
cellType: CustomCell.self)) { _, values, cell in
// do your stuff
}
Then when ever you search change the values of tableViewOptions like below.
if isSearching {
tableViewOptions.value = filteredTableArray
} else {
tableViewOptions.value = SharedModel.shared.participants
}
I solve this by declaring a decorator for an Observable and an extension for a UISearchBar (you could also declare it for the UISearchController):
//FIXME: Item can't be type constrained. Have to use optional casting.
class FilterableByTitleCollection<Item>: ObservableType {
private let origin: Observable<Array<Item>>
private let filteringStrategySource: Observable<TitlableModelFilteringStrategy> //FIXME: This is a strategy source
init<Origin: ObservableType>(
origin: Origin,
filteringStrategySource: Observable<TitlableModelFilteringStrategy>) where Origin.E == Array<Item> {
self.origin = origin.asObservable()
self.filteringStrategySource = filteringStrategySource
}
typealias E = Array<Item>
func subscribe<O:ObserverType>(_ observer: O) -> Disposable where O.E == Array<Item> {
return Observable.combineLatest(
origin,
filteringStrategySource
)
.observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
.map{ origin, strategy in
guard origin is Array<Titlable> else { assert(false); return origin }
return origin.filter{ strategy.shouldInclude(item: $0 as! Titlable) }
}
.observeOn(MainScheduler.instance)
.subscribe(observer)
}
}
...
extension UISearchBar {
var titlableFilteringStrategy: Observable<TitlableModelFilteringStrategy> {
return Observable<String?>.merge(
[
self.rx.text.asObservable(),
self.rx.textDidEndEditing
.map{ [weak self] in
assert(self != nil)
return self?.text
},
self.rx.cancelButtonClicked.map{ Optional<String>.some("") }
]
).distinctUntilChanged{ (old: String?, new: String?) -> Bool in
old == new
}.map{ TitlableModelFilteringStrategy(filteringPredicate: $0) }
}
}
...
struct TitlableModelFilteringStrategy {
private let filteringPredicate: String
init(filteringPredicate: String?) {
self.filteringPredicate = filteringPredicate ?? ""
}
func shouldInclude(item: Titlable) -> Bool {
return filteringPredicate.isEmpty ? true : item.title.localizedCaseInsensitiveContains(filteringPredicate)
}
func equals(to another: TitlableModelFilteringStrategy) -> Bool {
return filteringPredicate == another.filteringPredicate
}
}
...
protocol Titlable {
var title: String { get }
}
I am facing an issue where I am unable to keep existing relationships after calling add(_, update: true) function.
I wrote a TaskSync class that is responsible for creating/updating Task objects:
class TaskSync: ISync {
typealias Model = Task
func sync(model: Task) {
let realm = try! Realm()
let inWrite = realm.isInWriteTransaction
if !inWrite {
realm.beginWrite()
}
let _task = realm.object(ofType: Task.self, forPrimaryKey: model.id)
// Persist matches as they are not getting fetched with the task
if let _task = _task {
print("matches: \(_task.matches.count)")
model.matches = _task.matches
}
realm.add(model, update: true)
if _task == nil {
var user = realm.object(ofType: User.self, forPrimaryKey: model.getUser().id)
if (user == nil) {
user = model.getUser()
realm.add(user!, update: true)
}
user!.tasks.append(model)
}
if !inWrite {
try! realm.commitWrite()
}
}
func sync(models: List<Task>) {
let realm = try! Realm()
try! realm.write {
models.forEach { task in
sync(model: task)
}
}
}
}
When a model is to be synced, I check if it already exists in the Realm and if so, I fetch it and try to include the matches property as this one is not included in the model.
Right before the call realm.add(model, update: true), model contains list of matches, however right after the realm.add is executed, the matches list is empty.
Here are the two models:
class Task: Object, ElementPreloadable, ElementImagePreloadable, ItemSectionable {
dynamic var id: Int = 0
dynamic var title: String = ""
dynamic var desc: String = ""
dynamic var price: Float = 0.0
dynamic var calculatedPrice: Float = 0.0
dynamic var location: String = ""
dynamic var duration: Int = 0
dynamic var date: String = ""
dynamic var category: Category?
dynamic var currency: Currency?
dynamic var longitude: Double = 0.0
dynamic var latitude: Double = 0.0
dynamic var state: Int = 0
dynamic var userId: Int = 0
// Existing images
var imagesExisting = List<URLImage>()
// New images
var imagesNew = List<Image>()
// Images deleted
var imagesDeleted = List<URLImage>()
private let users = LinkingObjects(fromType: User.self, property: "tasks")
var user: User?
var matches = List<Match>()
dynamic var notification: Notification?
override static func ignoredProperties() -> [String] {
return ["imagesExisting", "imagesNew", "imagesDeleted", "user", "tmpUser"]
}
override static func primaryKey() -> String? {
return "id"
}
func getImageMain() -> URLImage? {
for image in imagesExisting {
if image.main {
return image
}
}
return imagesExisting.first
}
func getSection() -> Int {
return state
}
func getSectionFieldName() -> String? {
return "state"
}
func getId() -> Int {
return id
}
func getURL() -> URL? {
if let image = getImageMain() {
return image.getResizedURL()
}
return nil
}
func getState() -> TaskOwnState {
return TaskOwnState(rawValue: state)!
}
func getUser() -> User {
return (user != nil ? user : users.first)!
}
}
class Match: Object, ElementPreloadable, ElementImagePreloadable, ItemSectionable {
dynamic var id: Int = 0
dynamic var state: Int = -1
dynamic var priorityOwnRaw: Int = 0
dynamic var priorityOtherRaw: Int = 0
dynamic var user: User!
var messages = List<Message>()
private let tasks = LinkingObjects(fromType: Task.self, property: "matches")
var task: Task?
dynamic var notification: Notification?
override static func primaryKey() -> String? {
return "id"
}
override static func ignoredProperties() -> [String] {
return ["task"]
}
func getId() -> Int {
return id
}
func getSection() -> Int {
return 0
}
func getURL() -> URL? {
if let image = user.getImageMain() {
return image.getResizedURL()
}
return nil
}
func getPriorityOwn() -> PriorityType {
if priorityOwnRaw == PriorityType.normal.rawValue {
return PriorityType.normal
}
else {
return PriorityType.favorite
}
}
func getPriorityOther() -> PriorityType {
if priorityOtherRaw == PriorityType.normal.rawValue {
return PriorityType.normal
}
else {
return PriorityType.favorite
}
}
func getSectionFieldName() -> String? {
return nil
}
func getTask() -> Task {
return (task != nil ? task : tasks.first)!
}
}
I spent hours trying to figure out why I am unable to keep the matches relationship when updating the task. Every advice will be highly appreciated!
This question was also asked upon Realm's GitHub issue tracker. For posterity, here is the solution.
List properties should always be declared as let properties, as assigning to them does not do anything useful. The correct way to copy all objects from one List to another is model.tasks.append(objectsIn: _user.tasks).
In my watchOS2 app I have array of tuples like this:
var medicines = [(String, String?, String?)]()
And in refreshing function i'd like to clear this array of tuples to append it with new String items. How can i do this ? I want to avoid having the same things in my array. Or maybe there is a better idea ?
My refresh function:
let iNeedCoreData = ["Value": "CoreData"]
session.sendMessage(iNeedCoreData, replyHandler: { (content: [String: AnyObject]) -> Void in
if let meds = content["reply"] as? [String: [String]] {
self.medicines = [(String, String?, String?)]()
if let medicineNames = meds["medicines"], amountNames = meds["amount"], timeNames = meds["time"] {
if medicineNames.count != 0 {
self.addMedicines(medicineNames)
self.addQuantities(amountNames)
self.addTime(timeNames)
self.table.setHidden(false)
self.reloadTable()
} else {
self.alertLabel.setHidden(false)
}
}
}
}) { (error) -> Void in
print("We got an error from our watch device:" + error.domain)
}
Adding to tuple funcs:
func reloadTable() {
self.table.setNumberOfRows(medicines.count, withRowType: "tableRowController")
var rowIndex = 0
for item in medicines {
if let row = self.table.rowControllerAtIndex(rowIndex) as? tableRowController {
row.medicineLabel.setText(item.0)
if let quantity = item.1, time = item.2 {
row.amountLabel.setText(quantity)
row.timeLabel.setText(time)
}
rowIndex++
}
}
}
func addMedicines(medicineNames: [String]) {
for name in medicineNames {
medicines.append((name, nil, nil))
}
}
func addQuantities(quantities: [String]) {
guard medicines.count == quantities.count else { return }
for i in 0..<medicines.count {
medicines[i].1 = quantities[i]
}
}
func addTime(timeNames: [String]) {
guard medicines.count == timeNames.count else { return }
for i in 0..<medicines.count {
medicines[i].2 = timeNames[i]
}
}
Once the var has been declared, type hints are no longer needed.
self.medicines = []
I've tried to think of a few ways to overcome your problem here, but your code is very inflexible and needs to be refactored.
You are at the limit for the utility of tuples and need to turn medicine into a class or struct (use a struct) which supports Equatable.
In addition, you need to create an array of new objects, which can be merged into the existing self.medicines, building the new objects directly in self.medicines is very limiting.
Here is the tuple as a struct
struct Medicine: Equatable {
let name: String
let amount: String
let time: String
}
func == (lhs: Medicine, rhs: Medicine) -> Bool {
return lhs.name == rhs.name && lhs.amount == rhs.amount && lhs.time == rhs.time
}
Here is adding new values without removing old values or having duplicates
if let names = meds["medicines"], amounts = meds["amount"], times = meds["time"]
where names.count == amounts.count && names.count == times.count
{
for i in 0..<names.count {
let medicine = Medicine(name: names[i], amount: amounts[i], time: times[i])
if !medicines.contains(medicine) {
medicines.append(medicine)
}
}
}
I'm not sure that this not duplicate. I have protocol and several classs that confurm to it.
protocol DbObject: class {
class func tableName() -> String
init(set: FMResultSet)
func save(db: FMDatabase)
}
And here how I use it:
func updateTable<T where T:DbObject>(nodes: [T]) {
self.db.executeUpdate("DELETE from \(T.tableName())")
for elem in nodes {
elem.save(self.db)
}
}
func loadAllFromObjectTable<T where T:DbObject>(cl: T) -> [T] {
var objArr = [T]()
if let set = db.executeQuery("select * from \(T.tableName())") {
while (set.next() ?? false) {
let obj = T(set: set)
objArr.append(obj)
}
}
return objArr
}
But I wanna that function loadAllFromObjectTable get as parameter class that confirms protocol, not object. How can I achieve that?
EDIT:
From here https://stackoverflow.com/a/26229630/820795
func loadAllFromObjectTable<T where T:DbObject>(_: T.Type) -> [T] {
var objArr = [T]()
if let set = db.executeQuery("select from \(T.tableName())") {
while (set.next() ?? false) {
if let obj = T(set: set)
objArr.append(obj)
}
}
return objArr
}
And usage:
manager.loadAllFromObjectTable(SignNote.self)
I have a syncing engine with a server that follows this code for created updated and deleted objects
let lastSynchronizationDate = (NSUserDefaults.standardUserDefaults().objectForKey("com.fridge.sync") as [String: NSDate])["skin"]!
query.whereKey("updatedAt", greaterThanOrEqualTo: lastSynchronizationDate)
query.findObjectsInBackgroundWithBlock { (remoteSkins, error) -> Void in
if error != nil {
return
}
if remoteSkins.isEmpty {
return
}
RLMRealm.defaultRealm().transactionWithBlock {
let deletedSkins = (remoteSkins as [PFObject]).filter {
$0["state"] as Int == 0
}.map {
Skin(forPrimaryKey: $0["color"])
}.filter {
$0 != nil
}.map {
$0!
}
let createdSkins = (remoteSkins as [PFObject]).filter {
$0["state"] as Int != 0
}.filter {
let skin = Skin(forPrimaryKey: $0["color"])
println(skin.name)
return skin == nil
}.map { (remoteSkin) -> Skin in
let remoteSkinModel = RemoteSkinModel(remoteSkin)
let skin = Skin()
skin.skinModel = remoteSkinModel // Error
return skin
}
let updatedSkins = (remoteSkins as [PFObject]).filter {
$0["state"] as Int != 0
}.map {
($0, Skin(forPrimaryKey: $0["color"]))
}.filter {
$0.1 != nil
}.map {
$0.1.skinModel = RemoteSkinModel($0.0)
}
RLMRealm.defaultRealm().deleteObjects(deletedSkins)
RLMRealm.defaultRealm().addObjects(createdSkins)
}
}
My skin model:
class Skin: RLMObject {
dynamic var name: String = ""
dynamic var hexString: String = ""
var skinModel: SkinModel! {
didSet {
name = skinModel.name ?? oldValue.name ?? ""
hexString = skinModel.hexString ?? oldValue.name ?? ""
}
}
override class func primaryKey() -> String {
return "hexString"
}
override class func ignoredProperties() -> [AnyObject] {
return ["skinModel"]
}
func save() {
}
}
protocol SkinModel {
var name: String { get }
var hexString: String { get }
}
class RemoteSkinModel: SkinModel {
let remoteSkin: PFObject
let name: String
let hexString: String
init(_ remoteSkin: PFObject) {
self.remoteSkin = remoteSkin
self.name = remoteSkin["name"] as? String ?? ""
self.hexString = remoteSkin["color"] as? String ?? ""
}
}
The first the engine runs everything goes well, but the next time it pops the bad access error when comparing if the realm object is nil or not in the createdSkins' code.
Any idea as to why this would happen?
Thanks in advance
What happens if you force skin to be an optional?
let skin: Sin? = Skin(forPrimaryKey: $0["color"])
if let skin = skin {
println(skin.name)
}
return skin == nil
I think the Swift compiler may be preventing checks to nil because Skin(forPrimaryKey:) should be a failable initializer, but Realm can't mark that as such (only Apple code can have those).
If that's not the issue, let me know and we'll keep troubleshooting this together. (I work at Realm).