Синхронизация удаленных данных JSON с локальным хранилищем кеша в iOS Swift

Я пытаюсь найти решение для простой обработки всех необходимых шагов для использования удаленных данных JSON только для чтения на устройствах iOS. Это означает извлечение удаленных данных JSON, сохранение в локальном кеше на устройстве iOS для автономного использования, обновление кеша, анализ данных JSON. Я думаю, что это очень распространенное требование для всех мобильных приложений в настоящее время.

Я знаю, что можно вручную загрузить удаленный файл JSON, сохранить его в локальной базе данных или файле на устройстве iOS, а когда сеть недоступна, извлечь его из локального хранилища, в противном случае загрузить его из сети. сейчас делаю вручную. :) Но много шагов, которые, надеюсь, можно сделать с помощью фреймворков/библиотек, не так ли?

Итак, я попробовал фреймворк HanekeSwift, который делает почти все, что мне нужно, но только кэширует удаленный JSON (и изображения), но не обновляет кеш! что мне не полезно. Я также знаю, что существуют Alamofire и SwiftyJSON, но у меня нет с ними опыта.

У вас есть опыт, как это сделать?

Сводка

  • библиотеки или фреймворки для поддержки iOS8 в Swift
  • загрузить удаленный JSON и сохранить в локальном кеше
  • возможность обновить локальный кеш из его источника
  • приятный бонус - простой разбор JSON

введите здесь описание изображения


person kolisko    schedule 09.02.2015    source источник


Ответы (3)


Отличный вопрос!

Вы можете добиться этого с помощью комбинации Alamofire и SwiftyJSON. Я бы порекомендовал комбинацию нескольких вещей, чтобы сделать это как можно проще.

Я думаю, у вас есть два подхода к получению JSON.

  1. Получить данные JSON в памяти и использовать политику кэширования
  2. Загрузите данные JSON на диск прямо в локальный кеш.

Вариант 1

// Create a shared URL cache
let memoryCapacity = 500 * 1024 * 1024; // 500 MB
let diskCapacity = 500 * 1024 * 1024; // 500 MB
let cache = NSURLCache(memoryCapacity: memoryCapacity, diskCapacity: diskCapacity, diskPath: "shared_cache")

// Create a custom configuration
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders
configuration.HTTPAdditionalHeaders = defaultHeaders
configuration.requestCachePolicy = .UseProtocolCachePolicy // this is the default
configuration.URLCache = cache

// Create your own manager instance that uses your custom configuration
let manager = Alamofire.Manager(configuration: configuration)

// Make your request with your custom manager that is caching your requests by default
manager.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"], encoding: .URL)
       .response { (request, response, data, error) in
           println(request)
           println(response)
           println(error)

           // Now parse the data using SwiftyJSON
           // This will come from your custom cache if it is not expired,
           // and from the network if it is expired
       }

Вариант 2

let URL = NSURL(string: "/whereever/your/local/cache/lives")!

let downloadRequest = Alamofire.download(.GET, "http://httpbin.org/get") { (_, _) -> NSURL in
    return URL
}

downloadRequest.response { request, response, _, error in
    // Read data into memory from local cache file now in URL
}

Вариант 1, безусловно, использует наибольший объем поддерживаемого Apple кэширования. Я думаю, что с тем, что вы пытаетесь сделать, вы должны иметь возможность использовать NSURLSessionConfiguration и конкретный политика кэширования, чтобы выполнить то, что вы хотите сделать.

Вариант 2 потребует гораздо большего объема работы и будет немного странной системой, если вы используете политику кэширования, которая фактически кэширует данные на диске. Загрузки заканчиваются копированием уже кэшированных данных. Вот как выглядел бы поток, если бы ваш запрос существовал в вашем пользовательском кэше URL-адресов.

  1. Сделать запрос на скачивание
  2. Запрос кэшируется, поэтому кэшированные данные загружаются в NSInputStream
  3. Данные записываются в предоставленный URL через NSOutputStream
  4. Сериализатор ответа вызывается, когда вы загружаете данные обратно в память.
  5. Затем данные анализируются с помощью SwiftyJSON в объекты модели.

Это довольно расточительно, если только вы не загружаете очень большие файлы. Вы потенциально можете столкнуться с проблемами памяти, если загрузите все данные запроса в память.

Копирование кэшированных данных в предоставленный URL, скорее всего, будет реализовано через NSInputStream и NSOutputStream. Все это обрабатывается Apple внутри платформы Foundation. Это должен быть очень эффективный способ перемещения данных. Недостатком является то, что вам нужно скопировать весь набор данных, прежде чем вы сможете получить к нему доступ.

NSURLCache

Еще одна вещь, которая может быть очень полезна для вас, — это возможность получить кешированный ответ непосредственно из вашего NSURLCache. Взгляните на метод cachedReponseForRequest:, который можно найти здесь.

SwiftyJSON

Последний шаг — преобразование данных JSON в объекты модели. SwiftyJSON делает это очень просто. Если вы используете Вариант 1 из приведенного выше, вы можете использовать пользовательский сериализатор ответов в Alamofire-SwiftyJSON. . Это будет выглядеть примерно так:

Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
         .responseSwiftyJSON { (request, response, json, error) in
             println(json)
             println(error)
         }

