protocol MWAPIDelegate{
    
    typealias XMLResponse = XMLParser
    
    func didFailToConnectToEndpoint(withError error: Error)
    
    func didFailToGetXMLData(withHTTPResponse httpResponse: HTTPURLResponse)
    
    func didFailToSerializeXMLData(withHTTPResponse httpResponse: HTTPURLResponse)
    
    func didFailToSerializeXMLData(withError error: Error?)

    func didFinishReceivingHTTPResponse(withHTTPResponse httpResponse: HTTPURLResponse)
    
    func didFinishReceivingXMLData(withXMLResponse xmlResponse: XMLResponse, withHTTPResponse httpResponse: HTTPURLResponse)
    
    func didFinishReceivingXMLDocument(withXMLDocument xmlDocument: XMLDocument, withHTTPResponse httpResponse: HTTPURLResponse)
    
    func didFinishReceivingDictionaryEntry(withDictionaryEntry dictionaryEntry: MWDictionaryEntry, withHTTPResponse httpResponse: HTTPURLResponse)


    
}

	
	

  
class MWDictionaryEntry{
    
    static let kHeadWord = "headWord"
    static let kPronunciation = "pronunciation"
    static let kEtymology = "etymology"
    static let kAudioFilePath = "audioFilePath"
    static let kDefinitions = "definitions"
    static let kFunctionalLabel = "functionalLabel"
    
    /** Similar to an NSObject, where properties can be accessed through key-value coding **/
    
    var debugDescription: String{
        
        let headwordString = "The headword is: \(self.headWord)"
        let functionalLabelString = "The functional label is: \(self.functionalLabel)"
        let pronunciationString = "Pronunciation is: \(self.pronunciation)"
        let etymology = "Etymology is: \(self.etymology)"
        let definitions = (self.entryDict[MWDictionaryEntry.kDefinitions] as! [String]).reduce("Definitions include: ", { return "\($0)\n\($1)"})
        
        
        let audioFilePath = "Audio FilePath is: \(self.audioFilePath)"
        
        return "\n\(headwordString),\n\(functionalLabelString),\n\(etymology),\n\(pronunciationString),\n\(audioFilePath),\n\(definitions)"
    }
    
    var headWord: String{
        
        get{
            return self.entryDict[MWDictionaryEntry.kHeadWord] as! String
        }
        
        set{
            self.entryDict[MWDictionaryEntry.kHeadWord] = newValue
        }
        
    }
    
    var pronunciation: String{
        
        get{
            return self.entryDict[MWDictionaryEntry.kPronunciation] as! String
        }
        
        set{
            self.entryDict[MWDictionaryEntry.kPronunciation] = newValue
        }
        
    }
    
    var etymology: String{
        
        get{
            return self.entryDict[MWDictionaryEntry.kEtymology] as! String
        }
        
        set{
            self.entryDict[MWDictionaryEntry.kEtymology] = newValue
        }
        
    }
    
    
    var audioFilePath: String{
        
        get{
            return self.entryDict[MWDictionaryEntry.kAudioFilePath] as! String
        }
        
        set{
            self.entryDict[MWDictionaryEntry.kAudioFilePath] = newValue
        }
        
    }
    
    var functionalLabel: String{
        
        get{
            return self.entryDict[MWDictionaryEntry.kFunctionalLabel] as! String
        }
        
        set{
            self.entryDict[MWDictionaryEntry.kFunctionalLabel] = newValue
        }
        
    }
    
    var definitions: [String]{
        
        get{
            return self.entryDict[MWDictionaryEntry.kDefinitions] as! [String]
        }
        
        set{
            self.entryDict[MWDictionaryEntry.kDefinitions] = newValue
        }
        
    }
    
    func addDefinition(def: String){
        
        if var defArray = entryDict[MWDictionaryEntry.kDefinitions] as? [String]{
            defArray.append(def)

        }
    }
    
    var entryDict: [String: Any]!
    
    init(){
        
        entryDict = [
            MWDictionaryEntry.kHeadWord: String(),
            MWDictionaryEntry.kPronunciation: String(),
            MWDictionaryEntry.kEtymology: String(),
            MWDictionaryEntry.kAudioFilePath: String(),
            MWDictionaryEntry.kDefinitions: [String](),
            MWDictionaryEntry.kFunctionalLabel: String()
            
        ]
    }
    
  
    
}

  class MWAPIClient{


}

