How to remove optional text from json Result In swift - ios

I am using newsApi to get list of news from it. I created model based on news's property, all property are optional in model and as i parse it printed to console getting result but all fields have data with optional text
I have created three struct based on news api fields, They are like
struct GoogleNews: Codable {
var status: String?
var totalResults: Int?
var articles: [Article]
}
struct Article: Codable {
var source: Source
var author: String?
var title: String?
var description: String?
var url: String?
var urlToImage: String?
var publishedAt: String?
var content: String?
}
struct Source: Codable {
var id: String?
var name: String?
}
Calling the appi
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {return}
do {
let allNews = try JSONDecoder().decode(GoogleNews.self, from: data)
print(allNews.articles[0])
} catch let error {
print(error.localizedDescription)
}
}.resume()
After calling the api, in result all fields are having result with optional text
name: Optional("Venturebeat.com")), author: Optional("Dean Takahashi"), title: Optional("How Paymentwall’s Terminal3 lets game developers create their own online shops"), description: Optional("Paymentwall built a business as a global payments platform, with much of its focus on games. Last year, the company spun out its Terminal3 as a platform for monetizing and distributing games. Now it is making it easier for indie, small, and medium-size game c…")...ect
What should be the solution to remove the optional text from the results..

For the values in your struct that are optional, be sure they're optionals because you know for sure that there are cases where a value won't be returned. If you'd like to unwrap them, you have 2 ways of doing so.
The first way is to use an if-let statement, which would look something like this:
if let name = allNews.articles[0].name {
}
Within the curly braces is where you would use the variable name, which wouldn't be the optional value you're asking about because it's been unwrapped.
The second method you could use is a guard statement, which looks like this:
guard let name = allNews.articles[0].name else { return }
In this instance, the name variable would be unwrapped and can be used anywhere in the scope of your code. However, it's only valid if it can be successfully unwrapped. If it cannot, then the return statement is called and breaks out of whatever scope it's in.

The thing is you're trying to print an object which all properties are optionals. You have to unwrap the properties even if you unwrap the article . You can unwrap your optionals like this:
let article = allNews.articles[0]
if let source = article.source {
print(source)
}
Also you can unwrap more than one property at once:
if let source = article.source, let author = article.author, let title = article.title {
// do things
}

Just remove the sign of ? from the end of the Struct members declaration to become like this:
let author: String

Related

Query relation images from Parse that uses Back4App database

So I have an ParseObject setup as this - This is the main Object in Parse called MGLocation:
struct MGLocation: ParseObject {
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var originalData: Data?
var ACL: ParseACL?
var title: String?
var category: String?
init() {}
init(objectId: String?) {
self.objectId = objectId
}
}
Then I have my Codable setup using the following code:
struct Place: Codable, Identifiable {
let id: Int
var b4aId = ""
let title: String?
let category: String
init(
id: Int,
title: String?,
category: String,
) {
self.id = id
self.title = title
self.category = category
}
init(with p: MGLocation) {
self.id = atomicId.wrappingIncrementThenLoad(ordering: .relaxed)
self.b4aId = p.objectId ?? ""
self.title = p.title ?? "Undefined"
self.category = p.category ?? "Uncategorized"
}
}
Then I have the following function which pulls in the MGLocation:
func fetchPlaces() {
let query = MGLocation.query().limit(1000)
query.find { [weak self] result in
guard let self = self else {
return
}
switch result {
case .success(let items):
self.places = items.map({
Place(with: $0)
})
case .failure(let error):
}
}
}
Questions:
What is the best way that I can pull in a relational column? Inside MGLocation, I have a related column called images which can access another object.
This calls MGImage which has the following columns:
id, title, column, mime
Does anyone know how I can infuse and pull in the related column also? All help will be appreciated!
I recommend using Parse-Swift from here: https://github.com/netreconlab/Parse-Swift, as oppose to the parse-community one. I'm one of the original developers of Pase-Swift (you can see this on the contributors list) and I don't support the parse-community version anymore. The netreconlab version is way ahead of the other in terms of bug fixes and features and you will get better and faster support from the netreconlab since I've been the one to address the majority of issues in the past.
What is the best way that I can pull in a relational column? Inside MGLocation, I have a related column called images which can access another object.
I always recommend looking at the Playgrounds for similar examples. The Playgrounds has Roles and Relation examples. Assuming you are using version 4.16.2 on netreconlab. The only way you can do this with your current relation on your server is like so:
// You need to add your struct for MGImage on your client
struct MGImage: ParseObject { ... }
struct MGLocation: ParseObject {
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var originalData: Data?
var ACL: ParseACL?
var title: String?
var category: String?
var images: ParseRelation<Self>? // Add this relation
/* These aren't needed as they are already given for free.
init() {}
init(objectId: String?) {
self.objectId = objectId
}
*/
}
//: It's recommended to place custom initializers in an extension
//: to preserve the memberwise initializer.
extension MGLocation {
// The two inits in your code aren't needed because ParseObject gives them to you
}
//: Now we will see how to use the stored `ParseRelation on` property in MGLocation to create query
//: all of the relations to `scores`.
Task {
do {
//: Fetch the updated location since the previous relations were created on the server.
let location = try await MGLocation(objectId: "myLocationObjectId").fetch()
print("Updated current location with relation: \(location)")
let usableStoredRelation = try location.relation(location.images, key: "images")
let images = try await (usableStoredRelation.query() as Query<MGImage>).find()
print("Found related images from stored ParseRelation: \(images)")
} catch {
print("\(error.localizedDescription)")
}
}
If your images column was of type [MGImage] instead of Relation<MGImage> then you would be able to use var images: [MGImage]? in your MGLocation model and simply use include on your query. You can see more here: https://github.com/netreconlab/Parse-Swift/blob/325196929fed80ca3120956f2545cf2ed980616b/ParseSwift.playground/Pages/8%20-%20Pointers.xcplaygroundpage/Contents.swift#L168-L173

