how can I access field/method from another UIViewController in Swift? - ios

I have the following situation:
I have a UIViewController that has embedded two Container Views, one is hidden and one is visible. Both of those Container Views have embedded another UIViewControllers with some elements.
I want to present a data fetched from webservice on both panels, but I want to fetch the data only once, then just parse it in a specific way, based on what panel user currently sees.
I have a method that fetches data as json:
func loadInitialDataWithoutCluster() {
print("loadInitialData");
RestApiManager.sharedInstance.getRequests { json in
if let jsonData = json.array {
for requestJSON in jsonData {
dispatch_async(dispatch_get_main_queue(),{
if let request = SingleRequest.fromJSON(requestJSON){
//how can I add those single requests
// to a list/array/whatever here that will be
// accessible from both of two other panels?
}
})
}
}
}
}
and then when it fetches all the data and (somehow) assigns it to the list/array - how can I refer to it from two different panels?

This would be the solution for best programming practice. Technically you shouldn't allow any class to manipulate your data directly, instead create functions which do the work for you. I have included a function which reads and writes to an array, you can manipulate these to your needs.
class ArrayObject {
private var downloadedData = [1,2,3]
internal func readData() -> [Int] {
return downloadedData
}
internal func addData(number: Int) -> [Int] {
downloadedData.append(number)
return downloadedData
}
}
class genericControllerClass: UIViewController {
func currentData() {
let getUsers = ArrayObject().addData(15)
}
}

You can create a new file with a simple structure to store an array of the details you need, you can access this from anywhere in the project.
struct Arrays {
static var downloadedData = [TheTypeYouNeedToStore]()
}
You can add to this array once the data has been downloaded just by using Arrays.downloadedData.append(). Then anywhere else in the project this can be accessed and modified using Arrays.downloadedData[0]

Related

Append filtered string array to UILables - Swift

