Handling RLMResult in heterogeneous table view - ios

How should I handle a table view whose cells represent both a RLMResult and other custom views?
Say I have this structure, a todo list app that shows items of todo list and some ads:
protocol TableItem {}
class ToDoItem: RLMObject, TableItem {
// properties
}
class Advertising: TableItem {
// properties
}
class ViewController: UIViewController {
var result: RLMResult<ToDoItem>
var ads: [Advertising]
var tableViewItems: [TableItem] // Created by mixing both `result` and `ads`.
}
// other class stuff
I have to dequeue a different cell based on the the type of each item in tableViewItems.
The simplest thing would be to convert the result property to a simple Array<ToDoItem> and then concatenate both arrays; Realm documentation discourages to convert a RLMResult to a simple array to avoid losing all lazy features of RLMResult.
Any idea?

Related

Generics types in VIPER subclassing. Cannot assign value of type 'ChildType' to type 'ParentType<Any>?'

I have VIPER architecture and I want to create parent VIPER search component that works with generic type. The idea is simple so I have search bar and I have table view. For example I can show drinks or foods names in table view. Depends on which generic data type I specified I want to show or foods or drinks.
I found very good example that solves my issue with generic view controllers. But I want to create something similar for swift generic VIPER architecture.
I will skip describing all VIPER classes like (Router, Interdictor and etc).
So I have parent view controller:
BaseSearchViewController: UIViewController {
var presenter: BaseSearchPresenter<Any>?
}
and child:
FoodSearchViewController: BaseSearchViewController {
}
This is a parent presenter specified in BaseSearchViewController
class BaseSearchPresenter<T> {
var items [T]
}
also I have child food search presenter with a specific type Food I would like to display in my table:
class FoodSearchPresenter: BaseSearchPresenter<Food> {
}
When I try to configure my VIPER:
let viewController = FoodSearchViewController(...) // init VC
let presenter = FoodSearchPresenter()
viewController.presenter = presenter // assigning child FoodSearchPresenter instance to a BaseSearchViewController presenter variable leads to this error:
Cannot assign value of type 'FoodSearchPresenter' to type 'BaseSearchPresenter<Any>?'
Here is a repo with the issue.
I left comments about how to attack this problem, but to your specific case, the answer is that BaseSearchViewController needs to be generic.
class BaseSearchPresenter<T> {
var items: [T] = []
}
// BaseSearchViewController must be generic in order to vary its Element
class BaseSearchViewController<Element>: UIViewController {
var presenter: BaseSearchPresenter<Element>?
}
struct Food {}
class FoodSearchPresenter: BaseSearchPresenter<Food> {}
class FoodSearchViewController: BaseSearchViewController<Food> {}
let viewController = FoodSearchViewController()
let presenter = FoodSearchPresenter()
viewController.presenter = presenter
To my point about starting concrete, I mean start with FoodSearchViewController as its own thing. Don't inherit from anything except UIViewController. Then build a second view controller that would make sense in this app and would need something similar. Then extract the commonality between them. This will tend to drive you in the right direction.

Proper way to pass multiple values with protocols in iOS

So I have two ViewControllers. First (MapVC) with map and second (SettingsVC) with many settings that need to be applied to this map.
I thought it would be nice idea to create protocol like
protocol MapSettingsDelegate: class {}
I know that I can specify function inside this protocol. But how I should do it when I have many settings - how should I pass them from SettingsVC to MapVC.
Example:
struct MySettings {
var value1: String
var value2: String
// and so on...
}
protocol MapSettingsDelegate: class {
func settingsUpdated(newSettings: MySettings)
}
and implement it inside your controller
class MapVC : MapSettingsDelegate {
...
func settingsUpdated(newSettings: MySettings) {
// Update everything you need
}
...
}
Feel free to ask for details

Swift: Store Type of implementation of Protocol in static var of protocol extension

