Swifty Coding程序员swift

跟着Alamofire(4.0.0)学Swift3(一)

2016-09-30  本文已影响7447人  纸简书生

最近一段时间搞得东西太多了。服务端Spring,Android入门。但是自己的老本行一直没有好好的整理过。加上现在Swift3已经出来了一段时间了。还是不能忘了老本行,为了顺应潮流前前后后看了不少关于Swift的。还是觉得要在成熟的项目中才能学到更多的东西。所以选择了Alamofire这个库作为学习材料。

文中难免有错,不喜勿喷!

跟着Alamofire(4.0.0)学Swift3(二)

枚举定义(AFError异常类型)

枚举感觉更像一个类。感觉失去当初熟悉的枚举的影子。四不像,可以定义方法,但是不能定义变量

enum CompassPoint{
    case North
    case Sourth
    case East
    case West
    //枚举中 可以定义方法
    func show(){
        print(self)
    }
}
// 定义枚举变量
var p = CompassPoint.North
// 类型标注之后 可以使用点来获取枚举值
var p2 : CompassPoint = .Sourth
p.show()
p2.show()

除此之外,在Alamofire中的枚举更是有点与众不同。可以在枚举里面定义其他枚举类型,并且枚举的case可以传递参数。异常类型可以直接通过throw抛出。

public enum AFError: Error {

    public enum ParameterEncodingFailureReason {
        case missingURL
        // 可以传递参数
        case jsonEncodingFailed(error: Error)
        case propertyListEncodingFailed(error: Error)
    }

   ...
   
   // 这才是真正的case
    case invalidURL(url: URLConvertible)
    case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
    case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
    case responseValidationFailed(reason: ResponseValidationFailureReason)
    case responseSerializationFailed(reason: ResponseSerializationFailureReason)
}

枚举可以扩展。并且可以在扩展里面给枚举添加属性值,通过判断当前枚举,返回相应的内容

extension AFError {
    /// Returns whether the AFError is an invalid URL error.
    public var isInvalidURLError: Bool {
        if case .invalidURL = self { return true }
        return false
    }

...
    /// `underlyingError` properties will contain the associated values.
    public var isResponseSerializationError: Bool {
        if case .responseSerializationFailed = self { return true }
        return false
    }
}

这里就是判断当前的错误是属于哪一种类型的错误。除此之外,由于扩展可以定义多个,那么就可以对某一类功能归类到统一扩展中。比如其中就定义了一个便捷属性的扩展。

extension AFError {
    /// The `URLConvertible` associated with the error.
    public var urlConvertible: URLConvertible? {
        switch self {
        case .invalidURL(let url):
            return url
        default:
            return nil
        }
    }

   ...

    /// The `String.Encoding` associated with a failed `.stringResponse()` call.
    public var failedStringEncoding: String.Encoding? {
        switch self {
        case .responseSerializationFailed(let reason):
            return reason.failedStringEncoding
        default:
            return nil
        }
    }
}

当然除了给AFError添加扩展之外,还为AFError内部枚举定了相应扩展。

extension AFError.ParameterEncodingFailureReason {
    var underlyingError: Error? {
        switch self {
        case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
            return error
        default:
            return nil
        }
    }
}

然后上上层直接调用underlyingError得到error

Summary