Теперь, если вы использовали Вариант 2, вам нужно будет загрузить данные с диска, затем инициализировать объект SwiftyJSON и начать синтаксический анализ, который будет выглядеть примерно так:

let data = NSData(contentsOfFile: URL.path)!
let json = JSON(data: data)

Это должно охватывать все инструменты, которые вам понадобятся для выполнения того, что вы пытаетесь сделать. То, как вы спроектируете точное решение, безусловно, зависит от вас, поскольку существует очень много возможных способов сделать это.

person cnoon    schedule 10.02.2015
comment
Спасибо за ответ! Единственным возможным решением для меня является вариант 2 для хранения данных на локальном диске. Что именно делает Alamofire, когда 1. сетевое подключение недоступно и 2. когда есть? ad1 — загрузить данные из кеша, если они доступны, ad2 — обновить данные в кеше? Может глупый вопрос, но правильно ли? - person kolisko; 11.02.2015
comment
Если сетевое подключение недоступно, вы получите сетевую ошибку в файле response. Конечно, вы можете добавить логику, чтобы пытаться загружать данные только при наличии подключения к Интернету. Что касается других ваших вопросов, я внес несколько больших обновлений в ответ выше, касающийся NSURLCache и разных NSURLRequestCachePolicy. Надеюсь, это даст вам лучшее представление о вариантах использования уже существующей системы кэширования Apple. - person cnoon; 12.02.2015
comment
В моем приложении я бы использовал вариант 1 и использовал NSURLCache. Я хотел бы использовать кешированный ответ, когда у меня нет доступного соединения. Существует ли способ использовать кеш напрямую, без запроса, в случае отсутствия соединения? Спасибо - person Giovanni Grano; 04.05.2016
comment
Что произойдет, если я использую обе вещи одновременно? - person Sivajee Battina; 01.10.2018
comment
@cnoon Не могли бы вы объяснить мне, сколько времени истечения срока действия денежных данных? и он будет автоматически обновляться при сервисном звонке? - person Nikunj Kumbhani; 05.10.2018

Ниже приведен код, который я использовал для кэширования своих запросов с помощью Alamofire и SwiftyJSON — надеюсь, он кому-нибудь поможет.

func getPlaces(){
    //Request with caching policy
    let request = NSMutableURLRequest(URL: NSURL(string: baseUrl + "/places")!, cachePolicy: .ReturnCacheDataElseLoad, timeoutInterval: 20)
    Alamofire.request(request)
        .responseJSON { (response) in
            let cachedURLResponse = NSCachedURLResponse(response: response.response!, data: (response.data! as NSData), userInfo: nil, storagePolicy: .Allowed)
            NSURLCache.sharedURLCache().storeCachedResponse(cachedURLResponse, forRequest: response.request!)

            guard response.result.error == nil else {
                // got an error in getting the data, need to handle it
                print("error calling GET on /places")
                print(response.result.error!)
                return
            }

            let swiftyJsonVar = JSON(data: cachedURLResponse.data)
            if let resData = swiftyJsonVar["places"].arrayObject  {
                // handle the results as JSON, without a bunch of nested if loops
                self.places = resData

                //print(self.places)

            }
            if self.places.count > 0 {
                self.tableView.reloadData()
            }
    }
}
person Charz    schedule 10.03.2016
comment
Можно ли изменить кешированные данные, чтобы позже обновить данные на сервере? - person fullmoon; 08.11.2017

Это версия Swift 3, основанная на ответе Чарла (с использованием SwiftyJSON и Alamofire):

func getData(){

    let query_url = "http://YOUR-URL-HERE"   

    // escape your URL
    let urlAddressEscaped = query_url.addingPercentEncoding(withAllowedCharacters:NSCharacterSet.urlQueryAllowed)


    //Request with caching policy
    let request = URLRequest(url: URL(string: urlAddressEscaped!)!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 20)

    Alamofire.request(request)
        .responseJSON { (response) in
            let cachedURLResponse = CachedURLResponse(response: response.response!, data: (response.data! as NSData) as Data, userInfo: nil, storagePolicy: .allowed)
            URLCache.shared.storeCachedResponse(cachedURLResponse, for: response.request!)

            guard response.result.error == nil else {

                // got an error in getting the data, need to handle it
                print("error fetching data from url")
                print(response.result.error!)
                return

            }

            let json = JSON(data: cachedURLResponse.data) // SwiftyJSON

            print(json) // Test if it works

            // do whatever you want with your data here

    }
}
person lenooh    schedule 07.12.2016
comment
Вызывая динамический (другой URL-адрес и параметр запроса) запрос, я получаю тот же ответ, который сохраняется в кеше в первый раз. Любая идея ? - person jaya raj; 04.03.2017
comment
@ленух. возможно ли хранить данные в автономном режиме с помощью Alamofire в swift 3 - person Uma Madhavi; 15.05.2017
comment
@jayaraj & lenooh .. Это мой код, ibb.co/nKKXkk. Как я могу получить это автономное хранилище в Swift 3 - person Uma Madhavi; 15.05.2017