iOS Swift JSON retrieving data issue - ios

What I would like to do: Retrieve in my static cell Labels the returns of my JSON Query.
What is my problem: I can not do it. Can not return single value to change my label.
What did I try: First of all, I install the Alamofire and SwiftyJSON library. Secondly I did my PHP Script which result in correct JSON Data. I create the labels in my storyboard and created the #IBOutlet UILabel in my UITableViewController. Thirdly, I did my Alamofire request and I can get the output of my whole Data.
What I can not do: I can not get the individuals fields to replace my labels. I would like to retrieve [username] [gender] [location] [birthday] [mobilephone] [signature] from my JSON and replace my labels with this return. But when I would like to retrieve [mobilephone] for example, I got nil result.
I also think I got a problem with my JSON as it does not seem to return Array but only Dictionary
my tableviewcontroller
import UIKit
import Alamofire
import SwiftyJson
class PersonalDetails: UITableViewController {
required init(coder aDecoder: NSCoder) {
println("init PersonalDetails")
super.init(coder: aDecoder)
}
deinit {
println("deinit PersonalDetails")
}
var usersData = [PersonalDetailsData]()
#IBOutlet weak var dataUsername: UILabel!
#IBOutlet weak var dataGender: UILabel!
#IBOutlet weak var dataArea: UILabel!
#IBOutlet weak var dataBirthday: UILabel!
#IBOutlet weak var dataMobilePhone: UILabel!
#IBOutlet weak var dataSignature: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var username = prefs.valueForKey("USERNAME") as NSString
//load and parse the JSON into an array
Alamofire.request(.GET, "http://mywebsite/app/data/jsonpersodata.php", parameters: ["username": username]).responseJSON { (request, response, data, error) in
let swiftyJSONObject = JSON(data!)
if (error != nil)
{
// got an error in getting the data, need to handle it
println("error calling GET usersdata")
println(error)
}
else if let data: AnyObject = data
{
// handle the results as JSON, without a bunch of nested if
let userdata = JSON(data)
if let mobilephone: String = userdata [0]["mobilephone"].stringValue {
self.dataMobilePhone.text = mobilephone
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func logoutTapped(sender : UIButton) {
let appDomain = NSBundle.mainBundle().bundleIdentifier
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)
self.performSegueWithIdentifier("logout", sender: self)
}
}
my class
import Foundation
class PersonalDetailsData: NSObject {
var dataUsername:String?
var dataGender:String?
var dataArea:String?
var dataBirthday:String?
var dataMobilePhone:String?
var dataSignature:String?
}
my json
[
{
"username": "username1",
"gender": "?",
"location": "??",
"birthday": "1983/01\/16",
"mobilephone": "136777777",
"signature": null
}
]
my php
<?php
header('Content-type: application/json');
/* include db.config.php */
include_once("config.php");
// Get user id
$id = isset($_GET['username']) ? mysql_real_escape_string($_GET['username']) : “”;
if(empty($id)){
$data = array ("result" => 0, "message" => 'Wrong user id');
} else {
// get user data
$sql = mysql_query("SELECT username, gender, location, birthday, mobilephone, signature FROM users WHERE username='$id'");
$data = array ();
while ($row = mysql_fetch_array($sql, MYSQL_ASSOC)) {
$row_array['username'] = $row['username'];
$row_array['gender'] = $row['gender'];
$row_array['location'] = $row['location'];
$row_array['birthday'] = $row['birthday'];
$row_array['mobilephone'] = $row['mobilephone'];
$row_array['signature'] = $row['signature'];
//push the values in the array
array_push($data,$row_array);
}
echo json_encode($data);
mysql_close($conn);
/* JSON Response */
}
?>

The JSON you're returning is actually an array, not a direct value of the object you're expecting (see the [ and ] in your raw JSON? They represent an array).
Here's what you could do to parse the first item in your JSON array:
// handle the results as JSON, without a bunch of nested if
let userdata = JSON(data)
if let mobilephone: String = userdata[0]["mobilephone"].string {
self.dataMobilePhone.text = mobile phone
}
// etc.

Related

How to get an especific object from a JSON of an API in Xcode with Alamofire?

I am doing a project of an E-commerce app in swift and the data must be obtained from an API that i created with mockapi.io
The link of the API is: https://62858a2ff0e8f0bb7c057f14.mockapi.io/categorias but if you dont want to enter the link here is how the JSON looks like:
[
{
"categorie1": "Fruits",
"categorie2": "Animals",
"categorie3": "Juices",
"categorie4": "Vegetables",
"categorie5": "Alcohol",
"categorie6": "Desserts",
"id": "1"
}
]
I have a file named "ResponseWS" that contains the sructs to obtain the data from the API, it looks like this:
import Foundation
struct ResponseWS:Decodable{
let Datos:[Categories]
}
struct Categories:Decodable{
let id: String
let categorie: String
}
I also have a file named "ConnectWS" where I process the data with the Alamofire:
import Foundation
import Alamofire
class ConnectWS{
static let cmd = ConnectWS()
private let urlBase = "https://62858a2ff0e8f0bb7c057f14.mockapi.io/categorias"
private let code = 200...299
func getUsers(user: String, pass: String,success: #escaping(_ user: String) -> (), failure: #escaping(_ error: Error?)-> ()) {
AF.request(urlBase,method: .get).validate(statusCode: code).responseDecodable(of: ResponseWS.self){
response in
if let respuesta = response.data {
print(String(decoding: respuesta, as: UTF8.self))
success(String(decoding: respuesta, as: UTF8.self))
}else{
print(response.error!)
failure(response.error)
}
}
}
}
And finally I have my ViewController where I want to show the data of the API:
import UIKit
class ViewControllerCategorias: UIViewController {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var message: UILabel!
#IBOutlet weak var buttonOutlet: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonAction(_ sender: Any) {
ConnectWS.cmd.getUsers(user: "erik", pass: "1234", success: {user in
self.name.text = user
}, failure: {error in
self.message.text = error.debugDescription
})
}
}
As you can see in the "ViewController" I have a button that when I click it, it returns ALL the data from the API, and what I want to do is get especifics "categories" from the API
When I click the button, the label shows:
[{"categorie1":"Fruits","categorie2":"Animals","categorie3":"Juices","categorie4":"Vegetables","categorie5":"Alcohol","categorie6":"Desserts","id":"1"}]
¿How can I get an especific object of the API?
You have to decode the response data into that object. You can use this generic method for decoding. Just create a model class/struct that you want to use for converting after decoding JSON.
func decode<T: Codable>(_ data: Data) -> T {
// Create a decoder
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
// Create a property for the decoded data
guard let results = try? decoder.decode(T.self, from: data) else {
fatalError("Failed to decode!")
}
// Return the ready-to-use data
return results
}

How to add data to a specific uid in Firestore Database?

I would like some help with the coding on how to store data into a specific user after the user have successfully logged in. Below are the codes for the page where user can input the details of their new readings.
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class NewBookViewController: UIViewController {
#IBOutlet weak var bookTitleTextField: UITextField!
#IBOutlet weak var bookAuthorTextField: UITextField!
#IBOutlet weak var bookSummaryTextField: UITextField!
#IBOutlet weak var ratingController: UIView!
#IBOutlet weak var newBookCancelButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
func validateFields() -> String? {
if
bookTitleTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
bookAuthorTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
bookSummaryTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
return "Please fill in all the fields."
}
return nil
}
#IBOutlet weak var newBookSaveButton: UIButton!
var ref = Firestore.firestore()
#IBAction func newBookSaveButtonTapped(_ sender: Any) {
let uid = Auth.auth().currentUser?.uid
self.ref?.child("new reading").child(uid).setValue(post)
func post() {
let bookTitleTextField = "bookTitle"
let bookAuthorTextField = "bookAuthor"
let bookSummaryTextField = "bookSummary"
let post : [String : AnyObject] = [ "bookTitle" : bookTitleTextField as AnyObject, "bookAuthor" : bookAuthorTextField as AnyObject, "bookSummary" : bookSummaryTextField as AnyObject]
}
this is the successful user sign up on cloud firestore. after the user have logged in, I wanted to add those 3 data (title, author, summary) FOR the specific user.
It looks like you're close. Right now, you aren't returning anything from post, though. I think you also mean to be getting the text values from each UITextField instead of just declaring Strings with the names of the fields.
#IBAction func newBookSaveButtonTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid else {
//handle your error here
return
}
self.ref?.child("new reading").child(uid).setValue(post())
}
func post() -> [String:String] {
return ["bookTitle" : bookTitleTextField.text ?? "",
"bookAuthor" : bookAuthorTextField.text ?? "",
"bookSummary" : bookSummaryTextField.text ?? ""]
}
You should take a much safer approach to handling the user's ID and the values of the text fields. Here, the data is only written to the database if the user is logged in and all 3 of the text fields have strings in them. I don't know what collection you intended to place this document in so I went with what you wrote but I suspect it isn't right.
class NewBookViewController: UIViewController {
private let db = Firestore.firestore()
#IBAction func newBookSaveButtonTapped(_ sender: Any) {
guard let uid = Auth.auth().currentUser?.uid,
let data = bookData() else {
return
}
db.collection("new reading").document(uid).setData(data)
}
// This returns an optional dictionary (nil when the data is incomplete).
// This is entirely optional (pun) but I suspect you don't want
// empty fields in these database documents.
func bookData() -> [String: Any]? {
guard let title = bookTitleTextField.text,
let author = bookAuthorTextField.text,
let summary = bookSummaryTextField.text else {
return nil
}
let data: [String: Any] = [
"bookTitle": title,
"bookAuthor": author,
"bookSummary": summary
]
return data
}
}

