我读过@Mattt的几个教程和自述文件,但是我搞不懂几件事.

  1. 在现实世界的API中,URLRequestConvertible的正确用法是什么?看起来,如果我将通过为所有API实现URLRequestConvertible协议来创建一个路由,它将几乎不可读.我应该 for each 端点创建一个路由吗?

  2. 第二个问题很可能是因为缺乏使用Swift 语言的经验.我搞不懂为什么enum要用来建造路由?为什么我们不将类与静电方法一起使用呢? 下面是一个示例(摘自Alamofire的自述文件)

    enum Router: URLRequestConvertible {
        static let baseURLString = "http://example.com"
        static let perPage = 50
    
        case Search(query: String, page: Int)
    
        // MARK: URLRequestConvertible
    
        var URLRequest: NSURLRequest {
            let (path: String, parameters: [String: AnyObject]?) = {
                switch self {
                case .Search(let query, let page) where page > 1:
                    return ("/search", ["q": query, "offset": Router.perPage * page])
                case .Search(let query, _):
                    return ("/search", ["q": query])
                }
            }()
    
            let URL = NSURL(string: Router.baseURLString)!
            let URLRequest = NSURLRequest(URL: URL.URLByAppendingPathComponent(path))
            let encoding = Alamofire.ParameterEncoding.URL
    
            return encoding.encode(URLRequest, parameters: parameters).0
        }
    }
    
  3. 传递参数有两种方式:

    case CreateUser([String: AnyObject])
    case ReadUser(String)
    case UpdateUser(String, [String: AnyObject])
    case DestroyUser(String)
    

    和(假设用户有4个参数)

    case CreateUser(String, String, String, String)
    case ReadUser(String)
    case UpdateUser(String, String, String, String, String)
    case DestroyUser(String)
    

    @Mattt在示例中使用第一个.但这将导致在路由外部对参数名称进行"硬编码"(例如,在UIViewController中). 参数名称中的拼写错误可能会导致错误.
    其他人正在使用第二个选项,但在这种情况下,每个参数表示的含义一点也不明显.
    做这件事的正确方式是什么?

推荐答案

很好的问题.让我们逐一分析一下.

URLRequestConvertible在真实API中的正确用法是什么?

URLRequestConvertible协议是确保给定对象可以创建有效NSURLRequest的轻量级方式.实际上并没有一套严格的规则或指导方针强迫您以任何特定方式使用此协议.它只是一个方便的协议,允许其他对象存储正确创建NSURLRequest所需的状态.关于Alamofire的更多信息可以在here找到.

我应该 for each 端点创建一个路由吗?

绝对不是.这将 destruct 使用Enum的全部目的.Swift Enum对象功能强大,让您可以共享大量公共状态,并打开实际不同的部分.能够用以下简单的东西创建一个NSURLRequest真的很强大!

let URLRequest: NSURLRequest = Router.ReadUser("cnoon")

我搞不懂为什么要用枚举来构建路由?为什么我们不将类与静电方法一起使用呢?

之所以使用枚举,是因为它是在公共接口下表示多个相关对象的更简明的方式.所有的方法都是在所有 case 之间共享的.如果您使用静电方法,则必须 for each 方法的每个 case 使用一个静电方法.否则,您必须在对象内使用Obj-C样式的枚举.这里有一个简单的例子来说明我的意思.

enum Router: URLRequestConvertible {
    static let baseURLString = "http://example.com"

    case CreateUser([String: AnyObject])
    case ReadUser(String)
    case 更新User(String, [String: AnyObject])
    case DestroyUser(String)

    var method: Alamofire.HTTPMethod {
        switch self {
        case .CreateUser:
            return .post
        case .ReadUser:
            return .get
        case .更新User:
            return .put
        case .DestroyUser:
            return .delete
        }
    }

    var path: String {
        switch self {
        case .CreateUser:
            return "/users"
        case .ReadUser(let username):
            return "/users/\(username)"
        case .更新User(let username, _):
            return "/users/\(username)"
        case .DestroyUser(let username):
            return "/users/\(username)"
        }
    }
}

