How to store JSON response in Core Data? - ios

I have a Core Data object.
I was curious, as a relatively inexperienced iPhone developer, whether anyone could recommend an approach, and a suitable JSON implementation for the iPhone, which would allow me to store JSON responses as Core Data objects.
I'm getting 5 records (dictionaries) from JSON response. I need to store them in Core Data, and retrieve them when ever necessary .
I have searched, unsuccessfully, for a tutorial/code sample on this point so any assistance would be gratefully received.

You can check here also, They have explained from beginning, you can follow and will get it
http://www.appcoda.com/fetch-parse-json-ios-programming-tutorial/
http://maniacdev.com/2013/04/library-for-integrating-web-services-turning-json-data-into-core-data-backed-native-objects

Set your Core Data property to transformable. Then use this extension:
extension NSObject {
static func storeJSON(dataToStore: [String: AnyObject], completion: (data: NSData?) -> Void) {
do {
let data = try NSJSONSerialization.dataWithJSONObject(dataToStore, options: [])
completion(data: data)
} catch let error as NSError {
print("NSJSONSerialization Error: \(error)")
completion(data: nil)
}
}
func retrieveJSON(completion: (json: JSON) -> Void) {
if let data = self as? NSData {
do {
let nsJSON = try NSJSONSerialization.JSONObjectWithData(data, options: [])
completion(json: JSON(nsJSON))
} catch let error as NSError {
print("NSJSONSerialization Error: \(error)")
completion(json: nil)
}
}
}
}
If you don't use SwiftJSON then just use:
extension NSObject {
static func storeJSON(dataToStore: [String: AnyObject], completion: (data: NSData?) -> Void) {
do {
let data = try NSJSONSerialization.dataWithJSONObject(dataToStore, options: [])
completion(data: data)
} catch let error as NSError {
print("NSJSONSerialization Error: \(error)")
completion(data: nil)
}
}
func retrieveJSON(completion: (json: AnyObject?) -> Void) {
if let data = self as? NSData {
do {
let nsJSON = try NSJSONSerialization.JSONObjectWithData(data, options: [])
completion(json: nsJSON)
} catch let error as NSError {
print("NSJSONSerialization Error: \(error)")
completion(json: nil)
}
}
}
}
Example use with user.jsonTest as a transformable in core data:
func testThis() {
makeSaveData() {
self.user.jsonTest!.retrieveJSON() {
json in
print("json: \(json)")
}
}
}
func makeSaveData(completion: () -> Void) {
var solarResourceDic: [String: String] = [:]
var srDics: [[String: String]!] = []
for i in 0..<5 {
solarResourceDic = [:]
solarResourceDic["system_capacity"] = "\(i)"
solarResourceDic["azimuth"] = "\(i + 1)"
solarResourceDic["tilt"] = "\(i + 2)"
solarResourceDic["array_type"] = "\(i + 3)"
solarResourceDic["module_type"] = "\(i + 4)"
solarResourceDic["losses"] = "\(i + 5)"
srDics.append(solarResourceDic)
}
let dic = ["Solar Resource": srDics]
NSObject.storeJSON(dic) {
data in
if data != nil {
self.user.jsonTest = data
appDelegate.coreData.saveContext()
completion()
} else {
print("Error storing data")
}
}
}

You can follow this great tutorial which shows how to save json to core data. In general you need to learn to things: how to parse a json feed (it will result to NSDictionaries with the parsed items) and how to save this dictionaries to your persistent store. This tutorial covers both.

I know this is old but there is a library called Sync which does json saving to DB
Sync eases your everyday job of parsing a JSON response and getting it into Core Data. It uses a convention-over-configuration paradigm to facilitate your workflow.
Syncing JSON to Core Data is a repetitive tasks that often demands adding a lot of boilerplate code. Mapping attributes, mapping relationships, diffing for inserts, removals and updates are often tasks that don't change between apps. Taking this in account we took the challenge to abstract this into a library. Sync uses the knowledge of your Core Data model to infer all the mapping between your JSON and Core Data, once you use it, it feels so obvious that you'll wonder why you weren't doing this before.
Link:-Sync

Related

Decode nested JSON arrays and dictionaries in Swift using Structs with JSONserialization