Realm writes data every time the app loads

I'm writing an App, that takes information from an Excel document and saves the Data in Realm. My problem is, that every time I open the App, the Realm Database will save a copy of the Information. Now I get my TableViews with 3 times the same items.
Here is the code in my Main View Controller:
class ViewController: UIViewController {
let realm = try! Realm()
var importExcel = Import()
var xslxConvert = xslxConverter()
var currentString: [String] = []
var Name = ""
#IBOutlet weak var VRLabel: UIButton!
#IBOutlet weak var configurationLabel: UIButton!
#IBOutlet weak var Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
currentString = importExcel.Results()
Label.text = Name
DispatchQueue.main.async {
self.saveData()
print("Data from Excel saved")
}
}
//MARK: - SaveData to the Realm
func saveData() {
do {
try realm.write {
for item in currentString {
let newItem = FunctionData()
newItem.functionName = item
realm.add(newItem)
}
}
} catch {
print ("Error trying to Realm data, \(error)")
}
}
}
How can I make a filter of something, to make that the App just save the Information from Excel ones?
Thanks a lot for the help!
Ok, I think it doesn't work with UUID(), because it will be different all time.
let filter = // choose what you need
if let items = Array(realm.objects(FunctionData.self).filter("parameterName == %#", filter)) {
// Do not save
} else {
// Save
}
And try to use realm.add(newItem, update: .modified) for saving

