0

I'm building a Swift app and testing in an Xcode Playground. Calling the NYTimes Search API and trying to store its response in a struct. The code executes cleanly and no errors appear (I am using a do, try, catch), but I cannot print any properties from the resulting object (print(json.status)).

My hunch is that something is fishy with this line but I'm not sure what since no errors are printing from the catch statement

let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in

Form the URL endpoint to make the API call:

func APICall() {
        
        let APIKey = "MY_API_KEY_GOES_HERE_BUT_IT'S_A_SECRET"
        let searchTerm = "A Super Bowl Sideshow: See the Ageless Man!"
        
        // Remove the spaces and convert them to percents
        guard let encodedSearchTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) 
           else {
              print("Error encoding search term in URL")
              return
        }
    
        let url = "https://api.nytimes.com/svc/search/v2/articlesearch.json?q=" + encodedSearchTerm + "&api-key=" + APIKey

        getData(from: url)
}

Data Task:

func getData(from url: String) {
    
    //I believe something is wrong with the following line but I'm not sure what it is
    let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
        
        guard let data = data, error == nil else {
            print("Error loading data")
            return
        }
        
        var result: NYTSearchResponse?
        
        do {
            
            result = try JSONDecoder().decode(NYTSearchResponse.self, from: data)
            
        } catch {
            print(error)
        }
        
        guard let json = result else {
            print("Error assigning result to json")
            return
        }
        
        //Try to print these from the resulting object but these commands do not print
        print(json.status)
        print(json.response.docs[0].abstract)
    })
    
    task.resume()
}

My NYTSearchResponse struct which mirrors the NYT API JSON response. It's pretty complicated, but I pasted the json response into https://app.quicktype.io/ to build the struct.

// MARK: - Welcome
struct NYTSearchResponse: Codable {
    let status, copyright: String
    let response: Response
}

// MARK: - Response
struct Response: Codable {
    let docs: [Doc]
    let meta: Meta
}

// MARK: - Doc
struct Doc: Codable {
    let abstract: String
    let webURL: String
    let snippet, leadParagraph, printSection, printPage: String
    let source: String
    let multimedia: [Multimedia]
    let headline: Headline
    let keywords: [Keyword]
    let pubDate: Date
    let documentType, newsDesk, sectionName, subsectionName: String
    let byline: Byline
    let typeOfMaterial, id: String
    let wordCount: Int
    let uri: String

    enum CodingKeys: String, CodingKey {
        case abstract
        case webURL = "web_url"
        case snippet
        case leadParagraph = "lead_paragraph"
        case printSection = "print_section"
        case printPage = "print_page"
        case source, multimedia, headline, keywords
        case pubDate = "pub_date"
        case documentType = "document_type"
        case newsDesk = "news_desk"
        case sectionName = "section_name"
        case subsectionName = "subsection_name"
        case byline
        case typeOfMaterial = "type_of_material"
        case id = "_id"
        case wordCount = "word_count"
        case uri
    }
}

// MARK: - Byline
struct Byline: Codable {
    let original: String
    let person: [Person]
    let organization: String?
}

// MARK: - Person
struct Person: Codable {
    let firstname: String
    let middlename: String?
    let lastname: String
    let qualifier, title: String?
    let role, organization: String
    let rank: Int
}

// MARK: - Headline
struct Headline: Codable {
    let main: String
    let kicker, contentKicker: String?
    let printHeadline: String
    let name, seo, sub: String?

    enum CodingKeys: String, CodingKey {
        case main, kicker
        case contentKicker = "content_kicker"
        case printHeadline = "print_headline"
        case name, seo, sub
    }
}

// MARK: - Keyword
struct Keyword: Codable {
    let name, value: String
    let rank: Int
    let major: String
}

// MARK: - Multimedia
struct Multimedia: Codable {
    let rank: Int
    let subtype: String
    let caption, credit: String?
    let type, url: String
    let height, width: Int
    let legacy: Legacy
    let subType, cropName: String

    enum CodingKeys: String, CodingKey {
        case rank, subtype, caption, credit, type, url, height, width, legacy, subType
        case cropName = "crop_name"
    }
}

// MARK: - Legacy
struct Legacy: Codable {
    let xlarge: String?
    let xlargewidth, xlargeheight: Int?
}

// MARK: - Meta
struct Meta: Codable {
    let hits, offset, time: Int
}
Natalie
  • 53
  • 8
  • So what actually prints? – matt Mar 09 '21 at 04:02
  • Also how do you know you are not printing empty strings? Why not print `json` itself and actually see what you’ve got? Or even use the debugger? – matt Mar 09 '21 at 04:16
  • @matt Nothing prints! Just tried to print(json). – Natalie Mar 09 '21 at 04:35
  • Also I'm using a playground in Xcode. When I print(url) in the APICall(), the url string shows to the right of the screen, but print(json) in getData() doesn't show any preview on the right column – Natalie Mar 09 '21 at 04:36
  • Try using a non playground. Playgrounds are not good for this sort of thing. – matt Mar 09 '21 at 04:37
  • @matt Thanks for the advice. It was totally the playground. – Natalie Mar 09 '21 at 06:00
  • Check out https://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground for some ideas on how to make waiting for asynchronous requests in playgrounds work. – Gereon Mar 10 '21 at 20:40

1 Answers1

1

I moved the code out of the playground and it works.

Natalie
  • 53
  • 8
  • The trouble is that the original question never mentioned the playground. – matt Mar 09 '21 at 06:04
  • Apologies, I'm pretty new to coding and very new to Xcode and StackOverflow. Will get better at everything! – Natalie Mar 09 '21 at 18:39
  • OK but what I'm suggesting is: let's edit the question to make the playground part of it. That will make this question-and-answer _useful_ to people. Otherwise this is just your personal blog about your personal adventures in programming, which is not a correct use of Stack Overflow. – matt Mar 09 '21 at 18:42
  • I understand. Made an edit to mention use of the playground in the original question. – Natalie Mar 09 '21 at 19:00
  • 1
    Yeah, now we're cooking. :) Maybe the playground could have been made to work by making it live longer; the problem here is that your code is asynchronous so the playground just stops before we reach the end of the code. But really it is better to develop in a real project, where you get the benefit of the debugger and other forms of sanity. Playgrounds are just for play. – matt Mar 09 '21 at 19:30
  • That makes sense. Thanks a ton for the tips and pointers. – Natalie Mar 10 '21 at 18:32