Issue with adding Data to an AnyObject Var so that I could make native ads work

for postdata in postdata {
if index < tableViewItems.count {
tableViewItems.insert(postdata, at: index)
index += adInterval
} else {
break
}
}
I'll need to add both PostData ads and Native Ads on the same AnyObject Var for me to get it to work and I can't find a way to add the Post Data because it says an error appears saying "Argument type 'PostData' expected to be an instance of a class or class-constrained type." Assistance would be very much appreciated, thank you.
edit 1
class Ad {
var postimage: String!
var publisher: String!
var timestring: String!
var timestamp = Date().timeIntervalSince1970
}
class PostDataAd: Ad {
// Declare here your custom properties
struct PostData1
{
var postimage: String
var publisher: String
var timestring : String
var timestamp = Date().timeIntervalSince1970
}
}
class NativeAd: Ad {
// Declare here your custom properties
struct NativeAd
{
var nativeAds: [GADUnifiedNativeAd] = [GADUnifiedNativeAd]()
}
}
My model class to merge both Data into one AnyObject Var
and then trying to append the Data from Firebase by doing this
var ads: [Ad] = [PostDataAd(), NativeAd()]
let postList = PostDataAd.PostData1(postimage: postimage, publisher:
postpublisher, timestring: postid, timestamp: timestamp)
self.ads.insert(postList, at:0)
an error occurs saying Cannot convert value of type 'PostDataAd.PostData1' to expected argument type 'Ad'
I hope I got what you want correctly. So basically you have two objects which you want to store in one array, under AnyObject. If that is correct, I recommend you to go in a bit of different direction. It is a nice example where you can use subclassing. You can declare a class called Ad, where you define the common properties which will be true for both PostDataAds and NativeAds.
class Ad {
// Add here the common elements you want to retrieve from both classes
var name: String = ""
}
After you define your PostDataAds and NativeAds inheriting from Ad:
class PostDataAd: Ad {
// Declare here your custom properties
}
class NativeAd: Ad {
// Declare here your custom properties
}
And if you want to define an array with two types of objects you can go:
let ads: [Ad] = [PostDataAd(), NativeAd()]
When retrieving you can check their type:
if let item = ads.first as? PostDataAd {
// The Ad is a PostDataAd
} else if let item = ad as? NativeAd {
// The Ad is a NativeAd
}
Or at some cases you don't even how to know the exact type, as you can access the properties defined in Ad without checking.
Update:
First of all your PostData1 and Ad objects are the same, you don't need to duplicate them. If you really want to have two classes you can inherit PostData1 from Ad.
class Ad {
var postimage: String
var publisher: String
var timestring: String
var timestamp = Date().timeIntervalSince1970
// You can add timestamp here also if you wish
init(postimage: String, publisher: String, timestring: String) {
self.postimage = postimage
self.publisher = publisher
self.timestring = timestring
}
}
class PostDataAd: Ad {
// Define some custom properties
}
And if you want to append PostData to the [Ad] array, you would do the following:
var ads: [Ad] = []
// Replace with your values
let postList = PostDataAd(postimage: "", publisher: "", timestring: "")
ads.insert(postList, at: 0)
// Appending NativeAd works also
let nativeAdd = NativeAd(postimage: "", publisher: "", timestring: "")
ads.append(nativeAdd)