I am trying to create some structs to decode some JSON received from an API using JSONSerialization.jsonObject(with: data, options: [])
This is what the JSON looks like:
{"books":[{"title":"The Fountainhead.","author":"Ayn Ranyd"},{"title":"Tom Sawyer","author":"Mark Twain"},{"title":"Warhol","author":"Blake Gopnik"}]}
Here are the structs that I am trying to use for decoding.
struct BooksReturned : Codable {
let books : [Book]?
}
struct Book : Codable {
let BookParts: Array<Any>?
}
struct BookParts : Codable {
let titleDict : Dictionary<String>?
let authorDict : Dictionary<String>?
}
The error is:
The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
The non-working code I am using to decode is:
let task = session.dataTask(with: url) { data, response, error in
if let data = data, error == nil {
let nsdata = NSData(data: data)
DispatchQueue.main.async {
if let str = String(data: data, encoding: .utf8) {
let json = try? JSONSerialization.jsonObject(with: data, options: [])
do {
let mybooks = try JSONDecoder().decode(BooksReturned.self, from: data)
//do something with book
}
} catch {
print(error.localizedDescription)
print(error)
}
}
}
} else {
// Failure
}
}
task.resume()
}
I have some very limited ability to change JSON. The only thing I can do is remove the "books" : Everything else is received from an external API.
Thanks for any suggestions on how to get this to work.
The JSON you provided seems to be valid. Modify your Book model and the decoding part as the following.
Model:
struct Book: Codable {
let title, author: String
}
Decoding:
let task = session.dataTask(with: url) { data, response, error in
if let data = data, error == nil {
DispatchQueue.main.async {
do {
let mybooks = try JSONDecoder().decode(BooksReturned.self, from: data)
print(mybooks)
}
} catch {
print(error.localizedDescription)
print(error)
}
}
} else {
// Failure
}
task.resume()

Alamofire ignoring closure that sets/handles data

I am using Alamofire to perform a network request to the dummy data source https://jsonplaceholder.typicode.com/posts and render it in my application.
I have a file called NetworkingClient.swift that abstracts most of this logic out and allows is to be reused.
public class NetworkingClient {
typealias WebServiceResponse = ([[String: Any]]?, Error?) -> Void
func execute(_ url: URL, completion: #escaping WebServiceResponse) {
Alamofire.request(url).validate().responseJSON { response in
print(response)
if let error = response.error {
completion(nil, error)
} else if let jsonArray = response.result.value as? [[String: Any]] {
completion(jsonArray, nil)
} else if let jsonDict = response.result.value as? [String: Any] {
completion([jsonDict], nil)
}
}
}
}
I call the execute in a set up function I have in my main view controller file:
func setUpView() {
let networkingClient = NetworkingClient()
let posts_endpoint = "https://jsonplaceholder.typicode.com/posts"
let posts_endpoint_url = URL(string: TEST_URL_STRING)
networkingClient.execute(posts_endpoint_url) { (json, error) in
if let error = error {
print([["error": error]])
} else if let json = json {
print(json)
}
}
}
Where I call this inside viewDidLoad() under super.viewDidLoad()
I've set breakpoints inside the response in closure and I wasn't able to trigger any of them, in fact I think it's skipping the entire thing completely and I don't know why.
I am following this youtube video where the video guide does the exact same thing except their request goes through.
What am I missing?
I am using Swift 4, XCode 10, running on iOS 12.1 and my AlamoFire version is 4.7.
It's all about async stuff.your are declaring NetworkingClient object in func called setupView and Alamofire using .background thread to do stuff.so time executing of networkingClient.execute is not clear and after that setUpView deallocate from memory and all it's objects are gone including NetworkingClient.so for preventing this just declare let networkingClient = NetworkingClient() outside of function

Use JSON data offline Swift 3

