如何编写一个将字符串和泛型类型作为参数的函数?

时间:2016-06-03 02:56:03

标签: ios json swift generics

我正在使用名为Gloss的库来帮助解析JSON数据。结果我创建了Glossy:

类型的结构
struct LoyaltyCard: Glossy {
let id: Int

init?(json: JSON) {
    guard let __id: Int = "id" <~~ json
      else { return nil }
}

我有许多不同的Glossy结构,并希望将它们与字符串一起传递给函数但是我一直收到错误:“无法使用类型'的参数列表调用'getMemberInfo'(String,memberData:LoyaltyCard.Type) '“,这是我的函数的缩写版本:

 func getMemberInfo<T: Glossy> (memberDataRequest: String, memberData:T) {
 let urlAccess = "\(baseURL)/api/\(memberDataRequest)"

///////code////////////

let data = object as! NSData

              let jsonInfo: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.init(rawValue: 0))

              let jsonArray = jsonInfo as! NSArray
                  if let dict = jsonArray[0] as? JSON //This means as type Gloss
                      {
                    let loyaltyID= LoyaltyCard(json: dict)
                    print(loyaltyID?.id)

                  }
                }

如何使这项功能发挥作用?

1 个答案:

答案 0 :(得分:2)

我从您的代码示例和您的评论中推断出,您不一定要将Glossy类型传递给 getMemberInfo,但关键要求是您要执行网络请求并返回Glossy类型。

虽然我得到了你想要做的事情,但我个人会退出通用方法,只使用protocol extension。您最终会得到一个可以为任何Glossy类型调用的方法。如果此协议扩展方法返回类型Self,那么最终将返回您恰好称之为的Glossy类型。

首先,让我们退一步,明确Glossy协议可能是什么样子。至少,你有一些可用的初始化程序(以及你需要的其他类型):

protocol Glossy {
    init?(json: [String: AnyObject])
}

(注意,我没有使用JSON类型,但如果你愿意,可以随意使用。我个人只是使​​用Swift集合来解析JSON,但是做任何你想做的事。)

然后我在协议扩展中定义static方法来执行请求。以下方法使用NSURLSession,但如果您使用Alamofire或其他方法,基本想法是相同的:

extension Glossy {
    static func performMemberRequest(memberDataRequest: String, completionHandler:(Self?, ErrorType?) -> ()) -> NSURLSessionTask {
        let urlAccess = "\(baseURL)/api/\(memberDataRequest)"

        let request = NSMutableURLRequest(URL: NSURL(string: urlAccess)!)
        let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
            guard let data = data where error == nil else {
                completionHandler(nil, error)
                return
            }

            do {
                if let array = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [[String: AnyObject]],
                    let dictionary = array.first {
                    completionHandler(Self(json: dictionary), nil)
                } else {
                    completionHandler(nil, GlossyError.InvalidJSONError)
                }
            } catch let parseError {
                completionHandler(nil, parseError)
            }
        }

        task.resume()

        return task
    }
}

请注意,上述内容中存在一些值得注意的问题:

  • 网络请求应始终异步执行。因此,请使用completionHandler之类的异步模式,而不是尝试立即返回某个对象。

    如果您要返回任何内容,那么您应该返回的唯一内容是NSURLSessionTask对象,因此如果您需要该功能,调用者可以选择捕获并取消请求。

  • 我将方法的名称更改为更具描述性并符合Cocoa命名约定。

  • 顺便说一句,您的代码建议您的API返回一个数组,而您只是抓住第一个字典。这似乎是一种奇怪的模式,但我已经在上面跟着它了。如果你真的要返回一个数组,那么你会这样做,因为你考虑了一个你可以返回多个项目的场景。在这种情况下,我建议迭代整个数组并让完成处理程序返回[Self]?Glossy个对象的数组),而不仅仅是Self?(即只有第一个)。

    此外,我不会亲自建议将数组作为顶级项返回的结构。该Web服务如何报告错误?我有一个字典结构返回成功/失败和/或返回代码等。然后有一个专用的结果键,这将是你的结果数组。

    但我没有解决上述任何更广泛的API问题,而是遵循代码段中的模式。但这些是您可能想要考虑的因素。

  • 在我的示例中,我没有将这些completionHandler调用发送回主队列,但这通常是非常有用的模式(避免同步问题,UI更新等) )。这样做很简单,但我想保持上述相对简单。

但是,让我们从您的API等细节中剔除。让我们关注您希望在协议扩展中定义static方法的概念(因此可以从符合Glossy的任何类型调用它)。例如,我可以使用所需的初始化程序定义LoyaltyCard类:

struct LoyaltyCard: Glossy {
    let id: Int

    init?(json: [String: AnyObject]) {
        guard let id = json["id"] as? Int else {
            return nil
        }

        self.id = id
    }
}

完成所有这些后,我现在可以在static上调用Glossy LoyaltyCard协议扩展方法,例如:

LoyaltyCard.performMemberRequest(memberDataRequest) { loyaltyCard, error in
    guard let loyaltyCard = loyaltyCard where error == nil else {
        print(error)
        return
    }

    // do something with loyaltyCard here
    print(loyaltyCard)
}

// but don't use it here

那里有很多,但我不希望你迷失在细节中。但是我希望你在这里理解关键概念:不要将Glossy类型传递给你的方法,也不要使用泛型:而是使用协议扩展。并避免同步网络请求,因此请使用类似completionHandler模式的异步模式。