class MWAPIClient{
    static let sharedClient = MWAPIClient()
    
    /** Instance variables **/
    
    private var urlSession: URLSession!
    private var delegate: MWAPIDelegate?
    
    private var dictionaryEntry: MWDictionaryEntry?
    
    private  init(){

        urlSession = URLSession.shared
        delegate = self
        
        
    }
    
    func setOxfordDictionaryAPIClientDelegate(with mwapiDelegate: MWAPIDelegate){
        
        self.delegate = mwapiDelegate
        
    }
    
    func resetDefaultDelegate(){
        self.delegate = self
    }
}

       



extension MWAPIClient{

    
    func didFinishReceivingXMLDocument(withXMLDocument xmlDocument: XMLDocument, withHTTPResponse httpResponse: HTTPURLResponse){
        
        print("XML document received: \(xmlDocument.description)")
        
    }
    
    func didFinishReceivingDictionaryEntry(withDictionaryEntry dictionaryEntry: MWDictionaryEntry, withHTTPResponse httpResponse: HTTPURLResponse){
        
        print("Dictionary entry received: \(dictionaryEntry.debugDescription)")
        
    }
    
    
    func didFailToSerializeXMLData(withHTTPResponse httpResponse: HTTPURLResponse) {
        
    }
    
    func didFailToSerializeXMLData(withError error: Error?) {
        
    }
    
    func didFinishReceivingXMLData(withXMLResponse xmlResponse: MWAPIDelegate.XMLResponse, withHTTPResponse httpResponse: HTTPURLResponse) {
        
    }
    func didFailToGetXMLData(withHTTPResponse httpResponse: HTTPURLResponse) {
        
    }
    func didFailToConnectToEndpoint(withError error: Error) {
        
    }
    func didFinishReceivingHTTPResponse(withHTTPResponse httpResponse: HTTPURLResponse) {
        
    }
}

    

   
    private func getDictionaryObject(fromXMLNode entryNode: XMLNode) throws -> MWDictionaryEntry{
        
        let dictionaryEntry = MWDictionaryEntry()

        do{
            
            if let pronunication = try entryNode.nodes(forXPath: "//pr").first,let stringValue = pronunication.stringValue{
                
                dictionaryEntry.pronunciation = stringValue
            }
            
            if let functionalLabel = try entryNode.nodes(forXPath: "//fl").first,let stringValue = functionalLabel.stringValue{
                
                dictionaryEntry.functionalLabel = stringValue
            }
            
            if let soundFilePath = try entryNode.nodes(forXPath: "//wav").first,let stringValue = soundFilePath.stringValue{
                
                dictionaryEntry.audioFilePath = stringValue
            }
            
            if let headWord = try entryNode.nodes(forXPath: "//hw").first,let stringValue = headWord.stringValue{
                
                dictionaryEntry.headWord = stringValue
            }
            
            if let etymology = try entryNode.nodes(forXPath: "//et").first,let stringValue = etymology.stringValue{
                
                dictionaryEntry.etymology = stringValue
            }
            
            
            
            if let definitionNode = try entryNode.nodes(forXPath: "//def").first{
                
                let definitions: [String] = try definitionNode.nodes(forXPath: "//dt").map({
                    
                    (definitionNode: DDXMLNode) in
                    
                    return definitionNode.stringValue

                }).filter({ return $0 != nil}) as! [String]
                
                
                let processedDefinitions: [String] = definitions.map({
                    
                    var mutableDef = $0
                    
                    if(mutableDef.first! == ":"){
                        mutableDef.removeFirst()
                    }
                    
                    return mutableDef
                })
                
                dictionaryEntry.definitions = processedDefinitions
                    
    
            }
            
     
            return dictionaryEntry

        } catch _ as NSError{
            throw NSError(domain: "XMLParsingError: failed to convert the XML dictionary entry into a valid dictionary object", code: 0, userInfo: nil)
        }
        
      
    }



    /** Wrapper function for executing aynchronous download of JSON data from Oxford Dictionary API **/
    
    private func startDataTask(withURLRequest request: URLRequest){
        
        guard let delegate = self.delegate else {
            fatalError("Error: no delegate specified for Oxford API download task")
        }
        
        _ = self.urlSession.dataTask(with: request, completionHandler: { data, response, error in
            
            switch (data,response,error){
            case (.some,.some,.none),(.some,.some,.some): //Able to connect to the server, data received
                
                let httpResponse = (response! as! HTTPURLResponse)
                print("Connected to server with HTTP status code of: \(httpResponse.statusCode)")
                
                
                do{
                    
                   

                    let xmlDocument = try XMLDocument(data: data!, options: 0)
                    
                    delegate.didFinishReceivingXMLDocument(withXMLDocument: xmlDocument, withHTTPResponse: httpResponse)
                    
                    let nodes = try xmlDocument.nodes(forXPath: "//entry")
                
                    if let entryNode = nodes.first{
                        
                        let entry = try self.getDictionaryObject(fromXMLNode: entryNode)
                        
                        delegate.didFinishReceivingDictionaryEntry(withDictionaryEntry: entry, withHTTPResponse: httpResponse)

                    }
                 
                    
                    
    
                } catch let error as NSError{
                    print("Error occurred while attempting to serialize JSON data: \(error.localizedDescription)")
                    delegate.didFailToSerializeXMLData(withHTTPResponse: httpResponse)

                }
            
                break
            case (.none,.some,.none),(.none,.some,.some): //Able to connect to the server but failed to get data or received a bad HTTP Response
                if let httpResponse = (response as? HTTPURLResponse){
                    delegate.didFailToGetXMLData(withHTTPResponse: httpResponse)
                }
                break
            case (.none,.none,.some): //Unable to connect to the server, with an error received
                delegate.didFailToConnectToEndpoint(withError: error!)
                break
            case (.none,.none,.none): //Unable to connect to the server, no error received
                let error = NSError(domain: "Failed to get a response: Error occurred while attempting to connect to the server", code: 0, userInfo: nil)
                delegate.didFailToConnectToEndpoint(withError: error)
                break
            default:
                break
            }
            
        }).resume()
    }
    
    
  

    


  

    
  


  func downloadXMLData(forHeadWord headWord: String, hasRequestThesaurusAPI: Bool){
        
        let mwapiRequest = MWAPIRequest(withHeadWord: headWord, isThesaurusRequest: false)
        
        let apiRequest = mwapiRequest.generateURLRequest()
        
        self.startDataTask(withURLRequest: apiRequest)
    }
    
    
  
  

  Dictionary entry received: 