I'm a beginner so please be gentle.
Ok so I successfully parse my JSON from the server and can access all of its contents but know I'm kind of stuck here:
//Functionn for fetching API data
func fetchRates(){
//Set EndPoint URL
let url = URL(string: endPoint)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error as Any)
}
do {
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
let dictonary = json as? [String: Any?]
let ratesJson = dictonary?["rates"] as? [String: Double]
print(ratesJson as Any)
} catch let jsonError {
print(jsonError)
}
}.resume()
}
Now I want to get my ratesJson Dictionary out of this function and actually use its contents to do some calculations, but after hours of trying and searching I still have no idea how.
I just want to:
Get JSON data stored presistently (for offline use)
Update the Data when the app launches and overwrite the old data
The possibilities I've come across so far are CoreData, which seems really confusing and complicated and Realm which I don't really know at all.
Help would be very much appreciated and sorry if this seems really stupid
Since you have the JSON parsing, I would create a object class to store the data using Core Data. There is a lot that goes into core data usage at first, but it is well worth the time investment to learn if you plan on continuing iOS development. Core Data Tuts
I also do not know what you plan on doing with your app, or the purpose, and Core Data might be a little overkill according to your purpose, but you could always save the updated JSON each time, overwriting a file in the device's document's dir, then read the data back in and parse it again.
You can write the JSON data to a outputstream using:
JSONSerialization.writeJSONObject(_ obj: Any, to stream: OutputStream, options opt: JSONSerialization.WritingOptions = [], error: NSErrorPointer)
//Write JSON data into a stream. The stream should be opened and configured.
//The return value is the number of bytes written to the stream, or 0 on error.
//All other behavior of this method is the same as the dataWithJSONObject:options:error: method.
UPDATE: Ok I think I see your question now. There are two ways that you can go about getting your data back to your view controller: 1. Delegate pattern 2.Closures. I personally like using delegates over closures.
With delegates you will need to create a protocol:
protocol SessionDataTaskComplete {
func dataDownloaded(parsedJson : [String : Double])
}
And inside of your URLSession class you will need a class variable to hold that protocol delegate:
var dataTaskCompleteDelegate : SessionDataTaskComplete?
Then you will need to have your view controller implement that protocol:
class MyViewController: UIViewController, SessionDataTaskComplete {
override func viewDidLoad() {
super.viewDidLoad()
//Also assign your viewController as your URLSession's SessionDataTaskComplete delegate
myURLSession.dataTaskCompleteDelegate = self
}
func dataDownloaded(parsedJson : [String : Double]) {
//Handle data as you wish here
}
}
Now in your fetchRates() you can use that delegate to pass the data back to your viewController:
func fetchRates(){
//Set EndPoint URL
let url = URL(string: endPoint)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error as Any)
}
do {
let json = try? JSONSerialization.jsonObject(with: data!, options: [])
let dictonary = json as? [String: Any?]
let ratesJson = dictonary?["rates"] as? [String: Double]
//print(ratesJson as Any)
if (dataTaskCompleteDelegate != nil) {
dataTaskCompleteDelegate!.dataDownloaded(parsedJson: ratesJson)
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
If you are not comfortable with using the delegate pattern, that is something else I would suggest spending time learning, as it is used all over with the iOS SDK. Try to think of them as an object assigning a duty/task to another object.

How To Fetch and Parse JSON Using Swift 2 + XCode 7 + iOS 9 [duplicate]

This question already has answers here:
How to parse JSON in Swift using NSURLSession
(5 answers)
Closed 7 years ago.
Was looking for useful tutorials for networking with Swift 2 and iOS 9, but it seems that topic has no content online. I have watched the WWDC session Networking with NSURLSession But I couldn't adapt the new API of iOS9 to have an asynchronous way to fetch and parse JSON data. What I have tried:
do {
if let url = NSURL(string: "site/api.php?option=fetchOps"),
let data = NSData(contentsOfURL: url),
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [[String:AnyObject]] {
}
} catch let error as NSError {
print(error.description)
}
Please share your experience with networking on iOS9 and Swift2, any best practices maybe a Singleton class for networking and parsing maybe an ORM ?
Easy way to make a request for you:
How to make an HTTP request in Swift?
Or If you want to send request by your own:
HTTP POST error Handling in Swift 2
Converting a String to JSON Object:
Just as an example, here I've converted a NSString
First of all convert the NSString to NSDictionary
static func convert(src: NSString) -> NSDictionary {
// convert String to NSData
let data = src.dataUsingEncoding(NSUTF8StringEncoding)
var error: NSError?
// convert NSData to 'AnyObject'
let anyObj: AnyObject?
do {
anyObj = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(rawValue: 0))
} catch let error1 as NSError {
error = error1
anyObj = nil
}
if(error != nil) {
// If there is an error parsing JSON, print it to the console
print("JSON Error \(error!.localizedDescription)")
//self.showError()
return NSDictionary()
} else {
return anyObj as! NSDictionary
}
}
And then, use it like this
if let name = dictionary["name"] as? String {
self.name = name
}
You can use the converted JSON to make an object like this
import Foundation
class Student {
var name="";
init(dictionary: NSDictionary) {
let _NAME = "name"
if let name = dictionary[_NAME] as? String {
self.name = name
}
}
}
oha, well ... search for async network calls for swift. You will find many things.
But yea... Create an NSMutableRequest, init eith the URL, set the method to GET or POST.
Now make:
let task = NSURLSession.sharedSession().dataTaskWithRequest(yourMutableRequest) {
data, response, error in
// do your stuff here
}
task.resume()
This will run asynchron, so you need to think about a way to handle the data. At first your response data is in the variable data this is a type of NSData. You will need this to create your dictionary:
let dictionaryOrArray = NSJSONSerialization.JSONObjectWithData(data, options:.MutableContainers, error: nil)
Maybe you need to put this in a guard or if let statemant.
Now you need to pass the data to the function caller... you can do this in many ways, i prefer success and failure blocks
You call your Request function and you provide a success and failure block. This blocks will do what should happen in the end:
let successBlock:[AnyObject]->Void = { response in
// handle your dict
}
let failureBlock:NSError->Void = { error in
// error handling
}
YourClass.fetchSomething(someURL, parameters: params, requestMethod: method, success : successBlock, failure: failureBlock)
Something like that. In your request block with data, response, error you just do the following:
If error {
failure(error)
return
}
success(dictionaryOrArray)

Error handling in block with Swift syntax

The error handling here doesn't feel right. Anyone have any suggests for how to improve it? Using optional binding to establish errors and a return value variable.
That cool?
class ChargePointsFetcher {
func getDevices(location: NSURL, completion handler:([ChargeDevice]?, error: NSError?) -> Void) {
let request = NSURLRequest(URL: location)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: { (let data, let response, let error) -> Void in
var returnValue: NSError?
if let e = error {
returnValue = e
}
var collection: [ChargeDevice]?
if returnValue == nil {
collection = [ChargeDevice]()
var parsingError: NSError?
if let json: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments, error: &parsingError) as? NSDictionary {
if let chargeDevices = json.valueForKey("ChargeDevice") as? NSArray {
for chargeDevice in chargeDevices {
let device = ChargeDevice()
collection!.append(device)
}
}
}
if let e = parsingError {
returnValue = e
}
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
handler(collection, error: returnValue)
})
})
}
}
In my opinion, you should try very hard to avoid the (Value?, Error?) pattern when you can. It creates bad corner cases since two combinations are illegal. Instead, when possible, I suggest (Value, Error?) any time Value has a sensible "zero". In this case "zero" is "empty array". This matches the ObjC sense very closely, since a nil NSArray is very similar to an empty array.
Doing that, you can substantially simplify this code:
func getDevices(location: NSURL, completion handler:([ChargeDevice], error: NSError?) -> Void) {
// Note that error is marked "var" so we can modify it, and switched from NSError! to NSError?
let task = session.dataTaskWithRequest(request, completionHandler: { (let data, let response, var error: NSError?) -> Void in
var result = [ChargeDevice]()
if error == nil {
// Avoid NSArray and NSDictionary wherever you can in Swift
if let
json = NSJSONSerialization.JSONObjectWithData(data,
options: .AllowFragments,
error: &error
) as? [String:AnyObject],
chargeDevices = json["ChargeDevice"] as? [AnyObject] {
// map is much simpler in this case, but in your full code, for may fine fine
result = chargeDevices.map{ _ in ChargeDevice() }
}
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
handler(result, error: error)
})
})
}
Note that in your code and my code, incorrect but valid JSON will not generate an error of any kind. It'll just quietly skip through the as? calls. I would probably move all of this JSON work into another function to keep this closure from getting out of hand.
Another approach here is called a Result. My preferred example is Rob Rix's. Even though I've done a lot of work on Result, I've personally find it difficult to bridge to Cocoa cleanly. It's useful if your entire program uses it, and if you encapsulate most Cocoa interaction, but I've found it cumbersome to use as a one-off solution. (Many people would disagree with me here, it's just my experience. Result is definitely worth exploring to form your own opinion.)

Resources