Okay so I have this protocol MenuEntry which I want to use to populate a TableView:
protocol MenuEntry {
static var title: String { get }
static func entrySelected(_ menuController: MenuController)
}
I want to implement this protocol in various places and let the item itself decide what to do. It might be a UIViewController which implements the protocol or a simple struct, which then calls a function on the menu itself:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
Now I want to build the MenuControllers datasource but without actually instantiating the entries because especially my view controllers are not necessarily available when the MenuControllers datasource is populated. Thats why I use static var/func in MenuEntry. Currently, I can simply do this to populate the datasource:
let dataSource: [MenuEntry.Type] = [SomeEntry.self]
And it seems to work pretty well. I can get the entries and call the corresponding functions:
dataSource.first?.title //Gives me "Some Entry"
Now comes the tricky part. I thought I could be really clever and create a protocol extension where I reference all the types in which I implement the protocol like so:
extension MenuEntry {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
//...
}
And then later use them via MenuEntry.someEntry. However, accessing someEntry on MenuEntry gives me an error:
error: static member 'someEntry' cannot be used on protocol metatype 'MenuEntry.Protocol'
So my question is: what am I missing? Am I just trying to misuse the language in a way which is not intended or am I just doing something wrong?
SOLUTION
From the accepted answer below, heres how I now do things. First, we need the mentioned struct (no need for a class I guess):
struct MenuEntries {}
Then, where ever I implement the MenuEntry protocol, I also extend this struct and add the entry like so:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
extension MenuEntries {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
}
The last thing is to create my datasource like so:
let dataSource: [MenuEntry.Type] = [MenuEntries.someEntry, ...]
Okay, now I have a list of all menu entries in one place. The downside is I have to remember to extend MenuEntries every time. Except there is some magical way to extend a struct on a conditional base I'm not aware of. But I guess thats just over the top and simply not possible.
From The Swift Book
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements.”
Your extension is attempting to implement functionality directly in the protocol, but this is not allowed; Only a class, structure or enumeration adopting the protocol can provide functionality.
You could define a class that returns your menu classes:
class MenuFactory {
static var someEntry: MenuEntry.type { return SomeEntry.self }
}

Save array of images with Realm in swift

okay, i have an app with 3 view controllers and in 2 of them i have 5 arrays of images. what i want to do is save each array of images using realm. the arrays are mutable and the user adds the images to the array of their choosing in vc1 and can send them to the arrays in vc2, but im not sure if i can just replace (in vc1)
this:
var array: [UIImage] = [] {
didSet{
cView1.reloadData()
}
}
with This:
dynamic var array: [UIImage] = [] {
didSet{
cView1.reloadData()
}
}
i am also getting this error when i try to inherit RLMObject.
"multiple inheritance from classes uiviewcontroller and RLMObject"
here is my code
class CViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDelegate, UICollectionViewDataSource, CDelegate , RLMObject
im fairly new to ios developing so any little bit helps thanks in advance
The best approach is to save file path in DB, not image itself. So you need to create array of image paths instead array of images. Like this: let images = List<String>()
Also according to Realm Docs 'List' can't be dynamic:
When added as a property on Object models, the property must be declared as let and cannot be dynamic.
And final, you must to inherit from 'Object', not 'RLMObject' and Realm class must be separate, like in this official example:
class Dog: Object {
dynamic var name: String = ""
dynamic var adopted: Bool = false
let siblings = List<Dog>()
}

Swift superclass/subclass view controller variables

I have a superclass UIViewController that contains this:
var results: [AnyObject] = []
I also have other methods in the superclass that's based on the content of results array.
I then have subclasses that inherit from this superclass, by loading data from REST API to populate results array with various types of data.
I currently don't override results array in subclass' viewDidLoad() or any other method. I simply clear the array using results = [] and then load the array with a specific class type, such as a custom class Product or String.
results.append(Product())
I've been intermittenly getting crashes, but I can't tell why. I suspect it's due to the way the inheritance is working. Am I doing this correctly? Should I be overriding results in an init() method of the subclass or using `override var?
I think typecasting can work here.
Try to do this in your subclasses:
class SubClass: SuperClass {
private var _results: [Product] = []
override var results: [AnyObject] {
get {
return _results
}
set {
_results = newValue as! [Product]
}
}
}
But Product must be a class and not struct or enum.
Also as String does not conform to AnyObject you cannot save Strings in results array.

Resources