要获得任何不同端点的方法,您可以调用相同的方法,而不必传入任何参数来定义您要查找的端点类型,它已经由您 Select 的 case 处理.

let createUserMethod = Router.CreateUser.method
let updateUserMethod = Router.更新User.method

或者,如果您想获取路径,相同类型的呼叫.

let updateUserPath = Router.更新User.path
let destroyUserPath = Router.DestroyUser.path

现在,让我们使用静电方法try 相同的方法.

struct Router: URLRequestConvertible {
    static let baseURLString = "http://example.com"

    static var method: Method {
        // how do I pick which endpoint?
    }

    static func methodForEndpoint(endpoint: String) -> Method {
        // but then I have to pass in the endpoint each time
        // what if I use the wrong key?
        // possible solution...use an Obj-C style enum without functions?
        // best solution, merge both concepts and bingo, Swift enums emerge
    }

    static var path: String {
        // bummer...I have the same problem in this method too.
    }

    static func pathForEndpoint(endpoint: String) -> String {
        // I guess I could pass the endpoint key again?
    }

    static var pathForCreateUser: String {
        // I've got it, let's just create individual properties for each type
        return "/create/user/path"
    }

    static var pathFor更新User: String {
        // this is going to get really repetitive for each case for each method
        return "/update/user/path"
    }

    // This approach gets sloppy pretty quickly
}

注意:如果您没有太多的属性或函数来打开大小写,那么枚举就不会比 struct 有多大优势.这只是一种具有不同句法糖的替代方法.

枚举可以最大化状态和代码重用.关联的值还允许你做一些非常强大的事情,比如对有点相似但有着难以置信的不同需求的对象进行分组...比如NSURLRequest个创意.

为枚举用例构造参数以提高可读性的正确方法是什么?(我不得不把这一块捣碎在一起)

这是个很棒的问题.您已经列出了两种可能的 Select .我再加一个可能更适合您需要的第三个.

case CreateUser(username: String, firstName: String, lastName: String, email: String)
case ReadUser(username: String)
case 更新User(username: String, firstName: String, lastName: String, email: String)
case DestroyUser(username: String)

在有关联值的情况下,我认为为元组中的所有值添加显式名称可能会有所帮助.这确实有助于构建上下文.这样做的缺点是,您必须在switch语句中重新声明这些值.

static var method: String {
    switch self {
    case let CreateUser(username: username, firstName: firstName, lastName: lastName, email: email):
        return "POST"
    default:
        return "GET"
    }
}

虽然这为您提供了一个很好的、一致的上下文,但它变得相当冗长.这是您目前在SWIFT中的三个选项,哪一个是正确的取决于您的使用 case .


更新

随着?? 阿拉莫菲尔4.0??, URLRequestConvertible现在可以更加智能,也可以投掷.我们在Alamofire中添加了对处理无效请求和通过响应处理程序生成合理错误的全面支持.这个新系统在我们的README页中有详细的记录.

Ios相关问答推荐

使用UIGraphicsBeginIMAext创建新图像会扭曲图像 colored颜色

如何使用Swift以编程方式更改SVG文件中某些元素的 colored颜色

Xcode 15模拟器用x86_64编译

swiftui动画如何实时确定不对称过渡

在左侧显示多行值时会出现 SwiftUI 错误,这会 destruct 堆栈中右侧的对齐方式

一对多关系 Firebase 实时数据库(嵌入数百万条 comments )

小部件链接有效,但不执行任何操作

在视图控制器中实例化视图控制器是否可能/智能?

SwiftUI 图像与下拉视图切换一起 skip

如何在 SwiftUI 的 TabView 中添加底部曲线?

如何包含 Xcode 子项目标头?

如何根据设备时间设置 Flutter 应用时间线?

如何在保持纵横比的同时zoom UIView 的内容以适应目标矩形?

如何阻止图像在 UIImageView 中拉伸?

如何关闭情节提要弹出框

如何在 iOS 中判断暗模式?

NSURLSession:如何增加 URL 请求的超时时间?

判断从 JSON 字符串返回的 Objective-C 中的空值

我可以在 UIScrollView 中使用 UIRefreshControl 吗?

如何为 iOS 13 的 SF Symbols 设置权重?