快速枚举的实用案例-第2部分

在本文的第1部分中,我们已经看到了如何将枚举用作表视图的数据源。 如果您碰巧错过了第1部分,请检查一下。 如前所述,在这一部分中,我将展示网络堆栈中enum的另一个高级用例,我们将使用以下功能。

  1. 泛型
  2. 关联值

网络堆栈的组件:

以下是支持基本get,post请求的任何网络堆栈的组件。

  • 基本网址
  • 每个端点相对于基本网址的Api路径
  • 请求类型(Http方法的类型)
  • 网路错误
  • api响应
  • 最后,一个处理所有网络请求的类(我们将其称为RequestManager)。

让我们看看如何实现上述每个组件。

基本网址:

我们定义了一个相对于所有其他api端点相对的基本url字符串,并将其包装在一个结构中。

  struct BaseURL { 
静态让url =“ https://example.com:7979/mobile-app/”
}

Api路径:

要实现Api端点,我们可以根据需要以两种方式使用枚举。

在api端点只是静态字符串路径的简单情况下,我们可以使用基于字符串的枚举,如下所示。

 枚举ApiPath:String { 
案例书=“书籍/全部书籍”
案例类别=“书籍/类别”
}

但是,当我们需要基于用户输入添加动态路径参数时,这将不起作用。 例如,我们可能想根据用户选择的类别列出书籍,并且api端点如下所示。

  case booksForCategory =“书籍/类别/ 5” 

由于此类别ID将随用户选择而变化,因此我们可以包含一个具有用户所选类别ID的变量,然后使用字符串插值将其添加到api路径。

  case booksForCategory =“书籍/类别/ \(类别)” 

为了使上面的语句真正起作用,我们需要将类别变量注入枚举实例。 因此,我们将在swift enum🙌中使用关联的值。 但是有一个陷阱,如果尝试将关联值添加到枚举,则编译器会抱怨说不允许其同时具有关联值和字符串原始值。 因此,正如我在第1部分中解释的那样,我们将使该枚举符合CustomStringConvertible协议。 现在我们也可以拥有关联的值! 代码如下。

 枚举ApiPath:CustomStringConvertible { 
案例书
案例类别
案例书ForCategory(字符串)
  //符合CustomStringConvertible协议 
  var description:String { 
切换自我{
案例书:
返回“ books / allBooks”
案例.categories:
返回“书籍/类别”
案例.booksForCategory(让categoryId):
返回“ books / categories / \(categoryId)”
}
}
}

请求类型:

同样,我们将使用基于字符串的枚举,其中包含常用的HTTP方法类型。

 枚举RequestType:String { 
case get =“ GET”
案例发布=“ POST”
大小写=“ PUT”
case delete =“删除”
}

网路错误:

对于网络错误,我们将使用与ApiPath相同的方法。 实现如下所示。

 枚举NetworkingError:CustomStringConvertible { 
 大小写自定义(消息:字符串) 
案例号InternetConnection
案例通用
案例failedWithStatusCode(statusCode:Int)
  var description:字符串{ 
 切换自我{ 
  case .noInternetConnection: 
 返回“请检查您的Internet连接。” 
 案例.custom(让消息发送): 
 返回讯息 
 大小写 
         返回“存在技术问题。如果问题仍然存在,请与我们联系。” 
 案例.failedWithStatusCode(让代码): 
 返回“失败,状态码为\(code)” 
  } 
  } 
  } 

api响应:

假设我们有一个对象(Api控制器),该对象处理特定的api请求并向我们返回响应。 让我们将此响应称为ServiceResponse。 如果我们从高层次看,ServiceResponse对于每个API都应该有两个可能的结果。 他们是,

  1. 成功
  2. 失败

成功后,我们需要响应数据,该数据是JSON数据或强类型的Model对象(例如:本例中的book对象)。 出现故障时,我们需要一条可以显示给用户的消息。

再次,我们可以使用一个枚举来工作……这一次,我们将在枚举中使用泛型。 该代码在下面的代码段中给出。

 列举ServiceResponse  { 
成功案例(T)
案例失败(NetworkingError)
}

我们成功使用泛型的原因是因为,这使我们能够在任何Api中使用此ServiceResponse并根据需要成功传递任何类型或对象。

如果失败,我们传递的关联值为NetworkingError,它也是一个枚举。

要求管理员:

现在,我们需要将所有部分放在一起,这使我们进入了RequestManager类。 此类充当整个应用程序中所有网络请求的网关。 事不宜迟,让我们继续实施它。

  RequestManager类{ 
  //单例实现 
静态让共享= RequestManager()
私人init(){}
  func request(_ type:RequestType,apiPath:ApiPath,queryParameters:String?= nil,完成:@转义(ServiceResponse )->()){ 
 保护Reachability.isConnectedToNetwork()其他{ 
完成(.failure(.noInternetConnection))
返回
}
 让urlString = BaseURL.url + apiPath.description +(queryParameters ??“”) 
让url:URL = URL(string:urlString)!
让会话= URLSession.shared
  var request = URLRequest(url:url) 
request.httpMethod = type.rawValue

session.dataTask(with:request){(data,response,error)in

//检查所有条件并相应返回
........
//成功
完成(.success(json))
  //失败时 
完成(.failure(.generic))
  } 

在上面的代码中,我们已将ServiceResponse的通用类型指定为JSON **,例如ServiceResponse 。 由于这是所有不同的Api控制器都将使用的通用方法,因此我们只需返回json响应,然后让api控制器根据需要将json映射到合适的Model类型。

**这是代表SwiftyJSON中的JSON对象的类型,它是最流行的json解析库之一。

用法:

最后,让我们实现一个示例Api控制器,在这种情况下,是一个BooksApiController,它会向我们返回给定类别的所有书籍的列表。

为此,我们首先需要定义我们的书本模型,与下面的代码相似。

 结构书{ 
变量名称:字符串
var id:String
var author:String
var价格:双倍
var category:String
}

现在我们已经准备好Book模型,让我们回到实现BooksApiController的角度。

 类BooksApiController { 

有趣的getBooksForCategory(_类别:字符串,_完成:@转义(ServiceResponse )->()){

RequestManager.shared.request(.get,apiPath:.booksForCategory(category)){(响应)在
 切换响应{ 
案例.success(let json):
var books = [Book]()
  json.arrayValue中的bookRaw { 
do {//将原始json映射到模型
let book:Book =试试JSONDecoder.decode(json:bookRaw)
books.append(book)
} {
打印(error.localizedDescription)
}
}
 完成(。成功(书本)) 
//返回模型数组,在这种情况下为Book
 案例。失败(让错误): 
 完成(.failure(错误)) 
//只传递错误,以便可以将其显示给用户。
}
}
}

在上面的代码片段中,这次我们将通用参数类型传递为[Book],因为我们需要从此Api返回一个Book对象数组。 可以根据需要将其更改为任何其他类型。 上面的大多数代码是不言自明的,除了JSONDecoder只是一种对象映射器。 那可能是另一篇文章的主题。 那就是所有的人! 编码愉快!!

如果您喜欢阅读本文,请拍掌a,我很想听听您的反馈意见。