Loading data and singleton for IOS - ios

I am making an instagram clone but I am having abit of question about what would be the best way to pass data arround.
I have 3 post arrays and three VCs as shown below
myPosts = [Post]()
nearbyPosts = [Post]()
followingPosts = [Post]()
myPostVC
nearbyPostVC
followingPostVC
The interesting part is when I download a post that is close by and it happens to be one of my post that I am also following. So when I download that post, I will need to put it in all three arrays so they are the same reference. This is so that if the user "likes" that post, it will be reflected in all three array because they point to the same post. (If they dont have the same reference, and the user likes or perform any sort of action on a post in myPosts array, I will have to manually loop through each nearbyPosts and followingPosts to update their "like count" individually)
Initially, my code looks this
myPost -> Initiated inside myPostVC
nearbyPost -> Initiated inside nearByVC
followingPostVC -> Initiated inside followingPostVC
So in myPostVC, I would need to create an instance of nearbyVC and followingPostVC so that everytime I download a post inside myPostVC, I loop through nearbyVC.nearbyPosts and followingPostVC.followingPosts and update anyting that has the same poast as the one that was just downloded. And I do this for all three VC. Imagine if I have moreVC, this gets very messy as each VC is linked to other VC (In my case, the threeVC as three tabs)
This is when I changed to Singleton... I made myPosts, nearbyPosts and followingPosts to be all inside a singleton as shown below. This allows the three VC to be disconnected and they just need to access whichever array they wish to by calling something like PostService.ps.myPosts (Without the need to create a refrence to other VC)
My question is...
I know it "might" be good to use the first method because it is not singleton, but is that actually a method people use in this case? (It makes all VC linked together. Very messy)
Is singleton the right way to solve this problem or is there a much better way? (Note that similar to instagram, I dont really need to store these posts on the phone to look at in future, so no coredata?)
class PostService {
static let ps = PostService()
private var _myPosts = [Post]()
private var _nearbyPosts = [Post]()
private var _followingPosts = [Post]()
var myPosts: [Post] {
return _myPosts
}
var nearbyPosts: [Post] {
return _nearbyPosts
}
var followingPosts: [Post] {
return _followingPosts
}

So many ways to approach this.
Create a new class (such as your PostService) to represent your data model, outside of any view controller. This data model class could be a singleton. I would put all of the posts into a single Set (not an array) named allPosts. Using a Set rather than an array will prevent any duplicates. Each Post object could have boolean vars indicating if it belongs to specific subsets or not. This could be stored or calculated depending on your data.
Then to access a specific subset, use the filter function on the set, such as:
var allPosts: Set<Post> = ...
var myPosts: [Post] {
return allPosts.filter { $0.isMine }
}
var nearbyPosts: [Post] {
return allPosts.filter { $0.isNearby }
}
The advantage here is no duplication of objects and a shared resource for each of your view controllers.
Note: Since this is a Set rather than an Array, it will not have any order to the items it contains. But you can always use the sort() function to do that.

Related

Best way to store and refrence lots data in Swift for use in a UITableView

This is a bit confusing so I apologise. But:
I am trying to make an app where the user has the ability to add items to a list of individual items that will be displayed back to them(Something along the lines of a todo list app) as a table view.
However, I have hit a roadblock I need to store several different bits of data for each item(Some Strings, Some ints and a date) in the list.
I think that a class(or struct) would be the best way to do this where an instance of the class holds the information need for each item and then the name of that instance is stored in a list so it can be accessed via the indexPath in the table view.
However, I don't know how I am going to make a new instance of the class for every item because the app could have hundreds of individual items.
I'm sorry, this is so confusing and any help would be appreciated! Feel free to ask for more info
Edit: what I am looking for and I'm sure there's a stupidly easy way of doing it but I'm try to work out how to create an instance of a class when the name of the class is stored in a variable. Ecencialy I want the instance of the class to store the item. To be created when the user inputs the item to be added to the table.
Eg. They enter an item. item1 and the other data that goes along with then I want to be able to store that in instance of the item class but I don't know how to make the name of that instance because the name I want which is item 1 is stored in a variable.
Sorry that's so confusing that's the reason I need help
So first: You can't store the Name of a Class in a Variable and use this variable to get a new instance of a class.
What you need is an Array containing all the different Items. This array is unlimited so you can store as many items in it as you like and you don't need to set a name for each of these Instances.
First create an empty array containing all Items as a property:
var items : [Item] = []
Second call the function populateItemArray() in your viewDidLoad():
private func populateItemArray() {
// Populate the items Array (Are you using CoreData, Realm, ...?)
}
Third, use the Item Array to populate the TableView.
REMEMBER: If your only using one section in your tableView, indexPath.row is always equal to the corresponding item in the array.
E.G. items[indexPath.row]
Hope this helps you!
UPDATE:
Look at this example struct Item. As you can see you can also store a date in it:
struct Item {
// First create all the properties for your struct.
var data1: String
var data2: Int
var data3: String
// You can create properties of any type you want, also of type date.
var dateOfCreation : Date
// Then implement all the methods of your custom Struct Item.
func setDate(with date : Date) {
self.dateOfCreation = date
}
func returnDate() -> Date {
return self.dateOfCreation
}
}

Which Data Model is the best for storing instagram like data?

I have an app similar to Instagram, in it I have to fetch posts and users to go with them. Now I have come to a point where I need to decide between two DM for how to store posts, below are the two options:
option 1: each post has a saves teh user that made it but teh user object is only a user object meaning it might be inefficient as I would have to keep saving teh same data into a User object
class Post: NSObject {
var images = Image()
let user = User()
var snapshot : [String : AnyObject]?
}
option 2: Here the post object would not keep track of teh user as teh user object keeps track of each post
class User: NSObject {
var posts = [Post]()
//Other data...
}
So my question is, which DM is best for my use case (that is to be able to fetch and display posts in a format similar to Instagram)
you should use
class Post {
var images: [String]
let userId: String
var snapshot : [String : AnyObject]?
}
class User {
let id: String
}
when you fetch posts, you can do something like this. repo.findPostsByUserId(userId, page, size)
# Galo Torres Sevilla comment is the best answer here so I will add it as teh correct answer:
First option is better. Each post should always keep a reference to
who posted it. Users on the other hand don’t need to have a reference
to each of their posts. Imagine a situation where you only want to
display the user’s name and profile pic, why would you fetch all the
posts in that case? Also, the model in your case should not contain
variables that point to reference types like images, it’s better to
keep them in value types such a String that points to the image url
and download the image as needed.
– Galo Torres Sevilla

Value type design pattern to replace class

We are a looking for a value type design pattern in swift that will allow us to create a shopping cart to hold Products. We are currently using a class but that is a reference type and when we try to add two different version of the same product (i.e. with a different colors or sizes), the first item we added gets changed to the second item we added because it points to the same object in memory.
The design pattern needs to be “global” so we can access it from any page in the app. Right now this is our Cart class that stores all the items in the cart. What do we need to do to make this a value type or how does it need to be reengineered to use a struct without a class?
class Cart : NSObject {
var allProductsInCart = [MainProduct]()
override init() {
super.init()
}
class var sharedCart: Cart {
struct Static {
static let instance = Cart()
}
return Static.instance
}
}
The problem we are getting is that we need the products in the cart to be of custom class “MainProduct.” Right now as you can see, they are stored as “MainProduct.” Do we need to switch the products to a struct or other design pattern as well? How would we do that?
Yes, given the desired behavior between a value type vs. reference type you should use a Struct.
A commonly used "pattern" for doing this is called "Redux".
The idea is that you have one, immutable version of the "state" of your app.
It can be accessed from anywhere and the only way to update it is through "actions". These will reconstruct the entire state with the required updates.
ViewControllers and views, etc... subscribe to updates of various parts of the state.
So you could have an AppState that contains a ShoppingCartState. When a product is added to it your CartViewController will be informed of this update and can update its view etc...
There are many different frameworks that are built to use Redux so I won't recommend one as you should find the one that is right for you. But I believe this pattern best suits the usage you are after.

How to pass json data in swift app between two viewcontrollers

I need help with passing json data.I have json data in this array
var TableData:Array< String > = Array < String >()
In this Array I have Name,Address,Latitude, Longitude
I show Name and Address in a tableView , but I would like to create annotation in different viewController with Latitude and Longitude depending on which cell user taps(Name,Adress,latitude,Longitude shoulld be equal) , so I am asking you if there is some good tutorial in swift , or if you have just some advice.
Thank you.
There are many different ways to pass data from one swift file to another. In the case that there is a rootViewController and a button is clicked to open a new ViewController. Having previously defined an array in the new ViewController, the json parsed array can be passed along using the prepareForSegue method.
Another popular way to pass information between different swift files would be using the AppDelegate. Here you can create instances of different swift classes using a method known as instantiateViewControllerWithIdentifier("identifier"). This can be done by creating a storyboard variable then calling this method by doing storyboard.instantiateViewControllerWithIdentifier("identifier").
let newvc = self.storyboard?.instantiateViewControllerWithIdentifier("newvcIdentifier") as! (UIViewController extended class)
newvc.data = TableData
self.navigationController?.pushViewController(newController, animated: true)
where newvc has a variable declared as follows:
var data: Array <String>!
Another method that can be used is having shared data among all of the classes using a singleton. A singleton can be created very simply in swift3, take a look here for details.
class JSONData {
var json: Array <String>
static let sharedInstance = JSONData()
}
Before segueing to the next vc, you should store the data in the sharedInstance class. This should be done by overriding the prepare method. Documentation on this method can be found here
sharedInstance.json = self.json
Once the data is set in the shared instance, after the new view controller is loaded, this data can be accessed through sharedInstance.json.
Hope this works for you and just comment if you have any other questions!
I would do something like this:
let newController = self.storyboard?.instantiateViewControllerWithIdentifier("newControllerIdentifier") as! NewControllerClassName
newController.data = array
self.navigationController?.pushViewController(newController, animated: true)
It also appears you are using a array of string type that are comma separated.
I would rather create a variable like below
var jsonArray:[[String:String]]?
so I am asking you if there is some good tutorial in swift
http://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/
He covers clean code and best practices. Better read this before implementing anything.
or if you have just some advice
Your code needs to be clean. Passing latitude as String it's going to create you a problem later (conversions, code understanding, or even bugs).
Make a new account here and watch uncle Bob. His lessons are valuable. matteomanferdini above follow uncle bob practices.
https://www.safaribooksonline.com/library/view/clean-code/9780134661742/

Class or Struct for a model similar to a relational database?

The application in question is built as follows:
A user selects a job
the job can have many components
each component can have many lineitems.
I am not clear on how this should be structured - should this be class or structs? Seeing that only one job is being processed at a time, I am fairly confident that jobs should be a class. However, when there are multiples of a certain object type, I am not exactly clear on how to form them, like the components and lineitem objects.
The application consists of ViewControllers and TableViewControllers. All the data is fetched from a server in JSON and populated into the appropriate view as needed. Here are the object types as they are currently setup:
A job object:
// Job Object
//
public struct Job {
static var globalId : String?
static var name : String?
static var status : String?
static var client = Client()
static var components = Array<Component>()
// etc..
}
A Component like so:
// JobComponent Object
//
public struct Component {
var name:String? = ""
var fmRecordId : String?
var startTS:NSDate?
var endTS:NSDate?
var notes:String? = ""
var items = Array<Lineitem>()
// etc...
}
and finally, a lineitem:
// Lineitem Object
//
public struct Lineitem {
var fmRecordId = String()
var itemName = String()
var itemNumber = String()
// etc...
}
All of these object are built within a public class called "PL".
When a user selects lineitem and edits it's values, the values are not available outside the VC in which they are edited because the VC isn't referencing the lineitem that is was passed, it is simply copying it. The same happens with components.
A workaround I found was to use the the Job struct PL.Job.self and always modify the components and lineitems like so where i = a desired index in the array:
PL.Job.components[i] to access a component
PL.Job.components[i].items[i] to access a specific item within that component.
However, this doesn't scale very well.
The desired behavior is to be able to pass a reference to a particular instance of an object around rather than pass around the index path of those objects in the PL.Job object.
I am well aware there is something wrong with how this is currently structured, but could someone please point me in the right direction?
A couple of points:
You can only pass class instances by reference. If you want to be able to pass a reference to a particular LineItem or Component or Job, and you want to be able to make changes to that object that are effective everywhere, then you need to define them as classes and not structs. Instances of struct types are always passed by value and not be reference. And when a value type is passed, it is copied, meaning that you create an entirely new copy of the object, and mutating the copy has no effect on the original.
Your Job struct only has static properties - i.e., there will only ever be one globalId, name, status etc. throughout your entire application. If you want to have multiple instances of Job, then these should not be static properties. You say that only one Job will be processed at a time, so maybe that was intentional. Either way, it is still often preferable to create an instance of a Job class that has those properties. It certainly would give you more flexibility later if you decide to make it possible to hold references to multiple jobs in memory, or to allow the user to select between different jobs, or switch between jobs, etc. For example, you may want to allow a user to switch to the Job they were processing earlier without necessarily destroying the Job that they are working on now.
But I think the main point is that you will need to define your objects as classes if you want to be able to pass them by reference. If you modify an object that is passed by reference, all other references to the same object will show the same changes (because, after all, they are just references to the same object). That doesn't work with value types, like structs.

Resources