Modify API to remove empty names.

I'm trying to find a way to remove instances with names as empty strings from my array that receives the data. how do I modify my filter to do that.      

Code Block
self.cPlayerArr = fillPlayers.filter(){
            $0.yahooName != ""
}


Code Block
   func parseJSON(completed: @escaping () -> ()) {
    let url = URL(string: "")
    let decoder = JSONDecoder()
    let appDelegate = UIApplication.shared.delegate as! AppDelegate //<- Intentionally using forced casting.
    decoder.userInfo[.managedObjectContext] = appDelegate.persistentContainer.viewContext
    URLSession.shared.dataTask(with: url!) { (data, response, error) in
       
      if error == nil {
        do {
          let fillPlayers = try decoder.decode([CurrentPlayers].self, from: data!)
          self.cPlayerArr = fillPlayers.filter(){
            $0.yahooName != ""
          }
          let json = try JSONSerialization.jsonObject(with: data!, options: [])
          print(json)
           
          DispatchQueue.main.async {
            completed()
          }
        } catch {
          print("JSON Error: ", error)
        }
      }
       
    }.resume()
  }


Code Block
extension CurrentPlayers {
  @nonobjc public class func fetchRequest() -> NSFetchRequest<CurrentPlayers> {
    return NSFetchRequest<CurrentPlayers>(entityName: "CurrentPlayers")
  }
  @NSManaged public var photoUrl: String?
  @NSManaged public var firstName: String?
  @NSManaged public var lastName: String?
  @NSManaged public var position: String?
  @NSManaged public var team: String?
  @NSManaged public var yahooName: String?
  @NSManaged public var status: String?
  @NSManaged public var jerseyNumber: Int64
}
extension CurrentPlayers : Identifiable {
}

Answered by OOPer in 668478022

This is an example of a json object,

Thanks for showing the example, but unfortunately, it is shown in plist-like format, not JSON text. That may lose some info compared to the original JSON text.
When you want to get the original JSON text, you may need to put a print statement after guard let data = data else {...}:
Code Block
//...
guard let data = data else {
print("data is nil")
return
}
print(String(data: data, encoding: .utf8) ?? "*Unknown encoding*")
//...



But as far as I can see, there is no entry for key JerseyNumber in the example.
Isn't it Jersey that you want to get as an Int64?
Code Block
struct ApiCurrentPlayers: Decodable {
//# Using Capitalized names to make this `Decodable` easily
var PhotoUrl: String?
var FirstName: String?
var LastName: String?
var Position: String?
var Team: String?
var YahooName: String?
var Status: String?
var Jersey: Int64? //# Fit the property name to the key name of the JSON
}
extension CurrentPlayers {
convenience init?(context: NSManagedObjectContext, apiCurrentPlayers: ApiCurrentPlayers) {
guard let yahooName = apiCurrentPlayers.YahooName else {
return nil
}
guard let jerseyNumber = apiCurrentPlayers.Jersey else { //<-
return nil
}
self.init(context: context)
self.photoUrl = apiCurrentPlayers.PhotoUrl ?? ""
self.firstName = apiCurrentPlayers.FirstName ?? ""
self.lastName = apiCurrentPlayers.LastName ?? ""
self.position = apiCurrentPlayers.Position ?? ""
self.team = apiCurrentPlayers.Team ?? ""
self.yahooName = yahooName
self.status = apiCurrentPlayers.Status ?? ""
self.jerseyNumber = jerseyNumber
}
}


What is the problem with your current code?

The property cPlayerArr would be filled with elements where yahooName is not an empty string.

I filter players based on a certain position using a segmented controller and I see that there are players that have no name in my collection view so I'm thinking since I used my original array to filter them that the problem must be with the API getting the empty names, the json has names with null.

so I'm thinking since I used my original array to filter them that the problem must be with the API getting the empty names, the json has names with null.