How Do I Post an Alamofire JSON Request on button press?

I am attempting to build a simple iOS app that features a login but first I want to make it so that pressing the "Continue" button on sign up posts data to the REST api. I can't successfully bind it to a button press for some reason. The code below doesn't know what inputboxes is. I ctr+dragged the button then added it in.
import Alamofire
import SwiftyJSON
import UIKit
class SignUpViewController: UIViewController {
var onButtonTapped : (() -> Void)? = nil
#IBOutlet weak var usernametextfield: UITextField!
#IBOutlet weak var passwordtextfield: UITextField!
#IBOutlet weak var emailtextfield: UITextField!
#IBOutlet weak var loginMessage: UILabel!
#IBAction func continueButtonPressed(sender: AnyObject) {
// POST requests dont need a response!
Alamofire.request(.POST, endpoint, parameters: inputboxes)
}
lazy var json : JSON = JSON.null
let endpoint = "anyapi.com/api/users"
override func viewDidLoad() {
digestUser()
}
func digestUser() {
let passwordInput = self.passwordtextfield.text
let usernameInput = self.usernametextfield.text
let emailInput = self.emailtextfield.text
let inputboxes: [String:AnyObject] = [
"hashword": passwordInput!,
"username": usernameInput!,
"email": emailInput!
]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Edit: Alamofire wasn't working properly because I forgot to include: "https://" before the URL
Declare inputboxes as instance variable below your outlets: var inputboxes: [String:AnyObject] = [:] and it should work.
Alamofire.request(.POST, BASE_URL , parameters: parameters as? [String : AnyObject])
.responseJSON { response in
if let JSON = response.result.value {
print("Success with JSON: \(JSON)")
}
}
Try to declare inputboxes outside function so it is accessible in the whole class.

Swift custom updateUI() function does not work on viewDidLoad

I'm working on an app, that should request some data from my server. I'm using Alamofire to do that, and then use SWXMLHash to parse the XML data. There are two View Controllers, on the first one I can write a shipment number, then override function prepareForSegue and send that number to the next View Controller that should display data from server and updateUI on viewDidLoad, but it does not. Where is a problem?
My Class:
class Shipment {
private var _shipmentNumber: String!
private var _shipmentStatus: String!
private var _trackURL: String!
var shipmentNumber: String {
if _shipmentNumber == nil {
_shipmentNumber = ""
}
return _shipmentNumber
}
var shipmentStatus: String {
if _shipmentStatus == nil {
_shipmentStatus = ""
}
return _shipmentStatus
}
init(spNumber: String) {
self._shipmentNumber = spNumber
_trackURL = "..."
}
func requestXmlInformation(completed: DownloadComplete) {
let url = NSURL(string: _trackURL)!
Alamofire.request(.GET, url).responseData { response in
if let xmlToParse = response.data as NSData! {
let xml = SWXMLHash.parse(xmlToParse)
do {
let xmlSpWeight = try xml["fmresultset"]["resultset"]["record"]["field"].withAttr("name", "ТotalWeight")["data"].element!.text! as String
self._shipmentStatus = xmlSpStatus
print(self._shipmentStatus)
} catch let err as NSError {
print(err.debugDescription)
}
}
}
}
}
My Second View Controller
#IBOutlet weak var numberLbl: UILabel!
#IBOutlet weak var weightLbl: UILabel!
#IBOutlet weak var statusLbl: UILabel!
#IBOutlet weak var packageQtyLbl: UILabel!
var shipment: Shipment!
override func viewDidLoad() {
super.viewDidLoad()
shipment.requestXmlInformation { () -> () in
self.updateUi()
print(self.statusLbl.text)
}
}
updateUI function:
func updateUi() {
numberLbl.text = shipment.shipmentNumber
weightLbl.text = shipment.shipmentWeight
statusLbl.text = shipment.shipmentStatus
packageQtyLbl.text = shipment.shipmentPackageQty
}
It prints data in terminal but i think updateUI function does not work.
Make sure that the code in your requestXmlInformation closure is called on the main thread. You shouldn't update the UI in background threads.
shipment.requestXmlInformation { () -> () in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.updateUi()
print(self.statusLbl.text)
})
}
Also, you don't seem to call the complete closure anywhere in your requestXmlInformation method

Resources