Long title! I do apologize.
Expected Outcome: Display uniqued string value in UILabel inside a UIView in a stackView. There may be multiple UIViews in the stackView. The stackView lives inside a tableCell. wow....
The views and cells are custom and I am not allowed to create sections. I have to work with what's in the existing codebase.
Issue I am stuck at trying to get the unique optional string values into the respective UILabels. I have a working extension to get unique items from an array. But I just don't know where to implement it, to get the unique values I need.
Code:
// Sample Model Structs
struct Parent {
var child: [Child]?
}
struct Child {
var childValue: String?
}
class TableViewCell {
var stackView = UIStackView()
func configureCellFrom(parent: Parent) {
/// Other code lives in the func to use the Parent struct.
if let child = parent.child {
if child.count > 1 {
tableCell.topLabel.text = "Multiple Child Values"
tableCell.ShowChildViewsButton.isHidden = false
for value in child {
let view = CustomUIView()
view.childValue.text = value.childValue.uniqued()
self.stackView.addArrangedSubview(view)
}
}
}
}
}
extension Sequence where Element: Hashable {
func uniqued() -> [Element] {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
Above Problem: Where I placed the uniqued() method, will parse out the individual characters in the string. So I know that it is one level too deep. What's the best way to achieve my required result?
The issue there is that uniqued method uses filter method declared on Sequence which will always return an array of the Sequence.Element in the case of a String an array of characters [Character]. You can simply initialize a new string with the array or characters or improve that method to support strings as well. To make that method support strings you need to extend RangeReplaceableCollection. There is a filter method declared on RangeReplaceableCollection which returns Self therefore if you filter a string it will return another string as I showed in this answer from the same post where you found the uniqued method you've shown in your question:
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
Usage:
view.childValue.text = value.childValue?.orderedSet
Try this:
for (i, value) in child.enumerated() {
let view = CustomUIView()
view.childValue.text = value.childValue.uniqued()[i]
self.stackView.addArrangedSubview(view)
}

How to modify Object in Dependency Injection Pattern

So for the time I worked on the project and avoided Singletons and used Dependency Injection. By this I mean instead of creating a shared instance I created a class instance and passed to all controllers whichever needs.
Now my question, my model object which has references in all controllers, I need to point them either to a new object as for the requirements the data is fully updated like calling the init() again.
But if I do that in a certain controller that reference will only point to this new object.
So if you get what I mean I want the pointee of the references or where at memory address that object is there should be replaced to a new one and all references should still point to that old address / new object.
I think you need to inject not model in controllers, but service for obtaining and saving this model, something like this.
Protocol for service:
protocol ModelServiceProtocol {
func obtainModel(completion: (Model) -> Void)
func save(model: Model, compleiton: ()->Void)
}
Example ViewController with dependency:
class ViewController: UIViewController {
let modelService: ModelServiceProtocol
init(modelService: ModelServiceProtocol) {
self.modelService = modelService
}
func obtainModel() {
modelService.obtainModel { model in
// do something
}
}
func saveEditedModel() {
modelService.save(model: model) {
// model saved
}
}
}
Implementation of ModelService that will obtain and save your model:
class ModelService: ModelServiceProtocol {
func obtainModel(completion: (Model) -> Void) {
// implementation of obtainig model from some storage
}
func save(model: Model, compleiton: ()->Void) {
// implementation of saving model in some storage
}
}
Injection of dependency:
func buildController() -> ViewController {
let modelService = ModelService()
let viewController = ViewController(modelService: modelService)
return viewController
}
In this approach you will get actual model in ViewController, edit and save to some storage. Model will be actual on every step

How to keep the reference to an array after manipulation?

How do I keep the reference to an array after an items is appended?
Updated code example, because the prior example didn't seem to be clear enough.
class ViewController: UIViewController {
var numbers = Numbers.singleton.numbers
override func viewDidLoad() {
print(numbers.count)
Numbers.singleton.add(1)
print(numbers.count) // prints 0
print(Numbers.singleton.numbers.count) // prints 1
}
}
class Numbers {
static let singleton = Numbers()
var numbers: [Int]!
private init() {
numbers = []
}
func add(number: Int) {
numbers.append(number)
}
}
Arrays in Swift don't have "references". They are structs, and a struct is a value type. Your (badly named) arrayRef is a separate copy, not a reference to self.array.
Moreover, there is no good reason to want to do what you (seem to) want to do. To have two simultaneous references to a mutable array would be unsafe, since the array can be changed behind your back. The Swift design is sensible; use it, don't subvert it.

Swift - filter Realm objects via UISearchBar in a UICollectionView

This is my Realm object, basically an image with some tag attached.
class AllTags: Object {
dynamic var singleTag = ""}
class Photo: Object {
var myTags: [String] {
get {
return _backingNewTags.map { $0.singleTag }
}
set {
_backingNewTags.removeAll()
_backingNewTags.appendContentsOf(newValue.map({ AllTags(value: [$0]) }))
}
}
let _backingNewTags = List<AllTags>()
override static func ignoredProperties() -> [String] {
return ["myTags"]
}
dynamic var imagePath = ""}
I have my collectionView, I can see all my photo and when pressing an image I can see my tags, so everything is working correctly.
I have added my UISearchBar, added the txtSearchbar.delegate = self and using let data = realm.objects(AllTags).map { $0.singleTag } I can print ALL the tags inside my database.
I just need to filter in real time while I type the CollectionView cells via the UISearchBar so it shows only the images tagged with the word I'm typing. Basic.
I've been following this tutorial to filter in the collectionView - https://github.com/codepath/ios_guides/wiki/Search-Bar-Guide#example-searching-a-collection-view - After 11 hours, I can't figure out how to make it works with Realm. With hardcoded Array like the example I can make it works.
In Realm, you can filter a Results<T> based on what you're looking for. For example:
let data = realm.objects(AllTags).filter("singleTag CONTAINS %#", searchTerm)
I'm wondering, however, why you're converting your _backingNewTags to an Array [String]? Why can't you just access the tags directly? This will be much more memory & CPU efficient, and will simplify your code...

CoreData: Move objects from one ArrayController to another

I have two NSArrayControllers that use Core Data entities and I want to move objects at certain indices (via NSIndexSet) from one AC to the other. Normally this would be easy but here it's not because Core Data is under it and if I try it, it seems to remove the objects from the source AC but not adding them to the target AC. I'm using this code:
extension NSIndexSet
{
func toArray() -> [Int]
{
var indexes:[Int] = [];
self.enumerateIndexesUsingBlock
{
(index:Int, _) in
indexes.append(index);
}
return indexes;
}
}
func moveIndicesToTargetAC(indexSet:NSIndexSet)
{
let a = indexSet.toArray();
for i in a
{
var obj:NSManagedObject = sourceArrayController?.arrangedObjects.objectAtIndex(i) as! NSManagedObject;
targetArrayController?.addObject(obj);
sourceArrayController?.removeObjectAtArrangedObjectIndex(i);
}
}
What do I need to do to have Core Data regard the deletions and additions?
Thanks for hints anyone! I solved the issue by creating a new object of the target (NSManagedObject) entity and transfer all the data over to it. It was indeed an issue because the core data entities of the two ACs is different.

Resources