extension AFError {
/// The URLConvertible associated with the error.
public var urlConvertible: URLConvertible? {
switch self {
case .invalidURL(let url):
return url
default:
return nil
}
}
```
MARK: - Error Descriptions

```
// MARK: - Error Descriptions
extension AFError: LocalizedError {
public var errorDescription: String? {


    switch self {
    case .invalidURL(let url):
        return "URL is not valid: \(url)"
    case .parameterEncodingFailed(let reason):
        return reason.localizedDescription
    case .multipartEncodingFailed(let reason):
        return reason.localizedDescription
    case .responseValidationFailed(let reason):
        return reason.localizedDescription
    case .responseSerializationFailed(let reason):
        return reason.localizedDescription
    }
    

}
```

通知定义(Notifications)

定义的通知是一件比较简单的事情。一般情况下,在OC中我们会直接定义一个字符串来表示某种通知。通常情况下也没怎么把通知管理起来。比如:NSString *NTESNotificationLogout = @"NTESNotificationLogout";

Alamofire中定义的通知就感觉很正式了。首先是成了一个扩展的形式,把相关的通知都写在里面。然后使用结构体来包装通知。代码如下:

extension Notification.Name {
    public struct Task {
        public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
        
        public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")

        public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")

        public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
    }
}

注意定义的都是静态常量

相当于扩展了系统通知name。把通知名称都定义在里面。然后通过不同的结构体定义不同用途的通知。其实在OC中也可以这样做。只是平时很少这样写,这样写之后代码组织就更加优雅了。

OC中也有这样的属性。

@interface NSNotification : NSObject <NSCopying, NSCoding>

@property (readonly, copy) NSNotificationName name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;

Swift中是

open class NSNotification : NSObject, NSCopying, NSCoding {
    open var name: NSNotification.Name { get }
    open var object: Any? { get }
    open var userInfo: [AnyHashable : Any]? { get }

使用的地方写法:

 NotificationCenter.default.post(
            name: Notification.Name.Task.DidResume,
            object: self,
            userInfo: [Notification.Key.Task: task]
        )

Summary

参数编码(ParameterEncoding)

再看枚举定义

枚举继承的类就是case所对应的类型呢。比如:

public enum HTTPMethod: String {
    case options = "OPTIONS"
    ...
    case connect = "CONNECT"
}

HTTPMethod继承自String,表示case所定义的就是字符串类型。如果改为int。就会出现:

这里的String。其实就是rawType

typealias

通过typealias就是指给一个类型取一个别名。比如public typealias Parameters = [String: Any]Parameters就是一个字典,key为字符串,value可以是任意类型

throws

Swift 2中所有的同步 Cocoa APINSError 都已经被 throw 关键字取代。这就很尴尬了,关于这个的用法可以参考这里Swift 2 throws 全解析 - 从原理到实践当然最好的还是直接看苹果的文档Page
Error Handling

结构体实现协议

异常

看一段函数

 public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()

        guard let parameters = parameters else { return urlRequest }

        if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
            guard let url = urlRequest.url else {
                throw AFError.parameterEncodingFailed(reason: .missingURL)
            }

            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                urlComponents.percentEncodedQuery = percentEncodedQuery
                urlRequest.url = urlComponents.url
            }
        } else {
            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
        }

        return urlRequest
    }

try的使用。如果方法有throws。那就就可以通过在调用的时候加上try来捕获异常。有一种场景比如不处理异常,我非常确定某个方法或者函数虽然声明会抛出异常,但是我自己知道我在使用时候是绝对不会抛出任何异常的。这种情况下 我们可以使用 try!。try! functionThrowErrorNil()

??问号,表示如果前面表达式为空,则就用后面的作为返回值。比如HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET")

if let和 guard else的使用。抛出异常直接就用throw抛出异常。千万要记住,if 后面不一定只是let。还有跟var。其本质就是变量和常量而已。比如上面代码中的if let method = HTTPMethod(rawValue: urlRequest.httpMethod ?? "GET")if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty

其他

} catch {
print("read content fail")
}
```
里的 try 关键字是写在具体调用代码行上面的。也就是说,那个语句会有可能抛出异常,我们才在哪个语句前面加上 try 关键字。这种方式有一个好处。就是我们可以一目了然的看到那些代码会抛出异常。而不是将所有代码都混在 try-catch 语句块中。

结果(Result)

这个类是一个泛型枚举。一开始我还在想结果不就成或者失败没。为什么还要高这么多。通过对结果的封装(Swift的枚举相当强大)可以直接获取到更加详细的信息。来看代码:

public enum Result<Value> {
    case success(Value)
    case failure(Error)

// 对结果信息进一步处理,可以马上返回成功或者失败。
    public var isSuccess: Bool {
        switch self {
        case .success:
            return true
        case .failure:
            return false
        }
    }

    public var isFailure: Bool {
        return !isSuccess
    }
    
// 对结果信息进一步处理,还可以直接返回成功的值。    
    public var value: Value? {
        switch self {
        case .success(let value):
            return value
        case .failure:
            return nil
        }
    }
    
    public var error: Error? {
        switch self {
        case .success:
            return nil
        case .failure(let error):
            return error
        }
    }
}

Swift的枚举比较高级的用法如上。

extension Result: CustomStringConvertible {
    /// The textual representation used when written to an output stream, which includes whether the result was a
    /// success or failure.
    public var description: String {
        switch self {
        case .success:
            return "SUCCESS"
        case .failure:
            return "FAILURE"
        }
    }
}

Request(请求)

一上来先定义一套协议,和类型别名(类型别名)。如下:

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

// 类似于block
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void

public protocol RequestRetrier {
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
}

protocol TaskConvertible {
    func task(session: URLSession, adapter: RequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
}

名字取得好就是好。见名知意

@discardableResult
open func authenticate(
user: String,
password: String,
persistence: URLCredential.Persistence = .forSession)
-> Self
{
let credential = URLCredential(user: user, password: password, persistence: persistence)
return authenticate(usingCredential: credential)
}
```

func hostFunc(@noescape closure: () -> ()) -> Void {
//以下编译出错, closure 被修饰后, 不能被其他异步线程捕获
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
closure()
}
}


### 参考

[Swift 2.0初探:值得注意的新特性](http://www.cocoachina.com/swift/20150623/12231.html)

[关于 Swift 2.0 - 语言新特性与革新](http://www.cnblogs.com/theswiftworld/p/swift2.html?utm_source=tuicool&utm_medium=referral)

[Swift 2.0 异常处理](http://www.jianshu.com/p/96a7db3fde00)

[Swift 3那些不同以往的特性](http://www.jianshu.com/p/5d911fae5b2f)

[iOS开发系列--Swift 3.0](http://www.cnblogs.com/kenshincui/p/5594951.html)
上一篇下一篇

猜你喜欢

热点阅读