Why does my array of NSManagedObjects is losing its properties? - ios

In my UIViewController I call a method in another class which returns me a list of NSManagedObjects, which I instantiate in an array.
Here is the code:
fileprivate var albumList = [Album]()
private func loadAlbums() {
APIHandler().getUsersAlbums() {
albums in
self.albumList = albums
self.collectionView?.reloadData()
}
}
But this was causing my array to have nil properties once loadAlbums was finished and APIHandler's instance cleared. I solved this for now by having an instance of APIHandler on my UIViewController and calling the function from there, like this:
let api = SpotifyAPIHandler()
fileprivate var albumList = [Album]()
private func loadAlbums() {
api.getUsersAlbums() {
albums in
self.albumList = albums
self.collectionView?.reloadData()
}
}
Still I am not happy with this. Why does this happen?
How can I instantiate a completely new list?

I think what's happening is you're creating a new instance of APIHandler every time you're calling the loadAlbums() func instead what you can do is have a static refrence of ur APIHander in the APIHander class like so
class APIHander: NSObject {
static let handler = APIHander()
}
Then you would call your API like this
private func loadAlbums() {
APIHandler.handler.getUsersAlbums() {
albums in
self.albumList = albums
self.collectionView?.reloadData()
}
}

Related

Dealing with Firebase observers when userID changes?