Sorry, I do not understand what you mean. Are you saying it is a problem of API calling and no need to filter any more?
Or do you still want to fix the behavior of filer?
It's an API issue I want to filter them when I parse the json.
I cannot be sure, but if the json has names with null is the problem, you can add another condition to reject it:
Code Block
self.cPlayerArr = fillPlayers.filter(){
$0.yahooName != nil && $0.yahooName != ""
}


Another way, avoid making your Core Data entity Codable and introduce some temporary type which is Codable:

ApiCurrentPlayers.swift

Code Block
struct ApiCurrentPlayers: Decodable {
//# Using Capitalized names to make this `Decodable` easily
var PhotoUrl: String?
var FirstName: String?
var LastName: String?
var Position: String?
var Team: String?
var YahooName: String?
var Status: String?
var JerseyNumber: Int64
}
extension CurrentPlayers {
//# Make this a failable initializer to reject some entities with nil name
convenience init?(context: NSManagedObjectContext, apiCurrentPlayers: ApiCurrentPlayers) {
//# Add more `let`s if you want to reject other nils
guard let yahooName = apiCurrentPlayers.YahooName else {
return nil
}
self.init(context: context)
self.photoUrl = apiCurrentPlayers.PhotoUrl ?? ""
self.firstName = apiCurrentPlayers.FirstName ?? ""
self.lastName = apiCurrentPlayers.LastName ?? ""
self.position = apiCurrentPlayers.Position ?? ""
self.team = apiCurrentPlayers.Team ?? ""
self.yahooName = yahooName
self.status = apiCurrentPlayers.Status ?? ""
self.jerseyNumber = apiCurrentPlayers.JerseyNumber
}
}


parseJSON

Code Block
func parseJSON(completed: @escaping () -> ()) {
let url = URL(string: "WebsiteLink")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error {
print(error)
return
}
guard let data = data else {
print("data is nil")
return
}
do {
let apiPlayers = try JSONDecoder().decode([ApiCurrentPlayers].self, from: data) //# Use `[ApiCurrentPlayers].self` here
//# ↓Convert `ApiCurrentPlayers` to `CurrentPlayers` and reject nil entities
self.cPlayerArr = apiPlayers.compactMap {CurrentPlayers(context: context, apiCurrentPlayers: $0)}
//let json = try JSONSerialization.jsonObject(with: data)
//print(json)
DispatchQueue.main.async {
completed()
}
} catch {
print("JSON Error: ", error)
}
}.resume()
}


Okay just one more thing, I'm trying to handle the nil values in my jerseyNumber but when I do my guard statement it somehow blocks the json from being parsed. Is there another way to deal with nil values from jerseyNumber?

Code Block
struct ApiCurrentPlayers: Decodable {
  var PhotoUrl: String?
  var FirstName: String?
  var LastName: String?
  var Position: String?
  var Team: String?
  var YahooName: String?
  var Status: String?
  var JerseyNumber: Int64?
}
extension CurrentPlayers {
  convenience init?(context: NSManagedObjectContext, apiCurrentPlayers: ApiCurrentPlayers) {
    guard let yahooName = apiCurrentPlayers.YahooName else {
      return nil
    }
guard apiCurrentPlayers.JerseyNumber != nil else {
      return nil
    }
    self.init(context: context)
    self.photoUrl = apiCurrentPlayers.PhotoUrl ?? ""
    self.firstName = apiCurrentPlayers.FirstName ?? ""
    self.lastName = apiCurrentPlayers.LastName ?? ""
    self.position = apiCurrentPlayers.Position ?? ""
    self.team = apiCurrentPlayers.Team ?? ""
    self.yahooName = yahooName
    self.status = apiCurrentPlayers.Status ?? ""
    self.jerseyNumber = apiCurrentPlayers.JerseyNumber!
  }
}



when I do my guard statement it somehow blocks the json from being parsed.

Sorry, I do not see what might be happening. can you show some example of the JSON as text?

