So I decided to migrate my App to Swift 3.0 but now I am seeing myself unable to solve a problem I´m facing with a .GET request with the new Alamofire. This is the request right now:
func getAlbums(_ ID: String, ANN: String, completed: #escaping DownloadComplete) {
let searchFT = ID.replacingOccurrences(of: " ", with: "+", options: NSString.CompareOptions.literal, range: nil)
let urlPath = ""
let url = URL(string: urlPath)
Alamofire.request(url!, method: .get).responseJSON { response in
if let JSON = response.result.value as! [String: AnyObject]! {
if let dict = JSON["results"] as! NSArray! {
AlbumsArtist.removeAll()
AlbumsArtist = [String]()
ImagesArtist.removeAll()
ImagesArtist = [String]()
for items in dict {
if let AT = items["artistName"] as? String {
if AT == ANN {
if let TN = items["collectionName"] as? String {
AlbumsArtist.append(TN)
} else {
AlbumsArtist.append("")
}
if let IM = items["artworkUrl100"] as? String {
let newString = IM.stringByReplacingOccurrencesOfString("100", withString: "600", options: NSStringCompareOptions.LiteralSearch, range: nil)
ImagesArtist.append(newString)
} else {
ImagesArtist.append("")
}
}
}
}
completed()
}
}
}
}
The problem that I am getting is with if let AT = items["artistName"] as? String the error says this: Type 'NSFastEnumerationIterator.Element (aka: 'Any') has no subscript members
Thank you very much!
As mentioned in the comment you should change your code to:
if let AT = items["artistName"] as? [[String:Any]]
But to make your code more safe you should use the following:
if let AT = items["artistName"] as? [[String:Any]] {
if AT == ANN {
guard let TN = items["collectionName"] as? String,
let IM = items["artworkUrl100"] as? String else { // Some error occurred }
AlbumsArtist.append(TN);
let newString = IM.stringByReplacingOccurrencesOfString("100", withString: "600", options: NSStringCompareOptions.LiteralSearch, range: nil)
ImagesArtist.append(newString)
}
}
Related
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 am new in using Swift I created an APIService using Alamofire, I tried to check whether I can retrieve data from API and it turn out well. My problem now, how can the data reflect to the variables in my Event Struct, so I could perform some validation base on the data read. I tried to check thru breakpoint but variable can't read data or either " " value. Please help me. Thank you
Event Struct
struct Event: Codable {
let id: String?
let name: String
let location: String
let startDateTime: Date
let endDateTime: String
let deleteFlag: Bool?
let deleteDateTime: String?
let dateCreated: String?
let hasRaffle: Bool?
let registrationReq: Bool?
let participantCount: Int
let closedFlag: Bool?
let closedDateTime: String?
let reopenFlag: Bool?
let reopenDateTime: String?
init?(JSON: [String: AnyObject]) {
guard let eventID = JSON["event_id"] as? String,
let eventName = JSON["event_name"] as? String,
let eventLocation = JSON["event_location"] as? String,
let startDateTime = JSON["start_datetime"] as? String,
let endDateTime = JSON["end_datetime"] as? String,
let participantCount = JSON["participant_count"] as? Int else {
return nil
}
self.id = eventID
self.name = eventName
self.location = eventLocation
self.endDateTime = endDateTime
self.participantCount = participantCount
if let formattedStartDateTime = getDateFromString(dateString: startDateTime, formatString: "yyyy-MM-dd'T'HH:mm:ss.SSS") {
self.startDateTime = formattedStartDateTime
}else {
self.startDateTime = Date()
}
if let deleteFlag = JSON["delete_flag"] as? Bool {
self.deleteFlag = deleteFlag
}else {
self.deleteFlag = nil
}
if let deletedDateTime = JSON["deleted_datetime"] as? String {
self.deleteDateTime = deletedDateTime
}else {
self.deleteDateTime = nil
}
if let dateCreated = JSON["date_created"] as? String {
self.dateCreated = dateCreated
}else {
self.dateCreated = nil
}
if let hasRaffle = JSON["hasRaffle"] as? Bool {
self.hasRaffle = hasRaffle
}else {
self.hasRaffle = nil
}
if let registrationReq = JSON["registration_req"] as? Bool {
self.registrationReq = registrationReq
}else {
self.registrationReq = nil
}
if let closedFlag = JSON["closed_flag"] as? Bool {
self.closedFlag = closedFlag
}else {
self.closedFlag = nil
}
if let closedDateTime = JSON["closed_datetime"] as? String {
self.closedDateTime = closedDateTime
}else {
self.closedDateTime = nil
}
if let reopenFlag = JSON["reopen_flag"] as? Bool {
self.reopenFlag = reopenFlag
}else {
self.reopenFlag = nil
}
if let reopenDateTime = JSON["reopen_datetime"] as? String {
self.reopenDateTime = reopenDateTime
}else {
self.reopenDateTime = nil
}
}
}
APIService
class APIService
{
let eventAPIKey: String
let eventBaseURL: URL?
//static let kEventID = "id"
init(APIKey: String)
{
self.eventAPIKey = APIKey
eventBaseURL = URL(string: BASE_URL)
}
func validatePasscode(passcode: String, completion: #escaping (Event?) -> Void)
{
let passcodeURL = URL (string: "\(PASSCODE_CHECKER_URL)/\(passcode)")
Alamofire.request(passcodeURL!, method: .get).responseJSON { (response) in
switch response.result{
case .success:
if let passcodeJSON = response.result.value{
print(passcodeJSON)
completion(Event(JSON: json as [String : Any]))
}
case .failure(let error):
print("\(error)")
}
}
}
}
You need to try and initialize the Event struct with the data you received passcodeJSON. As you can see your Event initializer is init?(JSON: [String: AnyObject])
if let passcodeJSON = response.result.value{
// print(passcodeJSON)
completion(Event(JSON: passcodeJSON))
}
and where you call your API service:
apiServiceClient.validatePasscode(passcode: "testing") { eventDetails in
// do something with eventDetails here
}
Hi I am trying to learn RXSwift and First time I came across these concepts like Maps and Compact Maps.
I am able to get the response, but this line always returns empty.
objects.compactMap(DummyUser.init)
fileprivate let Users = Variable<[DummyUser]>([])
fileprivate let bag = DisposeBag()
response
.filter { response, _ in
return 200..<300 ~= response.statusCode
}
.map { _, data -> [[String: Any]] in
guard (try? JSONSerialization.jsonObject(with: data, options: [])) != nil else {
return []
}
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
// print(json!["results"])
return json!["results"] as! [[String : Any]]
}
.filter { objects in
return objects.count > 0
}
.map { objects in
// objects.forEach{print($0["name"]!)}
let names = objects.map { $0["name"]!}
print(names)
return objects.compactMap(DummyUser.init)
}
.subscribe(onNext: { [weak self] newEvents in
self?.processEvents(newEvents)
})
.disposed(by: bag)
func processEvents(_ newEvents: [DummyUser]) {
var updatedEvents = newEvents + Users.value
if updatedEvents.count > 50 {
updatedEvents = Array<DummyUser>(updatedEvents.prefix(upTo: 50))
}
Users.value = updatedEvents
DispatchQueue.main.async {
self.MianUsertable.reloadData()
}
// refreshControl?.endRefreshing()
let eventsArray = updatedEvents.map{ $0.dictionary } as NSArray
eventsArray.write(to: userFileURL, atomically: true)
}
My Json Response is Here
https://randomuser.me/api/?results=5
DummyUser Class
import Foundation
typealias AnyDict = [String: Any]
class DummyUser {
let gender: String
let name: AnyDict
let dob: String
let picture: AnyDict
init?(dictionary: AnyDict) {
guard let Dgender = dictionary["gender"] as? String,
let Dname = dictionary["name"] as? AnyDict,
let birthdata = dictionary["dob"] as? AnyDict,
let Ddob = birthdata["dob"] as? String,
let Dpicture = dictionary["picture"] as? AnyDict
else {
return nil
}
gender = Dgender
name = Dname
dob = Ddob
picture = Dpicture
}
var dictionary: AnyDict {
return [
"user": ["name" : name, "gender": gender, "dob": dob],
"picture" : ["userImage": picture]
]
}
}
In your DummyUser model you are using failable initializer, so in case of wrong dictionary provided to init method it will return nil.
compactMap automatically automatically filters nil's and that's the reason why your output is empty.
Looking at this piece of code:
let names = objects.map { $0["name"]!}
return objects.compactMap(DummyUser.init)
I would debug this variable called names because it probably has wrong input for the DummyUser initializer. It should be dictionary containing all of your DummyUser parameters. You can also debug your failable initializer to see which of the parameter is missing.
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.
I don't know what i'm doing wrong in this block, but while checking it through instruments>leaks it shows 34 leaks and sometimes less leaks on this block, what is wrong in it, please share your expert opinion with me to understand the reasons of leaks.
public func getInspectionAddressCoordinates(requestMessage: InspectionServiceRequestMessage) -> (ServiceResponseMessage){
if Global.shared.isConnected {
let params = [
"address": requestMessage.inspectionAddress.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.CaseInsensitiveSearch, range: nil)
]
let networkRequestMessage = RequestNetworkMessage(requestType: RequestType.GET, contentType: ContentType.ContentTypeHTML, url: MAP_URL, params: params)
let networkResponseMessage = BaseNetwork().performNetworkTask(requestMessage: networkRequestMessage)
switch networkResponseMessage.statusCode {
case .Success:
if let data = networkResponseMessage.data as? NSData {
do {
let jsonObject: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
if let jsonDict = jsonObject as? [String: AnyObject] {
if let results = jsonDict["results"] as? [AnyObject] {
for dict in results {
if let geo = dict["geometry"] as? [String: AnyObject] {
if let loc = geo["location"] as? [String: AnyObject] {
if let lat = loc["lat"] as? Double {
if let lon = loc["lng"] as? Double {
print("\(lat), \(lon)")
let msg = self.getSuccessResponseMessage("Inspection Status Updated")
msg.data = ["lat":lat, "lon": lon]
return msg
}
}
}
}
}
}
}
} catch _ {
return self.getErrorResponseMessage("Unable to parse")
}
return self.getErrorResponseMessage("Unable to parse")
}
else {
return self.getErrorResponseMessage("Unable to parse")
}
case .Failure:
return self.getErrorResponseMessage("Network error in inspection status update request")
case .Timeout:
return self.getTimeoutErrorResponseMessage("Inspection status update request has been timed out")
}
}
else {
return self.getErrorResponseMessage(ERROR_NO_NETWORK)
}
}