I am building an app where people need to fill out a form and then it creates an HTTP post where the API will return a Json file to the app with data I need. Everything is working fine with accessing the API however I want to parse the data in another view controller. How can I access the JSON file from another view controller?
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
^^^^^^^
How do I take this JSON file to the next view controller so I dont have to do the parsing below?
let jsonData = json
let ratesJson = jsonData["rates"]!
let rates = ratesJson as! NSArray
print("Rates: \(rates)")
print("*************")
print(rates.count)
print("*************")
for item in 0..<rates.count {
let specificRate = rates[item]
let price = (specificRate as AnyObject)["amount_local"]!
let provider = (specificRate as AnyObject)["provider"]!
print("--\(item)--")
print("Price: \(price!)")
print("Provider: \(provider!)")
}
}
} catch let error {
print(error.localizedDescription)
}
})
I assume by your comment, your intention is to actually pass a JSON object, not a JSON file to the next view controller.
Therefore, you just need to pass the JSON object to your segue and assign it as a property to the next view controller.
Since the question was very open ended, here is one possible solution and interpretation.
Example:
//Sample snippet from code from question
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
performSegue(withIdentifier: "MySegue", sender: json);
}
override func prepare(for segue: UIStoryboardSegue!, sender: Any?) {
if (segue.identifier == "MySegue") {
let vc = segue.destinationViewController as! RandomViewController
vc.json = sender as! [String:Any];
}
}
One of the things that usually cause bugs in programs is state. What you are asking here in my opinion is state.
What I would do is i would write a generic function ( Swift encourages functional programming in a lot of cases, have a look at: swift map, filter etc. that deals with the HTTP request and returning the data in a closure.
This closure will then live within the view controller you want your json object in, thus solving your problem of accessing said data.
Now I don't believe that is the right approach either. I would create an object for your specific data returned, like say a "struct Person" or whatever your needs are.
This has 2 advantages:
In swift 3.1 you can have a custom init() method that parses the JSON safely ( remember, optionals! ) and populates the structs data accordingly
In swift 4.0 and Xcode9.0 You will be able to morph the struct you just created to conform to the Codable and Decodable protocols with wich json parsing will be greatly simplified, as explained here: ultimate swift json guide
Hope this was of any help.
Related
There is some documented API in Swift for moving items in an array.
The following extension does not compile.
Value of type [String] has no member move.
protocol Database: AnyObject {
var records: [String] { get set }
}
extension Database {
func move(from source: IndexSet, to destination: Int) throws {
records.move(fromOffsets: source, toOffset: destination)
// try save()
}
}
Any help appreciated.
The documentation states that this API is part of the standard library, yet it isn't available if you import Swift (or Foundation or UIKit).
This API is available in SwiftUI!
I have a tableView that bring all documents from Firestore collection and i want to fetch the last document only once when it is added to the Firestore after user refresh the tableView and after that I want to remove the listener so when the user refresh the tableView it only fetch document once , i used detach listener [from the documentation here][1] but it didn't work
func updatedFireStoredata() {
let listener = Firestore.firestore().collection("collection1").order(by: "date").limit(toLast: 1).addSnapshotListener() { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let data = diff.document.data()
self.amount.append(data["amount"] as? String ?? "")
print("New city: \(diff.document.data())")
}
}
}
table2.reloadData()
listener.remove()
}
If your goal is to only call once to retrieve this document, I would consider using the offline persistence that is built in to the SDK. You will need to request the document once with offline persistence enabled, but after that call you can request this data from that local cache.
https://firebase.google.com/docs/firestore/manage-data/enable-offline
https://firebase.google.com/docs/firestore/manage-data/enable-offline#disable_and_enable_network_access
Alternatively you could store the document locally, and not include it in reload.
If it is the case that this data does change, just not as often as every refresh, it may make sense to move this to an eventing model. You can use firestore watch/listen to monitor that query and get update callbacks as needed. https://firebase.google.com/docs/firestore/query-data/listen
There are a couple of things
First, requiring a user to keep clicking refresh to get the current data is not a good experience. The data should be presented to the user automatically without interaction
Second, Firebase is asynchronous - code outside the Firebase closure will execute before the data in the closure. See my answer Firebase is asynchronous <- that's for the Realtime Database but the concept applies to asynchronous functions in general.
To fix both issues here's updated code
func updatedFireStoredata() {
let listener = Firestore.firestore().collection("collection1").order(by: "date").limit(toLast: 1).addSnapshotListener() { querySnapshot, error in
if let err = error {
print("Error fetching snapshots: \(err.localizedDescription)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let data = diff.document.data()
self.amount.append(data["amount"] as? String ?? "")
print("New city: \(diff.document.data())")
}
}
table2.reloadData() //needs to be inside the closure
}
}
I moved the tableView reload inside the closure so it executes when new data arrives and then removed the code to remove the listener so your tableView will update with new data.
I also updated the error handler to handle an actual error code.
One thing you will want to do is to keep track of what the user has selected is is looking at - you don't want a selected item, for example, to un-select if other data is added which is what will happen when the tableView reloads.
I got a response from redash as follows:
{'PP_DOM': '{"DEFAULT":100}',....................., 'Myst':'["a","b","c","d"]',
I want to unmarshal "myst" key's value as a list in golang.I am newbie here.
You can unmarshall into map[string]interface{} if you have an unknown data structure to explore it. you then may later want to define a struct which maps to the data properly. So to get started, use something like this:
var jsonData = []byte(`{"PP_DOM": {"DEFAULT":100},"Myst":["a","b","c","d"]}`)
var data map[string]interface{}
err := json.Unmarshal(jsonData, &data)
if err != nil {
fmt.Println("json: error decoding", err)
}
fmt.Printf("Myst:%+v", data["Myst"])
https://play.golang.org/p/V5GBtLH6oLs
Your json doesn't appear to be valid, better to post what you have tried instead, which should at least involve valid json.
You should take a look at the docs for the json pkg, in particular the examples.
I am met with a problem when I tried to download a batch of images off firebase storage. Basically, because the file sizes differ, the images are not appended to the image array properly causing the images to be in the wrong order that I wanted. Below is the code
import Foundation
import FirebaseStorage
class GalleryCellDetailedData {
var selectedGallery:String?
var count:Int
init(selectedGallery:String?,count:Int){
self.selectedGallery = selectedGallery
self.count = count
}
func addImages(completion:(data:[NSData])->()){
var datas = [NSData]()
let myGroup = dispatch_group_create()
for i in 0..<count {
dispatch_group_enter(myGroup)
getImage(i, completion: { (image:NSData) in
datas.append(image)
print("Finish Request \(i)")
dispatch_group_leave(myGroup)
})
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
completion(data: datas)
})
}
private func getImage(number:Int, completion:(image:NSData)->()){
let storage = FIRStorage.storage()
//Reference to Firebase Profile Picture Storage
let storageRef = storage.referenceForURL("gs://mannacatering-addcb.appspot.com")
print("Initiating Image Download")
let galleryPicRef = storageRef.child("Gallery/\(selectedGallery!)/g\(String(number)).jpg")
//Download Image
galleryPicRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("fail to download image")
}
dispatch_async(dispatch_get_main_queue(), {
print("Dispatching image")
completion(image:data!)
})
}
}
}
I split them up into 2 separate functions because I tried to manage them in a single function and the order is in a mess as well and I thought this might work but apparently not.
Instead of storing your data in an array, store it in a dictionary. The key can be the number i or however you want to refer to an image when you use it.
I recommend taking an approach similar to Storing multiple images into firebase and getting urls, where you store the URLs in the Realtime Database, and use that as your source of truth for ordering, display, etc. It's far easier and makes for better apps :)
I converted ipad signature to png image successfully using UIImagePNGRrepresentation(Image). Now I want to store this Image from swift to a SQL Server database using a web service. I have not any idea about how do this?
This is my swift code
UIGraphicsBeginImageContextWithOptions(self.signatureMainImageview.bounds.size, false, 0.0)
self.signatureMainImageview.image?.drawInRect(CGRectMake(0, 0, self.signatureMainImageview.frame.size.width, self.signatureMainImageview.frame.size.height))
let SaveImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let image = UIImagePNGRepresentation(SaveImage)
var CardDataObj = structCardData()
CardDataObj.CustomerSignature = image!
let requestCardData = NSMutableURLRequest(URL: NSURL(string: "http://URL")!)
requestCardData.HTTPMethod = "POST"
let postString = CardDataObj.jsonRepresentation
requestCardData.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(requestCardData) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
Now I want to know how to get this image in webservice? which datatype use in webservice for image? which datatype use in sql for image? How to send this image to sql?
Rather than a data task you need an upload task. Either uploadTaskWithRequest:fromData:completionHandler or its file or stream variants
In order to begin the task you need to call task.resume()
It also helps to retrieve the response if you cast to HTTPURLResponse like so:
if let response = response as? NSHTTPURLResponse {
response.statusCode
response.allHeaderFields
}
I wrote a blogpost on uploading using a stream, which might be of some use. Here's also a more general post about NSURLSession.
The first blogpost linked to will give you some server-side code in PHP to receive a stream, but if you are uncertain about what to do on the SQL I'd recommended breaking this question into two and asking that question separately.