Safely unwrapping optional values and add it to Alamofire parameters [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a computed property of type Parameters in my APIRouter
// MARK: - Parameters
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters = ["main_category_id": doctorsFilter.0, "page": doctorsFilter.1, "specialty_id": doctorsFilter.2, "city_id": doctorsFilter.3, "region_id": doctorsFilter.4, "name": doctorsFilter.5, "company_id": doctorsFilter.6, "order_by": doctorsFilter.7]
return params
default:
return nil
}
}
some values in the Typealias called doctorsFilter are optional.
currently I have a warning asking me to provide default value for the optional values, and I don't want to provide default values , I want to check if the value exist to add it, otherwise i will not add the key and the value
how can I safely unwrap the optional values and add it to the parameters dictionary with out saying if let for all optional values?
example:
if let specialtyID = doctorsFilter.2 {
params["specialty_id"] = specialtyID
}
I don't want to unwrap it this way as I will check for all optional values and it will take more lines of code
EDIT:-
the DoctorsFilter type is documented, when I initialize an instance of type DoctorsFilter the autocomplete tells me which of them is what, I I've thought about making the DoctorsFilter class before but I'm looking for another way if any, maybe a built in reserved word can handle the whole situation! , I want to make it simple as much as possible.
making a function that handles the dictionary and returns it in DoctorsFilter class is an option. I'm thinking of adding this function to the APIRouter, is it fine to add it there? is it the rule of the APIRouter to handle the parameters ? or the APIRouter just interested in taking the parameters and will not handle it ?
There is no "one line" solution, but you can use KeyPaths to reduce the series of if let ... statements down to a loop.
Start by creating a struct for your filter rather than using a tuple.
To facilitate this, we define a protocol for Parameterable - This protocol requires a dictionary that maps parameter names (String) to the property (KeyPath) that holds that parameter name as well as a function to return the parameters dictionary.
protocol Parameterable {
var paramNames: [String:KeyPath<Self,String?>] {get}
func parameters() -> [String:Any]
}
Use an extension to create a default implementation of the parameters() function, as the code will be the same for all Parameterables. It iterates over the dictionary entries and uses the associated KeyPath to access the relevant property and put it in the output dictionary. If a given property is nil then it simply isn't added to the output dictionary, because that is how dictionaries work. No need to explicitly check.
(If you import Alamofire then you can use the typedef Parameters where I have used [String:Any])
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
parameters[paramName]=self[keyPath:keypath]
}
return parameters
}
}
Use this protocol to create a DoctorsFilter implementation:
struct DoctorsFilter: Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: String?
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:KeyPath<Self,String?>] = [
"main_category_id":\.mainCategoryId,
"page":\.page,
"specialty_id":\.specialtyId,
"city_id":\.cityID,
"region_id":\.regionId,
"name":\.name,
"company_id":\.companyId,
"order_by":\.orderBy]
}
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
return doctorsFilter.parameters()
case .someOtherThing(let someOtherThing):
return someOtherThing.parameters()
default:
return nil
}
}
}
The other approach is to simply split your creation of the parameters dictionary into multiple lines; If you assign nil against a dictionary key then there is no key/value pair stored in the dictionary for that key. In this case I have left your tuple approach in place, but you could use the struct (and I strongly suggest you do so)
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters()
params["main_category_id"] = doctorsFilter.0
params["page"] = doctorsFilter.1
params["specialty_id"] = doctorsFilter.2
params["city_id"] = doctorsFilter.3
params["region_id"] = doctorsFilter.4
params["name"] = doctorsFilter.5
params["company_id"] = doctorsFilter.6
params["order_by"] = doctorsFilter.7
return params
default:
return nil
}
}
If we want to handle mixed properties, rather than just optional strings, we need to modify the code slightly. We need to use PartialKeyPath. This makes the code a little more complex since the subscript operator for a PartialKeyPath returns a double optional. This needs to be handled.
protocol Parameterable {
var paramNames: [String:PartialKeyPath<Self>] {get}
func parameters() -> [String:Any]
}
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
let value = self[keyPath:keypath] as? Any?
if let value = value {
parameters[paramName] = value
}
}
return parameters
}
}
struct DoctorsFilter:Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: Int
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:PartialKeyPath<Self>] =
["main_category_id":\Self.mainCategoryId,
"page":\Self.page,
"specialty_id":\Self.specialtyId,
"city_id":\Self.cityID,
"region_id":\Self.regionId,
"name":\Self.name,
"company_id":\Self.companyId,
"order_by":\Self.orderBy]
}
There are three primary ways to safely unwrap an optional. You can also provide default values if you wish to unwrap an optional.
Guard Statement Unwrapping
var firstString: String?
// In some cases you might performa a break, continue or a return.
guard let someString = firstString else { return }
print(someString)
If Let Unwrapping
var secondString: String?
var thirdString: String?
thirdString = "Hello, World!"
// Notice that we are able to use the same "IF LET" to unwrap
// multiple values. However, if one fails, they all fail.
// You can do the same thing with "Guard" statements.
if let someString = secondString,
let someOtherString = thirdString {
print(someString)
print(someOtherString)
} else {
// With this code snippet, we will ALWAYS hit this block.
// because secondString has no value.
print("We weren't able to unwrap.")
}
Default Value Unwrapping
var fourthString: String?
// The ?? is telling the compiler that if it cannot be unwrapped,
// use this value instead.
print(fourthString ?? "Hello, World")
In Swift it is recommended that anytime you see a ! that you use some form of unwrapping. Swift is very "Type Safe".
Here's a resource you can use for Optionals.
https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html
Your Solution
Your solution might look something like this.
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
if let mainCatID = doctorsFilter.0,
let page = doctorsFilter.1,
let specialtyID = doctorsFilter.2,
let cityID = doctorsFilter.3,
let regionID = doctorsFilter.4,
let name = doctorsFilter.5,
let companyID = doctorsFilter.6,
let orderBy = doctorsFilter.7 {
params: Parameters = ["main_category_id": mainCatID,
"page": page,
"specialty_id": specialtyID,
"city_id": cityID,
"region_id": regionID,
"name": name,
"company_id": companyID,
"order_by": orderBy]
return params
} else {
//Unable to safely unwrap, return nothing.
return nil
}
default:
return nil
}
}

