Function
import Foundation
struct Foods {
var fid: Int
var fname: String
var hits: Int?
var addr: String?
}
class Food {
func getFoodsById(_ fid: Int) -> [Foods]? {
var foods: Array<Foods>?
let URL_GET_TEAMS:String = "http://jsm0803.iptime.org:81/html/sufoo/getFoodById.php"
let requestURL = URL(string: URL_GET_TEAMS)
let request = NSMutableURLRequest(url: requestURL!)
request.httpMethod = "POST"
//request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let postParameters = "Fid=" + String(fid)
// "name="+teamName!+"&member="+memberCount!;
request.httpBody = postParameters.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request as URLRequest){data, response, error in
if error != nil{
print("error is \(error)")
return;
}
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) // 테스트용
//print(dataString!)
do{
var itemJSON: Dictionary<String, Any>!
itemJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? Dictionary
let items:Array<Dictionary<String, Any>> = itemJSON["Food"] as! Array
for i in 0 ..< items.count{
var food: Foods
let item = items[i]
let fid:Int = item["Fid"] as! Int
let fname: String = item["Fname"] as! String
let hits: Int? = item["Hits"] as? Int
let delegate_image:String? = item["Delegate_Image"]as? String
food = Foods(fid: fid, fname: fname, hits: hits, addr: delegate_image)
foods?.append(food)
print("fid ->", food.fid)
print("fname ->", food.fname)
if let f = food.hits {
print("hits ->", f)
}
else{
print("hits ->", food.hits as Any)
}
if let f = food.addr {
print("delegate_image -> ", f)
}
else {
print("delegate_image -> ", food.addr as Any)
}
print("==============")
print("")
print ("fid ==== ", foods?.first?.fid)
print ("fid ==== ", foods?.last?.fid)
}
}catch {
print(error)
}
}
task.resume()
if let result = foods{
for r in result{
print ("r.fname")
print (r.fname)
}
}
print ("000000")
return foods
}
}
If I run this code in Xcode, I get the result below:
000000
fid -> 140
fname -> 밀 흑밀
hits -> nil
delegate_image -> ./pic_data/2309/20180423201954alj
==============
fid ==== nil
fid ==== nil
I want to return [var foods: Array?] value.
But although I made some values of Foods struct and used append function of Array in order to add Foods value into Array, it didn't works. There is no value in Array, only nil.(fid ==== nil)
Thus, it is useless to return that Array.
How can I get right results?
I need to get values like below:
fid ==== 140
fid ==== 140
Please help me to solve this problem.
I think I used Optional wrongly.
Main problem is you did not initiate your foods array.
//func getFoodsById(_ fid: Int) -> [Foods]? {
func getFoodsById(_ fid: Int) -> [Foods] {
//var foods: Array<Foods>?
var foods = [Foods]() // or var foods: [Foods] = []
You can initiate your array here and return empty list as a result if there is no food for the ID.
And rename your Foods struct with Food and Food class with something like FoodOperations. This would make more sense.
Read this for swift guides.
struct Food {
...
}
class FoodOperations {
...
}
if let error = error {
print("error is \(error)")
return;
}
print ("fid ==== ", foods.first?.fid ?? 0)
Better make a model class to store data. For example:
class Food {
var fid: Int
var fname: String
var hits: Int?
var addr: String?
}
Then after getting result do something like this :
var foodArray = [Foods]()
for item in items {
let food = Food()
guard let food.fid = item["Fid"] as? Int else {return}
foodArray.append(food)
}
print(foodArray)
And suggested by Francesco Deliro, use completion handler to return values when your for loop is done. In your case return statement is getting called before for loop ends.
Also don't do force unwrapping, try to use if let/ guard let .
You need to change your function implementation adding a completion handler, because your return is called before the for loop is ended:
func getFoodsById(_ fid: Int, completion: (([Foods]?, Error?) -> Void)?) {
//your precedent code
//then when you make the request call the completion
let task = URLSession.shared.dataTask(with: request as URLRequest){data, response, error in
guard error == nil else {
completion?(nil, error)
return
}
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) // 테스트용
//print(dataString!)
do{
var itemJSON: Dictionary<String, Any>!
itemJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? Dictionary
let items:Array<Dictionary<String, Any>> = itemJSON["Food"] as! Array
for i in 0 ..< items.count{
var food: Foods
let item = items[i]
let fid:Int = item["Fid"] as! Int
let fname: String = item["Fname"] as! String
let hits: Int? = item["Hits"] as? Int
let delegate_image:String? = item["Delegate_Image"]as? String
food = Foods(fid: fid, fname: fname, hits: hits, addr: delegate_image)
foods?.append(food)
print("fid ->", food.fid)
print("fname ->", food.fname)
if let f = food.hits {
print("hits ->", f)
}
else{
print("hits ->", food.hits as Any)
}
if let f = food.addr {
print("delegate_image -> ", f)
}
else {
print("delegate_image -> ", food.addr as Any)
}
print("==============")
print("")
print ("fid ==== ", foods?.first?.fid)
print ("fid ==== ", foods?.last?.fid)
}
completion?(foods, nil)
}catch {
print(error)
completion?(nil, error)
}
}
task.resume()
}
And you can use it in this way:
//where you have to make the call
self.getFoodsById(yourId) { (foodsArray, error) in
//here you can manage your foods array
}
Declare Struct
struct AlertModel { var alert:String var title:String }
Create Variable
var alertData = AlertModel
Append data
let alert = AlertModel(alert: "Waring", title: "Over Load") alertData.append(alert)
print(alertData)
Related
I want to use the variables UserFirstName, UserLastName and Numpts outside this function to put them in labels. Maybe I can put them in labels directly from the function but I'm not sure what's best.
If more code is needed please ask.
P.S I'm a beginner
func parseJson(_ data:Data)
{
var actArray = [Activity]()
do {
let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [Any]
for jsonResult in jsonArray
{
let jsonDict = jsonResult as! [String:String]
let act = Activity (NumUser: jsonDict["NumUser"]!, cFirstname: jsonDict["cFirstname"]!, cLastname: jsonDict["cLastname"]!, cEmail: jsonDict["cEmail"]!, cPassword: jsonDict["cPassword"]!, NumPts: jsonDict["NumPts"]!, forgotpass: jsonDict["forgotpass"]!)
if case (act.activityContainsString(value: "allo#allo.com")) = true
{
print(act.cFirstname)
print(act.cLastname)
print(act.NumPts)
let UserFirstName = (act.cFirstname)
let UserLastName = (act.cLastname)
let UserNumPts = (act.NumPts)
}
print (act)
actArray.append(act)
}
delegate?.itemsDownloaded(activities: actArray)
}
catch
{
print("There was an error")
}
}
I should be able to use my variables outside the function.
outside class make one struct like this
public struct UserDetails{
let firstName: String
let lastName: String
let numpty: Int
//make init method if needed for this struct
}
Inside class do below things:
func parseJson(_ data:Data)
{
var actArray = [Activity]()
do {
let jsonArray = try JSONSerialization.jsonObject(with: data, options: []) as! [Any]
for jsonResult in jsonArray
{
let jsonDict = jsonResult as! [String:String]
let act = Activity (NumUser: jsonDict["NumUser"]!, cFirstname: jsonDict["cFirstname"]!, cLastname: jsonDict["cLastname"]!, cEmail: jsonDict["cEmail"]!, cPassword: jsonDict["cPassword"]!, NumPts: jsonDict["NumPts"]!, forgotpass: jsonDict["forgotpass"]!)
if case (act.activityContainsString(value: "allo#allo.com")) = true
{
let data = UserDetails(firstName: act.cFirstname, lastName: act.cLastname, numpty: act.NumPts)
//function in which I can pass data to those labels
passDataToLabelsAndUpdate(data: data)
}
actArray.append(act)
}
delegate?.itemsDownloaded(activities: actArray)
}
catch
{
print("There was an error")
}
}
// this is the function which we can use to update labels
public func passDataToLabelsAndUpdate(data: UserDetails) {
// update labels
label.text = data.firstName
//similarly for other items
}
I'm playing with Swift and JSONPlaceholder. I want to retrieve all the data contained in: https://jsonplaceholder.typicode.com/photos
I created a function that is acceding to the url, downloading the JSON but then I don't know how can I obtain the title and the thumbnailUrl to pass then for populate the tableView. In the past I used this code but now it's not working because on the JSONPlaceholder there are no array.
Any help for re-arrange the code for read and obtain the jsonplaceholder elements?
func loadList(){
let url = URL(string: urlReceived)
var myNews = NewInfo()
let task = URLSession.shared.dataTask(with: url!) {
(data, response, error) in
if error != nil{
print("ERROR")
}
else{
do {
if let content = data{
let myJson = try JSONSerialization.jsonObject(with: content, options: .mutableContainers)
//print(myJson)
if let jsonData = myJson as? [String : Any] {
if let myResults = jsonData["list"] as? [[String : Any]]{
//dump(myResults)
for value in myResults{
//Read time string from root
if let time = value ["dt_txt"] as? String{
myNews.time = time
}
//Read main container
if let main = value["main"]
as? [String : Any]{
if let temperature = main["temp"] as? Double {
myNews.temperature = String(temperature)
}
}
//Read from weather container
if let weather = value["weather"] as? [[String: Any]]{
for value in weather{
if let weatherContent = value["description"] as? String{
myNews.weatherDescription = weatherContent
}
}
}
self.myTableViewDataSource.append(myNews)
}
dump(self.myTableViewDataSource)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
}
catch{
}
}
}
task.resume()
}//End func
Okey, so with Alamofire + SwiftyJSON, you can do this:
func loadList(){
let url = "https://jsonplaceholder.typicode.com/photos"
AF.request(url).responseJSON { (response) in
switch response.result {
case .success(let value):
let json = JSON(value)
print(json)
for value in json.arrayValue {
let url = value.dictionaryValue["url"]!.stringValue
let albumId = value.dictionaryValue["albumId"]!.stringValue
let thumbnailUrl = value.dictionaryValue["thumbnailUrl"]!.stringValue
let id = value.dictionaryValue["id"]!.stringValue
let title = value.dictionaryValue["title"]!.stringValue
// Add this album to array.
let album = AlbumModel(id: id, albumId: albumId, title: title, thumbnailUrl: thumbnailUrl)
albums.append(album)
}
case .failure(let error):
print(error)
}
}
}
EDIT:
I made model for values
class AlbumModel {
var id: String?
var albumId: String?
var title: String?
var thumbnailUrl: String?
init(id: String?, albumId: String?, title: String?, thumbnailUrl: String?){
self.id = id
self.albumId = albumId
self.title = title
self.thumbnailUrl = thumbnailUrl
}
}
After that, just create an array like var albums = [AlbumModel]() and you can append all the albums to this. Easy to use after in tableViewcell (example: albums[indexPath.row].id)
I try use nested DispatchGroup, there is my code :
class TranslateService {
private let myGroup = DispatchGroup()
func translateText(text:[String],closure:#escaping ((_ success:String?,_ error:Error?) -> Void)) {
var translateString: String = ""
var responseError: Error?
for index in 0...text.count - 1 {
let urlString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20171105T134956Z.795c7a0141d3061b.dc25bae76fa5740b2cdecb02396644dea58edd24&text=\(text[index])&lang=fa&format=plain&options=1"
if let allowString = Utilities.shareInstance.getQueryAllowedString(url: urlString) {
if let url = URL(string:allowString){
myGroup.enter()
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
self.myGroup.leave()
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let res = json as? [String:Any] {
if let code = res["code"] as? Int {
if code == 200 {
if let textArr = res["text"] as? [AnyObject] {
let flattArr = Utilities.shareInstance.flatStringMapArray(textArr)
if flattArr.count > 0 {
translateString += "،" + flattArr[0]
}
}
}
}
self.myGroup.leave()
}
}catch {
responseError = error
self.myGroup.leave()
}
}
self.myGroup.notify(queue: .main) {
print("Finished all requests.")
print(translateString)
closure(translateString, responseError)
}
}
}
}
}
}
class AddressService {
private let translateService: TranslateService = TranslateService()
private let myGroup = DispatchGroup()
func fetchAddressFromGeographicalLocation(latitude: Double, longitude: Double,closure:#escaping ((_ success:String,_ name:String,_ error:Error?) -> Void)) {
var address: String = ""
let name: String = ""
var responseError: Error?
if let url = URL(string:"https://maps.googleapis.com/maps/api/geocode/json?latlng=\(latitude),\(longitude)&key=AIzaSyAdEzHZfZWyjLMuuW92w5fkR86S3-opIF0&language=fa®ion=IR&locale=fa"){
self.myGroup.enter()
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
self.myGroup.leave()
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let addressDic = json as? [String:Any] {
if let result = addressDic["results"] as? [AnyObject] {
if result.count > 0 {
let flattRes = Utilities.shareInstance.flatMapArray(result)
let item = flattRes[0]
address = item["formatted_address"] as? String ?? ""
var res = address
if res.isContainEnglishCharachter {
self.myGroup.enter()
let resArr = res.components(separatedBy: ",")
var all : [String] = []
for item in resArr {
if item != " " {
all.append(item)
}}
self.translateService.translateText(text: all, closure: {stringAdd,error in
self.myGroup.enter()
if error != nil {
self.myGroup.leave()
}else {
address = stringAdd ?? ""
self.myGroup.leave()
} })
}else {
self.myGroup.leave()
}
}
}
}
}catch {
responseError = error
self.myGroup.leave()
}
self.myGroup.notify(queue: .main) {
// print("Finished all requests.")
closure(address, name, responseError)
}
}
}
}
}
All I want is that the myGroup I put in the class AddressService waiting for the myGroup that I put in the class TranslateService.
but now self.myGroup.notify not call in the AddressService class, So closure not work.
How can solve this problem, Thank you for all the answers.
I think you are over complicating it.
If I understood a bit, what you want to do is the following:
Get an address from the Address service.
Translate some words of that address, one by one, using the translation service.
When using the Address service there is only one call being done, so there is no need to use Dispatch Groups at this point. Dispatch Groups are used to synchronize more than one async call.
For your Translation service you can make good use of the Dispatch groups, since you are doing calls to the service inside a for loop. The problem here is, that the implementation is slightly wrong. You are setting the notification block inside the for loop, and it should be outside, so that it gets only triggered once, when all the calls inside the loop are done.
So move this block outside the for loop in the Translation service:
self.myGroup.notify(queue: .main) {
print("Finished all requests.")
print(translateString)
closure(translateString, responseError)
}
Now "Finished all requests." will only be printed once, when all requests are done.
In the address service you do not need dispatch groups at all. Just wait until the completion block is called.
self.translateService.translateText(text: all, closure: {stringAdd,error in
Everything is done here already.
}
Here I tried to parse the data from my local server but unable to parse it and it returning empty data and below are my model classes from which the data I was passing to an table view which can anyone help me what's wrong in implementing it?
Here I had attached my image which follows the Json format:
Code:
var homePageModel = [HomeBanner]()
func HomeBannerDownloadJsonWithURL(){
let url = URL(string: homePageUrl)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil { print(error!); return }
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
for item in jsonObj {
print(item)
for dict in item {
print(dict)
let dict = HomeBanner(json: item)
self.homePageModel.append(dict!)
print(self.homePageModel)
}
}
print(self.homePageModel)
DispatchQueue.main.async {
self.homeTableView.delegate = self
self.homeTableView.dataSource = self
self.homeTableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
}
struct HomeBanner {
let title : String?
let titleInArabic : String?
let showTitle : String?
var banner = [ChildrenBanners]()
init?(json : [String:Any]) {
if let customAttribute = json["childran_banners"] as? [[String: AnyObject]] {
var result = [ChildrenBanners]()
for obj in customAttribute {
result.append(ChildrenBanners(json: obj as! [String : String])!)
}
self.banner = result
} else {
self.banner = [ChildrenBanners]()
}
self.title = json["title"] as? String ?? ""
print(self.title)
self.titleInArabic = json["title_in_arabic"] as? String ?? ""
self.showTitle = json["show_title"] as? String ?? ""
}
}
struct ChildrenBanners {
let bannerId : String?
let name : String?
let status : String?
let sliderId : String?
let desktopImage : String?
let mobileImage : String?
let imageAlt : String?
let sortOrder : String?
let startTime : String?
let endTime : String?
init?(json : [String:Any]) {
self.bannerId = json["banner_id"] as? String ?? ""
print(self.bannerId)
self.name = json["name"] as? String ?? ""
self.status = json["status"] as? String ?? ""
self.sliderId = json["slider_id"] as? String ?? ""
self.desktopImage = json["desktop_image"] as? String ?? ""
self.mobileImage = json["mobile_image"] as? String ?? ""
self.imageAlt = json["image_alt"] as? String ?? ""
self.sortOrder = json["sort_order"] as? String ?? ""
self.startTime = json["start_time"] as? String ?? ""
self.endTime = json["end_time"] as? String ?? ""
}
}
Just try these lines of code
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
self.homePageModel = jsonObj.map{HomeBanner(json: $0)}
print(self.homePageModel)
DispatchQueue.main.async {
self.homeTableView.delegate = self
self.homeTableView.dataSource = self
self.homeTableView.reloadData()
}
}
} catch {
print(error)
}
and there is no necessity of making optional initializer for HomeBanner and ChildrenBanners just use init(json : [String : Any]){} for both struct
Root of json is an array and then second level is dictionary with keys list1, list2 etc. You are missing that in your code. Should be something like this (I haven't compiled it).
if let data = data, let jsonObj = try JSONSerialization.jsonObject(with: data) as? [[String:[String:Any]]] {
for item in jsonObj {
for (_, dict) in item {
if let obj = HomeBanner(json: dict) {
self.homePageModel.append(obj)
}
}
}
}
There are lot of other issues in your code. Like force unwrapping optional. Using same parameters again within a scope. For example.
for dict in item {
let dict = HomeBanner(json: item)
// ....
}
You shouldn't use same param names like you are using dict it hides the scope of the outer dict.
My problem arises when I want to populate data from my mysql database into a class object. I am trying to return an array of objects and it returns nil and then it fills itself somehow. How can I make it fill before returning the blank array?
Here is my code and a screenshot of code output
import Foundation
class Research
{
var mainResearchImageURL:String = ""
var userProfileImageURL:String = ""
var caption:String = ""
var shortDescription:String = ""
init(mainResearchImageURL :String, userProfileImageURL:String, caption:String, shortDescription:String)
{
self.mainResearchImageURL = mainResearchImageURL
self.userProfileImageURL = userProfileImageURL
self.caption = caption
self.shortDescription = shortDescription
}
class func downloadAllResearches()->[Research]
{
var researches = [Research]()
let urlString = "http://localhost/test/index.php"
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"
let postString = "action=listresearches"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error in
if (error == nil) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSArray
//let dictionary = json!.firstObject as? NSDictionary
var counter:Int = 0;
for line in json!{
let researchData = line as! NSDictionary
let researchLineFromData = Research(mainResearchImageURL: researchData["research_mainImageURL"] as! String, userProfileImageURL: researchData["research_creatorProfileImageURL"] as! String, caption: researchData["research_caption"] as! String, shortDescription: researchData["research_shortDescription"] as! String)
researches.append(researchLineFromData) //researches bir dizi ve elemanları Research türünde bir sınıftan oluşuyor.
counter += 1
print ("counter value \(counter)")
print("array count in loop is = \(researches.count)")
}
}catch let error as NSError{
print(error)
}
} else {
print(error)
}})
task.resume()
print("array count in return is = \(researches.count)")
return researches
}
}
And this is the output:
add this on you completionHandler ( it works if you update a view)
dispatch_async(dispatch_get_main_queue(), {
if (error == nil) { ...... }
})
Advice 1:
return the task and use a completion param in your method,
you can cancel the task if it's too slow.
Advice 2 :
Use alamofire and swiftyJson framework
What happen here is that you are returning the value before finish (remember that the call is Asynchronous), you can make something like this:
class func downloadAllResearches(success:([Research])->Void,failure:(String)->Void)
{
var researches = [Research]()
let urlString = "http://localhost/test/index.php"
let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"
let postString = "action=listresearches"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error in
if (error == nil) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSArray
//let dictionary = json!.firstObject as? NSDictionary
var counter:Int = 0;
for line in json!{
let researchData = line as! NSDictionary
let researchLineFromData = Research(mainResearchImageURL: researchData["research_mainImageURL"] as! String, userProfileImageURL: researchData["research_creatorProfileImageURL"] as! String, caption: researchData["research_caption"] as! String, shortDescription: researchData["research_shortDescription"] as! String)
researches.append(researchLineFromData) //researches bir dizi ve elemanları Research türünde bir sınıftan oluşuyor.
counter += 1
print ("counter value \(counter)")
print("array count in loop is = \(researches.count)")
}
success(researches)
}catch let error as NSError{
print(error)
failure("Can be extract from NSERROR")
}
} else {
print(error)
failure("Error - Can be extract for NSERROR")
}})
task.resume()
}
And for call this Fuction use something like this:
Research.downloadAllResearches({ (objects:[Research]) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Do whatever you like with the content
})
}) { (failureLiteral:String) -> Void in
}