And, this may not solve the blocks issue, but you should better use guard-let instead of != nil test and forced unwrapping !.
Code Block
convenience init?(context: NSManagedObjectContext, apiCurrentPlayers: ApiCurrentPlayers) {
guard let yahooName = apiCurrentPlayers.YahooName else {
return nil
}
guard let jerseyNumber = apiCurrentPlayers.JerseyNumber else {
return nil
}
self.init(context: context)
self.photoUrl = apiCurrentPlayers.PhotoUrl ?? ""
self.firstName = apiCurrentPlayers.FirstName ?? ""
self.lastName = apiCurrentPlayers.LastName ?? ""
self.position = apiCurrentPlayers.Position ?? ""
self.team = apiCurrentPlayers.Team ?? ""
self.yahooName = yahooName
self.status = apiCurrentPlayers.Status ?? ""
self.jerseyNumber = jerseyNumber
}


This is an example of a json object, I can print it but it just does not seem to parse anymore. I had to remove some url fields.

Code Block
 {
    BirthCity = Pittsburgh;
    BirthDate = "1993-07-11T00:00:00";
    BirthState = PA;
    Catches = "<null>";
    DepthChartOrder = 5;
    DepthChartPosition = C;
    DraftKingsName = "Vincent Trocheck";
    DraftKingsPlayerID = 607956;
    FanDuelName = "Vincent Trocheck";
    FanDuelPlayerID = 15283;
    FantasyAlarmPlayerID = 400963;
    FantasyDraftName = "Vincent Trocheck";
    FantasyDraftPlayerID = 1818113;
    FirstName = Vincent;
    GlobalTeamID = 30000009;
    Height = 70;
    InjuryBodyPart = Scrambled;
    InjuryNotes = Scrambled;
    InjuryStartDate = "2021-03-18T00:00:00";
    InjuryStatus = Scrambled;
    Jersey = 16;
    LastName = Trocheck;
    PlayerID = 30000226;
    Position = C;
    RotoWirePlayerID = 3784;
    RotoworldPlayerID = 3768;
    Shoots = R;
    SportRadarPlayerID = "16bf9f68-95f9-4789-a811-80fe6838e632";
    SportsDirectPlayerID = 4433;
    StatsPlayerID = 607956;
    Status = Active;
    Team = CAR;
    TeamID = 9;
    UsaTodayPlayerID = 8256334;
    Weight = 183;
    XmlTeamPlayerID = 19046;
    YahooName = "Vincent Trocheck";
    YahooPlayerID = 5431;
  }

Accepted Answer

This is an example of a json object,

Thanks for showing the example, but unfortunately, it is shown in plist-like format, not JSON text. That may lose some info compared to the original JSON text.
When you want to get the original JSON text, you may need to put a print statement after guard let data = data else {...}:
Code Block
//...
guard let data = data else {
print("data is nil")
return
}
print(String(data: data, encoding: .utf8) ?? "*Unknown encoding*")
//...



But as far as I can see, there is no entry for key JerseyNumber in the example.
Isn't it Jersey that you want to get as an Int64?
Code Block
struct ApiCurrentPlayers: Decodable {
//# Using Capitalized names to make this `Decodable` easily
var PhotoUrl: String?
var FirstName: String?
var LastName: String?
var Position: String?
var Team: String?
var YahooName: String?
var Status: String?
var Jersey: Int64? //# Fit the property name to the key name of the JSON
}
extension CurrentPlayers {
convenience init?(context: NSManagedObjectContext, apiCurrentPlayers: ApiCurrentPlayers) {
guard let yahooName = apiCurrentPlayers.YahooName else {
return nil
}
guard let jerseyNumber = apiCurrentPlayers.Jersey else { //<-
return nil
}
self.init(context: context)
self.photoUrl = apiCurrentPlayers.PhotoUrl ?? ""
self.firstName = apiCurrentPlayers.FirstName ?? ""
self.lastName = apiCurrentPlayers.LastName ?? ""
self.position = apiCurrentPlayers.Position ?? ""
self.team = apiCurrentPlayers.Team ?? ""
self.yahooName = yahooName
self.status = apiCurrentPlayers.Status ?? ""
self.jerseyNumber = jerseyNumber
}
}


Thanks for your help. I just noticed Jersey in the json it must of been changed recently.
Modify API to remove empty names.
 
 
Q