使用 Alamofire JSOn 和 HTML 创建通用获取器

Creating generic fetcher using Alamofire JSOn and HTML

我正在尝试启动一个基于网络抓取的项目。我已经为 JSON 的不同平台设置了工具,我使用 SwiftyJSON,对于原始的 HTML,我使用 hpple。我的问题是我正在尝试为内容设置一些通用的 class,为内容的提取器设置一些通用的 class。由于每次操作都是这样,

登录 如果有用户名或密码,请提供。 如果它有验证码显示并使用结果 使用 Alamofire 获取数据 使用 JSON 或 HTML 抓取数据 填充内容 class.

我想知道是否有办法定义某种协议、枚举或通用模板,以便我可以为每个 class 定义那些不同的函数。我想如果我做不到这一点,我会一遍又一遍地编写相同的代码。这就是我想出的。如果您能帮助我正确设置,我将不胜感激。

enum Company:Int {
    case CNN
    case BBC
    case HN
    case SO 
    
    var captcha:Bool {
        switch self {
        case CNN:
            return false
        case BBC:
            return true
        case HN:
            return true
        case SO:
            return false
        }
    }
    var description:String {
        get {
            switch self {
            case CNN:
                return "CNN"
            case BBC:
                return "BBC"
            case HN:
                return "Hacker News"
            case SO:
                return "Stack Overflow"
            }
        }
    }
}

class Fetcher {
    var username:String?
    var password:String?
    var url:String
    var company:Company
    
    init(company: Company, url:String) {
        self.url = url
        self.company = company
    }
    
    init(company: Company, url:String,username:String,password:String) {
        self.url = url
        self.company = company
        self.username = username
        self.password = password
    }
    
    func login() {
        
        if username != nil {
           // login
        }
        if company.captcha {
            //show captcha
        }
    }
    
    func fetch(){
        
    }
    
    func populate() {
        
    }
}

class CNN: Fetcher {
    
    
}

好的,这是一个有趣的练习...

您真的只需要进一步构建您的 Company 枚举以使您的 Fetcher 更加抽象。这是一种只对您自己的方法稍作修改的方法,它应该会让您更接近您想要实现的目标。这是基于我之前对你的另一个 的回复。

公司

enum Company: Printable, URLRequestConvertible {
    case CNN, BBC, HN, SO

    var captcha: Bool {
        switch self {
        case CNN:
            return false
        case BBC:
            return true
        case HN:
            return true
        case SO:
            return false
        }
    }

    var credentials: (username: String, password: String)? {
        switch self {
        case CNN:
            return ("cnn_username", "cnn_password")
        case BBC:
            return nil
        case HN:
            return ("hn_username", "hn_password")
        default:
            return nil
        }
    }

    var description: String {
        switch self {
        case CNN:
            return "CNN"
        case BBC:
            return "BBC"
        case HN:
            return "Hacker News"
        case SO:
            return "Stack Overflow"
        }
    }

    var loginURLRequest: NSURLRequest {
        var URLString: String?

        switch self {
        case CNN:
            URLString = "cnn_login_url"
        case BBC:
            URLString = "bbc_login_url"
        case HN:
            URLString = "hn_login_url"
        case SO:
            URLString = "so_login_url"
        }

        return NSURLRequest(URL: NSURL(string: URLString!)!)
    }

    var URLRequest: NSURLRequest {
        var URLString: String?

        switch self {
        case CNN:
            URLString = "cnn_url"
        case BBC:
            URLString = "bbc_url"
        case HN:
            URLString = "hn_url"
        case SO:
            URLString = "so_url"
        }

        return NSURLRequest(URL: NSURL(string: URLString!)!)
    }
}

新闻

struct News {
    let title: String
    let content: String
    let date: NSDate
    let author: String
}

抓取器

class Fetcher {

    typealias FetchNewsSuccessHandler = [News] -> Void
    typealias FetchNewsFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void

    // MARK: - Fetch News Methods

    class func fetchNewsFromCompany(company: Company, success: FetchNewsSuccessHandler, failure: FetchNewsFailureHandler) {
        login(
            company: company,
            success: { apiKey in
                Fetcher.fetch(
                    company: company,
                    apiKey: apiKey,
                    success: { news in
                        success(news)
                    },
                    failure: { response, json, error in
                        failure(response, json, error)
                    }
                )
            },
            failure: { response, json, error in
                failure(response, json, error)
            }
        )
    }

    // MARK: - Private - Helper Methods

    private class func login(
        #company: Company,
        success: (String) -> Void,
        failure: (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void)
    {
        if company.captcha {
            // You'll need to figure this part out on your own. First off, I'm not really sure how you
            // would do it, and secondly, I think there may be legal implications of doing this.
        }

        let request = Alamofire.request(company.loginURLRequest)

        if let credentials = company.credentials {
            request.authenticate(username: credentials.username, password: credentials.password)
        }

        request.responseJSON { _, response, json, error in
            if let error = error {
                failure(response, json, error)
            } else {
                // NOTE: You'll need to parse here...I would suggest using SwiftyJSON
                let apiKey = "12345678"
                success(apiKey)
            }
        }
    }

    private class func fetch(
        #company: Company,
        apiKey: String,
        success: FetchNewsSuccessHandler,
        failure: FetchNewsFailureHandler)
    {
        let request = Alamofire.request(company.URLRequest)
        request.responseJSON { _, _, json, error in
            if let error = error {
                failure(response, json, error)
            } else {
                // NOTE: You'll need to parse here...I would suggest using SwiftyJSON
                let news = [News]()
                success(news)
            }
        }
    }
}

示例ViewController 调用 Fetcher

class SomeViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        Fetcher.fetchNewsFromCompany(
            Company.CNN,
            success: { newsList in
                for news in newsList {
                    println("\(news.title) - \(news.date)")
                }
            },
            failure { response, data, error in
                println("\(response) \(error)")
            }
        )
    }
}

通过允许 Company 对象流经您的 Fetcher,您永远不必在 Fetcher 中跟踪公司的状态。都可以直接存入Enum里面。

希望对您有所帮助。干杯。

这是我使用 alamofire 和 alamofire 对象映射器所做的: 第 1 步:创建符合 Mappable 协议的模态 classes。

class StoreListingModal: Mappable {
var store: [StoreModal]?
var status: String?
required init?(_ map: Map){

}

func mapping(map: Map) {
    store <- map["result"]
    status <- map["status"]
}
}

第 2 步:使用通用类型创建提取请求:

func getDataFromNetwork<T:Mappable>(urlString: String, completion: (T?, NSError?) -> Void) {
    Alamofire.request(.GET, urlString).responseObject { (response: Response<T, NSError>) in
        guard response.result.isSuccess else{
            print("Error while fetching: \(response.result.error)")
            completion(nil, response.result.error)
            return
        }
        if let responseObject = response.result.value{
            print(responseObject)
            completion(responseObject, nil)
        }
    }
}

第 3 步:现在您只需调用此获取函数即可。可以这样做:

self.getDataFromNetwork("your url string") { (userResponse:StoreListingModal?, error) in

    }

您不仅会得到您的响应对象,它还会映射到您的模式 class。