I use this function inside an helper class to fetch the trips of a user. It keeps listening for new changes:
import Foundation
import Firebase
class APICardService: NSObject {
class func fetchTrips(forID userID: String, completion: #escaping ([Trip]) -> Void) {
let path = "users/\(userID)/trips"
Database.database().reference().child(path).queryOrdered(byChild: "timestamp").observe(.value) { (snapshot) in
var trips = [Trip]()
for child in snapshot.children {
if let child = child as? DataSnapshot {
if let dict = child.value as? [String: Any] {
let trip = Trip(key: child.key, dbValues: dict)
trips.append(trip)
}
}
}
completion(trips)
}
}
}
class MyViewController: UIViewController {
// but what if the userID changes? I have no way to stop the current observer and start a new one
internal func fetchCards() {
guard let userID = APIAuthService.getUserID() else { return }
APICardService.fetchTrips(forID: userID) { trips in
self.trips = trips.reversed() // show most recent first
self.addAnnotations(for: trips)
self.collectionView.reloadData()
}
}
}
The problem is that whenever the user signs out and logs into a new account this observer will still be listening for new value changes in the data but it won't work because the userID changed. How do I deal with this? I could remove the observer somehow but this is complicated because we're inside a separate class and I wouldn't know how to handle that.
In your class APICardService create these global variables
static var refHandle:DatabaseHandle? = nil
static var ref:DatabaseReference? = nil
in your Method func fetchTrips(forID userID: String, completion: #escapin
assign global variables like this
let path = "users/\(userID)/trips"
ref = Database.database().reference().child(path)
refHandle = ref?.queryOrdered(byChild: "timestamp").observe(.value) { (snapshot) in
Now you have the reference of your observer
just add following method in your class
class func removeObserverForFetchTrip() {
if let refHandleValue = refHandle {
ref?.removeObserver(withHandle: refHandleValue)
}
}
When user logout just call this method
Hope it is helpful
As #PrashantTukadiya says.
Try making something like this:
struct User {
var id: String
...
}
class UserHolder {
static var shared: UserHolder = UserHolder() // Singleton
var currentUser: User? {
willSet {
// Remove all the registered firebase handlers associated with the user
firebaseHandles.forEach({ $0.0.removeObserver(withHandle: $0.1) })
}
}
var firebaseHandles: [(DatabaseReference, UInt)] = []
// Note: Try to add all the firebase handles you register to "firebaseHandles"
// array. The more adequate solution is to make this class register
// the listeners, not all other classes.
}

How to Get data from another class in swift3

I have one swift file and in which I have kept my common function that I need every time so when I am accessing that function I am not getting value
Myfirst class
import Foundation
import UIKit
class test1
{
var mydata = NSMutableArray()
//MY COMMON FUNCTION
func loadMoredata(create_at:String)->NSMutableArray
{
**//////My CODE**
//getting correct data
print(mydata)
return mydata
}
}
Mysecond Class
override func viewWillAppear(_ animated: Bool)
{
//Calling that function
// not getting data here or empty array
let dt:NSMutableArray = test1().loadMoredata(create_at: "0")
// not getting data here or empty array
print(test1().mydata)
}
You dont get anything back from your function since your mydata property is an instance property. Hence, every test1 object you create, will have its own mydata property with its own data.
If you want store global state you could make test1 a singleton class, where mydata is globally accessible.
class test1
{
static let shared = test1()
var mydata = NSMutableArray()
private init(){}
//MY COMMON FUNCTION
func loadMoredata(create_at:String)->NSMutableArray
{
**//////My CODE**
//getting correct data
print(mydata)
return mydata
}
}

Realm-iOS: Model object becomes nil when it's added to realm

It's an RSS reader app. I instantiate my model object, call setupFavIcon() on it to download favIcon which is nil when creating an object. Then I add each object to realm. But when the icon is actually fetched, self is nil, so I can't update app's UI. I don't understand why self becomes nil.
class Article: Object {
dynamic var source = ""
dynamic var title = ""
dynamic var link = ""
dynamic var pubDate = Date()
dynamic var favIcon: Data?
dynamic var favIconDidLoad: (() -> ())?
func setupFavIcon(_ source: String) {
DownloadManager.sharedInstance.downloadFavIcon(source) { [weak self] icon in
if let icon = icon {
self?.favIcon = icon
self?.favIconDidLoad?()
}
}
}
override class func primaryKey() -> String? {
return "link"
}
override class func ignoredProperties() -> [String] {
return ["favIconDidLoad"]
}
}
Closure favIconDidLoad is defined in my TableViewCell class and invoked when favIcon is downloaded.
fileprivate func setupFavIcon(_ article: Article) {
if let favicon = article.favIcon {
setFavIcon(favicon)
} else {
article.favIconDidLoad = { [weak self] in
self?.setFavIcon(article.favIcon)
}
}
}
You should keep strong reference to your Article objects. When you load them from realm you need keep them in array. if you operate on results from realm they are released when method TableViewCell setupFavIcon ends.
You use weak reference to self in downloadFavIcon closure that most likely is called asynchronously, so your objects could be already deallocated. You need to use strong reference in closure or keep strong references to your objects somewhere.
Also note: if object is added to Realm, all changes to an object (addition, modification and deletion) must be done within a write transaction.

Pass array to singleton (swift)

class MySingleton{
static let shareInstance = MySingleton()
private init() {}
var myDetail = [Detail]()
}
class DetailTableViewController {
var expense = [Detail]()
override func viewDidLoad() {
super.viewDidLoad()
... put stuff in expense array ....
MySingleton.shareInstance.myDetail = expense //<--- doesn't work
// error is "cannot assign value of type '[Detail]' to type [MySingleton.Detail]"
}
}
How do I copy an array to my MySingleton?
right now i just pass my array around my classes using segue
From your error, it is likely you are defining Detail twice, once locally to the singleton, once globally for the viewController.

Making a Swift Array conform to a Protocol

Let's say that certain items can appear in a Feed, so long as they implement the necessary properties defined by the Feedable protocol. Let's also say that the Photo object is feed-worthy:
extension Photo: Feedable { }
Is it possible to say that an Array of these photos might also be Feedable?
extension [Photo] : Feedable
Or do I always need some kind of wrapper object, such as a PhotoAlbum, to conform to Feedable?
Edit
To re-iterate, I was curious whether I can make only arrays of Photo objects Feedable. Not making Array of any content type Feedable, not making an array of Feedables itself Feedable (both of which are offered as solutions below if that's what you need).
In other words, a solution (which I doubt exists) would allow me to define a variable of type Feedable with the following outcomes:
var feedable: Feedable
//photo is feedable, so this is fine
feedable = Photo() //ok
//arrays of photos are feedable
let photo1 = Photo()
let photo2 = Photo()
feedable = [photo1, photo2]
//arrays of other things are not
feedable = ["no", "dice"] //nope
//even if the contents of an array are themselves Feedable, that's not sufficient. E.g. Video is Feedable, but Array of Videos is not.
let video1 = Video()
let video2 = Video()
feeble = video1 //fine
feedable = [video1, video2] //nope
Perhaps this gist (which doesn't compile of course) shows the intention more clearly.
You can achieve your goal in this way:
Swift 4:
protocol Feedable {
func foo()
}
extension String: Feedable {
func foo() {
}
}
extension Array: Feedable where Element: Feedable {
func foo() {
}
}
// or in more generic way to support Array, Set and other collection types
extension Collection: Feedable where Element: Feedable {
func foo() {
}
}
If there was an array of Photo and Video,what would you like to be?
1.Every element performs like what they are.
extension Array where Element : Feedable {
func foo() {
if Element.self == Photo.self {
} else {
}
}
}
2.The whole array performs as 'Video'.
extension Array where Element : Photo {
func foo() {
}
}
I think this is currently not possible. In my project I have the same issue with a ModelProducer.
protocol M: ModelType {}
protocol ModelProducerType {
associatedtype M: ModelType
var model: M? { get }
func produce()
}
struct Test: ModelType {}
class TestProducer: ModelProducerType {
var model: Test?
func produce() {
model = Test()
}
}
I use ModelType as a ghost protocol. The problem is I cannot make a model producer that produces multiple ModelTypes, because of the same reason you discovered. The solution in this case was the following:
protocol M: ModelType {}
protocol ModelProducerType {
associatedtype M: ModelType
var model: [M] { get }
func produce()
}
struct Test: ModelType {}
class TestProducer: ModelProducerType {
var model: [Test] = []
func produce() {
model = [Test()]
}
}
This is more flexible from the start. I get rid of the optional variable and single model producers just have one item in the array. Maybe you can use a similar approach.
You can make an array to conform a protocol like this:
typealias PhotoArray = [Photo]
extension PhotoArray: Feedable {}
I didn't try in playground but maybe you can simply make an Array of Feedable:
var myPhotosArray = [Feedable]()
Then everything implementing the Feedable protocol would be allowed in the Array. If you want only a photo array, You can still subclass your Photo object to make a FeedablePhoto object.
Try this in Playground instead of downvoting without even testing.
Seriously 3 downvotes without any reasons and explanations...
import UIKit
protocol Tree: class {
func grow()
}
class BigTree: Tree {
internal func grow() {
print("Big tree growing")
}
}
class SmallTree: Tree {
internal func grow() {
print("Small tree growing")
}
}
class Car {
//not a tree
}
var manyTrees = [Tree]()
manyTrees.append(BigTree())
manyTrees.append(SmallTree())
manyTrees.append(Car()) //This makes an error "Car doesn't conform to expected type 'Tree'"

Resources