0

I'm trying to get some data from the server and use it globally in the app..

I mean for example, I'm using following code to get data from service:

struct Service : Decodable{
    let id: Int
    let name, description: String
    let createdAt: String?
    let updatedAt: String?
}

func makeGetCall() {
    let todoEndpoint: String = "http://web.src01.view.beta.is.sa/public/api/services"
    guard let url = URL(string: todoEndpoint) else {
        print("Error: cannot create URL")
        return
    }
    let urlRequest = URLRequest(url: url)

    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)

    let task = session.dataTask(with: urlRequest) {
        (data, response, error) in
        guard error == nil else {
            print("error calling GET on /public/api/services")
            print(error!)
            return
        }
        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }
        do {

            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            let todos = try decoder.decode([Service].self, from: responseData)
            for todo in todos{
                print(todo.name)
            }

        } catch  {
            print("error trying to convert data to JSON")
            return
        }
    }
    task.resume()
}

This code is located and called in HomeViewController and i'm getting data which i want.

But i want to access and use this data in another viewcontroller and in whole app...

How i can do it? How can i make the received data from the function is saved globally and how to use it in another viewcontroller?

Can someone tell me how i can do this?

aBilal17
  • 2,974
  • 2
  • 17
  • 23
Lama
  • 255
  • 3
  • 17
  • you can use [userdefaults](https://developer.apple.com/documentation/foundation/userdefaults) to store data in App. – Rocky Apr 18 '18 at 06:29
  • Save this data in CoreData or sqlite and in UserDefaults as well. What type of data is it? – aBilal17 Apr 18 '18 at 06:30
  • @aBilal17 it's json data converted int and strings.. but i got an array object .. how to save it in userdefaults? – Lama Apr 18 '18 at 06:33
  • @Rocky its an array of object .. does it work? – Lama Apr 18 '18 at 06:33
  • Ok,, you can store it in NSUserDefaults like this code. let arrayFruit = ["Apple","Banana","Orange","Grapes","Watermelon"] //store in user default UserDefaults.standard.set(arrayFruit, forKey: "arrayFruit") and can get back array like this, if let arr = UserDefaults.standard.array(forKey: "arrayFruit") as? [String]{ print(arr) } – aBilal17 Apr 18 '18 at 06:36
  • Here is the reference https://stackoverflow.com/questions/25179668/how-to-save-and-read-array-of-array-in-nsuserdefaults-in-swift – aBilal17 Apr 18 '18 at 06:37
  • @aBilal17 but i have a custom object called service .. and want to store an array of that object .. not just an array of string – Lama Apr 18 '18 at 06:38
  • @Lama you can save array of custom objects into userdefaults, for this your object must have to implement `NSCoding' protocol. this will help you: https://stackoverflow.com/questions/29986957/save-custom-objects-into-nsuserdefaults – Rocky Apr 18 '18 at 06:40
  • @Lama use can store this is just example, In NsUserDefaults you can store what you want. – aBilal17 Apr 18 '18 at 06:40
  • There is an alternative as well, just create a variable in AppDelegate and assign your data to it, and in other controller just create an object of App delegate and get the data back from that variable. – aBilal17 Apr 18 '18 at 06:42
  • @aBilal17 thank you so much for the help .. tried usersdefaults and worked as i need .. thank you! :) – Lama Apr 18 '18 at 07:17

1 Answers1

1

For such cases we usually use static data. They may be served as singleton or just a static property. In your case a static property for cached data may be nice. We can put static properties in extension so adding following may be nice:

// MARK: - Fetching Data
extension Service {

    private static var cachedServices: [Service]?

    static func fetchServices(_ completion: (_ services: [Service]) -> Void?) {
        if let cachedServices = cachedServices {
            completion(cachedServices)
        } else {
            makeGetCall { services in
                let newServices = services ?? []
                self.cachedServices = newServices
                completion(newServices)
            }
        }
    }

}

Now the usage from everywhere is calling

Service.fetchServices { services in
}

and this call may be asynchronous or not, depending if data is already loaded.

If you need to access them synchronous and you are sure data is already loaded then simply add another method in extension:

static func getCachedData() -> [Service] {
    return cachedServices ?? []
}

This method will return instantly but array will be empty if no data was received yet. But anywhere you can call Service.getCachedData()

This cache is now only preserved until your app terminates. If you want to preserve them longer then all you need to do is add the logic to save and load data into file or user defaults. The logic for that would be something like:

private static var cachedServices: [Service]? {
    didSet {
        self.saveServicesToFile(cachedServices)
    }
}

static func fetchServices(_ completion: (_ services: [Service]) -> Void?) 
{
    if let cachedServices = cachedServices {
        completion(cachedServices)
    } else if let saved = self.loadFromFile() {
        self.cachedServices = saved
        completion(saved)
    }else {
        makeGetCall { services in
            let newServices = services ?? []
            self.cachedServices = newServices
            completion(newServices)
        }
    }
}
Matic Oblak
  • 16,318
  • 3
  • 24
  • 43