Swift: Decode arbitrary protocol types

I'm trying to decode JSON that has a variable property which conforms to a protocol.
Consider the following set of structs:
protocol P: Decodable {
var id: String { get }
}
struct A: P {
let id: String
var someThing: Double
}
struct B: P {
let id: String
var anotherThing: String
}
struct S: Decodable {
let id: String
let instanceOfProtocol: P
}
We're trying to decode S.
The automatic synthesis of Decodable does not work (because the decoder can't know which type P is going to be decoded to) so I'm trying to do this in a custom initializer:
Option 1: Exhaustively Check Conforming Types:
if let instance = try? container.decode(A.self, forKey: .instanceOfProtocol) {
instanceOfProtocol = instance
} else if let instance = try? container.decode(B.self, forKey: .instanceOfProtocol) {
instanceOfProtocol = instance
} else {
throw NoConformingTypeError()
}
This works, but is very verbose, repetitive, and doesn't scale well, so I'm looking for other options.
Option 2: (Ab)use superDecoder:
let possibleTypes: [P.Type] = [A.self, B.self]
let childDecoder = try container.superDecoder(forKey: .instanceOfProtocol)
let decoded: [P] = possibleTypes.compactMap { try? $0.init(from: childDecoder) }
guard let instance = decoded.first else { throw NoConformingTypeError() }
instanceOfProtocol = instance
This works as well, but I'm not sure if superDecoder is meant to be used this way, or if it will break in the future.
Option 3:
let possibleTypes: [P.Type] = [A.self, B.self]
let decoded: [P] = possibleTypes.compactMap { try? container.decode($0, forKey: .instanceOfProtocol) }
guard let instance = decoded.first else { throw NoConformingTypeError() }
instanceOfProtocol = instance
This feels like the best option so far, but doesn't compile due to Ambiguous reference to member 'decode(_:forKey:)'.
Edit:
Option 4: Using a Generic Type:
struct S<T: P>: Decodable {
let id: String
let instanceOfProtocol: T
}
This is really nice, because synthesis of Decodable works again!
However, now we have to know what type T will be, because the decoding site now requires a type:
try JSONDecoder().decode(S<A>.self, from: data)
try JSONDecoder().decode(S<B>.self, from: data)
In our use case, we can't know what the type will be before, so we'd have to check here again...
Use generic type:
struct S<T: P>: Decodable {
let id: String
let instanceOfProtocol: T
}
Remember Protocol is not a Type! And Swift is strongly typed language. So it MUST know the type of all objects at first place even though the actual type is not exposable to the caller of the object.

Parse JSON in Swift 24hours of problems [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 4 years ago.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Improve this question
I am running low on energy and time complete this project now, I have done everything I can to learn and understand and implement a solution with no success, I would really like some help with this. Anyone got any ideas for implementing a solution?
I am really struggling to parse a json file into my iOS app in Swift.
I have a JSON output from an SQL db. I think the formatting is wrong and the data headers are a bit ambiguous but my developer who I hired to create the process is happy with the output. Link below.
So in the last 3 days I have read all of the Apple Developer materials on parsing JSON, also watched numerous tutorials which all cover pretty much the same data, and I don't know how much I have read and converted into actual projects which have all failed. I am really needing some guidance here. I have even in a desperate attempt to understand my problem from a different perspective I have looked at how it is done in other languages.
This is the latest iteration:
import UIKit
import Foundation
struct ScheduleList: Codable {
let data: [Datum]
enum CodingKeys: String, CodingKey {
case data = "data"
}
}
struct Datum: Codable {
let date: String
let message: String
let notice: String
enum CodingKeys: String, CodingKey {
case date = "date"
case message = "message"
case notice = "notice"
}
}
class VCMainViewController: UIViewController {
#IBOutlet weak var flightTable: UITableView!
#IBOutlet weak var date: UILabel!
#IBOutlet weak var route: UILabel!
#IBOutlet weak var std: UILabel!
#IBOutlet weak var sta: UILabel!
#IBOutlet weak var pax: UILabel!
let flight = [Datum]()
func parse() {
let jsonUrlString = "http://35.237.114.234/api/index.php?uid=Bx7faf08A9fYJ7bZCNMUX9EzxYN2"
guard let url = URL(string: jsonUrlString) else { return }
let task = URLSession.shared.scheduleListTask(with: url) { (scheduleList, response, error) in
guard let scheduleList = scheduleList, error == nil, response != nil else {
print("Something Wrong")
return
}
print("downlaoded")
do {
let decoder = JSONDecoder()
//Error Here: Cannot convert value of type 'ScheduleList' to expected argument type 'Data'
let downloadedFlights = try decoder.decode([scheduleList], from: scheduleList)
self.flight = downloadedFlights.date
} catch {
print("Something wrong after loading")
}
}
//I find I can print data / scheduleList here and it still returns correct data. So something above I guess is the problem.
task.resume()
/*
if scheduleList != nil {
print(scheduleList!)
} else {
print(error)
}
}
*/
}
override func viewDidLoad() {
super.viewDidLoad()
parse()
}
}
**Added full JSON file now due to issue with original post.
I think the structure is the problem.
Any advice where I am going wrong? I can't cast that as Data, the dataTask data is called scheduleList so I don't know why this won't accept it.
Thanks
Just change your last two characters from ], to ]}, and you'll have valid JSON.
It might even work :)
If it doesn't, please post back the exact error message, and the line it's failing on.
And again, having a JSON formatter/browser/validator handy is Good. I generally use a Notepad++ plugin. Here's a good on-line browser:
JSON formatter and validator
See also:
Stack Overflow: How to create a Minimal, Complete, and Verifiable example
The new way to handle JSON in Swift 4.1 is to use the Codable protocol (or its subvariants Encodable and Decodable). To go from a JSON string to actual objects, you should use Decodable.
//: Playground - noun: a place where people can play
import UIKit
import Foundation
struct Message: Decodable {
let date: String
let message: String
let notice: String
}
struct Flight: Decodable {
let flight: String
let aircraft: String
let operating: String
let type: String
let route: String
let std: String
let sta: String
let pax: String
let prm: String
let check: String
}
struct Response: Decodable {
let data: [Message]
let flights: [Flight]
let state: String
private enum CodingKeys: String, CodingKey {
case data, state
case flights = "flight" // Parses "flight" from the response to the flights property
}
}
// Get a String from your JSON and convert it to Data
let jsonString = "..."
let jsonData = jsonString.data(using .utf8)!
// Initialize the decoder
let decoder = JSONDecoder()
// Try to convert your JSON to objects
do {
let response = try decoder.decode(Response.self, from: jsonData)
print("Decoded successfully: \(response)")
// Access values as usual with a struct... well, this IS a Swift struct already
print("State: \(response.state)")
// etc...
} catch {
print("Error decoding: \(error)")
}
Check out Apple documentation here:
1) Encoding and Decoding Custom Types
2) JSONDecoder

Resources