The headword is: love,
The functional label is: noun,
Etymology is: Middle English, from Old English lufu; akin to Old High German luba love, Old English lēof dear, Latin lubēre, libēre to please,
Pronunciation is: ˈləv,
Audio FilePath is: love0001.wav,
Definitions include: 
strong affection for another arising out of kinship or personal ties maternal love for a child
attraction based on sexual desire :affection and tenderness felt by lovers
affection based on admiration, benevolence, or common interests love for his old schoolmates
an assurance of affection give her my love
warm attachment, enthusiasm, or devotion love of the sea
the object of attachment, devotion, or admiration baseball was his first love
a beloved person :darling often used as a term of endearment
used as an informal term of address
 :unselfish loyal and benevolent concern for the good of another: as
the fatherly concern of God for humankind
brotherly concern for others 
a person's adoration of God
a god or personification of love
an amorous episode :love affair
the sexual embrace :copulation
a score of zero (as in tennis)
god
holding one's opponent scoreless in tennis
inspired by affection
to hold dear :cherish
to feel a lover's passion, devotion, or tenderness for
caress
to fondle amorously
to copulate with
to like or desire actively :take pleasure in loved to play the violin
to thrive in the rose loves sunlight
to feel affection or experience desire
a romantic attachment or episode between lovers
a lively enthusiasm America's love affair with baseball
tomato
beads worn as a symbol of love and peace
an illegitimate child
a meal eaten in common by a Christian congregation in token of brotherly love
a gathering held to promote reconciliation and good feeling or show someone affectionate honor
any of a genus (Eragrostis) of grasses that resemble the bluegrasses but have flattened spikelets and deciduous lemmas
fatty bulges along the sides of the body at the waist
a gathering of people especially for the expression of their mutual love


    

    






     

To continue, please click here

If you feel confused or are having trouble following, you can go back to the previous page or back to the table of contents table of contents.