@mattt의 튜토리얼 인 README를 몇 개 읽었지만 몇 가지를 알아낼 수 없습니다.
-
URLRequestConvertible
실제 API에서 올바른 사용법은 무엇입니까 ?URLRequestConvertible
모든 API에 대한 프로토콜을 구현하여 하나의 라우터를 만들면 거의 읽을 수없는 것 같습니다. 엔드 포인트 당 하나의 라우터를 만들어야합니까? -
두 번째 질문은 Swift 언어에 대한 경험 부족으로 인한 것 같습니다.
enum
라우터 구축에 사용되는 이유를 알 수 없습니까? 정적 메서드에 클래스를 사용하지 않는 이유는 무엇입니까? 여기에 예가 있습니다 (Alamofire의 README에서)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 } }
-
매개 변수를 전달하는 방법에는 두 가지가 있습니다.
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는 예제에서 첫 번째를 사용하고 있습니다. 그러나 이는 라우터 외부 (예 : UIViewControllers)에서 “하드 코딩”매개 변수 이름으로 이어질 것입니다. 매개 변수 이름을 잘못 입력하면 오류가 발생할 수 있습니다.
다른 사람들은 두 번째 옵션을 사용하고 있지만이 경우 각 매개 변수가 무엇을 나타내는 지 전혀 명확하지 않습니다.
이를 수행하는 올바른 방법은 무엇입니까?
답변
좋은 질문입니다. 각각을 개별적으로 분석합시다.
실제 API에서 URLRequestConvertible의 올바른 사용법은 무엇입니까?
URLRequestConvertible
프로토콜은 유효을 만들 수 지정된 객체를 보장하는 경량의 방법입니다 NSURLRequest
. 이 프로토콜을 특정 방식으로 사용하도록 강요하는 엄격한 규칙이나 지침은 없습니다. 다른 개체가 NSURLRequest
. Alamofire와 관련된 더 많은 정보는 여기 에서 찾을 수 있습니다 .
엔드 포인트 당 하나의 라우터를 만들어야합니까?
절대 아니다. 그것은 Enum
. Swift Enum 객체는 놀랍도록 강력하여 많은 양의 공통 상태를 공유하고 실제로 다른 부분을 켤 수 있습니다. NSURLRequest
다음과 같이 간단한 것을 만들 수 있다는 것은 정말 강력합니다!
let URLRequest: NSURLRequest = Router.ReadUser("cnoon")
라우터 구축에 enum이 사용되는 이유를 알 수 없습니까? 정적 메서드에 클래스를 사용하지 않는 이유는 무엇입니까?
열거 형은 공통 인터페이스에서 여러 관련 객체를 훨씬 더 간결하게 표현하기 때문에 사용됩니다. 모든 방법은 모든 케이스간에 공유됩니다. 정적 메서드를 사용한 경우 각 메서드에 대한 각 경우에 대한 정적 메서드가 있어야합니다. 또는 개체 내부에 Obj-C 스타일 열거 형을 사용해야합니다. 여기 제가 의미하는 바에 대한 간단한 예가 있습니다.
enum Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
case CreateUser([String: AnyObject])
case ReadUser(String)
case UpdateUser(String, [String: AnyObject])
case DestroyUser(String)
var method: Alamofire.HTTPMethod {
switch self {
case .CreateUser:
return .post
case .ReadUser:
return .get
case .UpdateUser:
return .put
case .DestroyUser:
return .delete
}
}
var path: String {
switch self {
case .CreateUser:
return "/users"
case .ReadUser(let username):
return "/users/\(username)"
case .UpdateUser(let username, _):
return "/users/\(username)"
case .DestroyUser(let username):
return "/users/\(username)"
}
}
}
다른 끝점의 메서드를 가져 오려면 매개 변수를 전달하지 않고도 동일한 메서드를 호출하여 찾고있는 끝점 유형을 정의 할 수 있습니다. 선택한 경우에 이미 처리되어 있습니다.
let createUserMethod = Router.CreateUser.method
let updateUserMethod = Router.UpdateUser.method
또는 경로를 얻으려면 동일한 유형의 호출.
let updateUserPath = Router.UpdateUser.path
let destroyUserPath = Router.DestroyUser.path
이제 정적 메서드를 사용하여 동일한 접근 방식을 시도해 보겠습니다.
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 pathForUpdateUser: String {
// this is going to get really repetitive for each case for each method
return "/update/user/path"
}
// This approach gets sloppy pretty quickly
}
참고 : 케이스를 전환하는 속성이나 함수가 많지 않은 경우 열거 형은 구조체에 비해 많은 이점을 제공하지 않습니다. 다른 구문 설탕을 사용하는 대체 접근 방식입니다.
열거 형은 상태 및 코드 재사용을 극대화 할 수 있습니다. 관련 값을 사용하면 다소 유사하지만 NSURLRequest
생성 과 같이 매우 다른 요구 사항이있는 개체를 그룹화하는 것과 같은 매우 강력한 작업을 수행 할 수도 있습니다 .
가독성을 높이기 위해 열거 형 케이스의 매개 변수를 구성하는 올바른 방법은 무엇입니까? (이걸 함께 으깨 야 했어)
대단한 질문입니다. 이미 두 가지 가능한 옵션을 마련했습니다. 귀하의 필요에 더 잘 맞는 1/3을 추가하겠습니다.
case CreateUser(username: String, firstName: String, lastName: String, email: String)
case ReadUser(username: String)
case UpdateUser(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에서 세 가지 옵션이며 사용 사례에 따라 올바른 옵션이 사용됩니다.
최신 정보
?? Alamofire 4.0 ??의 출시와 함께, URLRequestConvertible
이제 훨씬 더 똑똑해지고 던질 수도 있습니다. 유효하지 않은 요청을 처리하고 응답 핸들러를 통해 현명한 오류를 생성하기 위해 Alamofire에 완전한 지원을 추가했습니다. 이 새로운 시스템은 README 에 자세히 설명되어 있습니다.
답변
다음은 Alamofire의 Githubenum Router
에서 권장하는 Swift 3의 최신 버전 입니다 . .NET을 사용하여 라우터를 올바르게 구현하는 방법 측면에서 유용하다는 것을 알기를 바랍니다 .URLRequestConvertible
import Alamofire
enum Router: URLRequestConvertible
{
case createUser(parameters: Parameters)
case readUser(username: String)
case updateUser(username: String, parameters: Parameters)
case destroyUser(username: String)
static let baseURLString = "https://example.com"
var method: HTTPMethod
{
switch self {
case .createUser:
return .post
case .readUser:
return .get
case .updateUser:
return .put
case .destroyUser:
return .delete
}
}
var path: String
{
switch self {
case .createUser:
return "/users"
case .readUser(let username):
return "/users/\(username)"
case .updateUser(let username, _):
return "/users/\(username)"
case .destroyUser(let username):
return "/users/\(username)"
}
}
// MARK: URLRequestConvertible
func asURLRequest() throws -> URLRequest
{
let url = try Router.baseURLString.asURL()
var urlRequest = URLRequest(url: url.appendingPathComponent(path))
urlRequest.httpMethod = method.rawValue
switch self {
case .createUser(let parameters):
urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
case .updateUser(_, let parameters):
urlRequest = try URLEncoding.default.encode(urlRequest, with: parameters)
default:
break
}
return urlRequest
}
}
답변
SweetRouter 를 사용해 보지 않겠습니까 ? 라우터를 선언 할 때 가지고있는 모든 상용구를 제거하는 데 도움이되며 여러 환경과 같은 것을 지원하며 코드를 실제로 읽을 수 있습니다.
다음은 스위트 라우터가있는 라우터의 예입니다.
struct Api: EndpointType {
enum Environment: EnvironmentType {
case localhost
case test
case production
var value: URL.Environment {
switch self {
case .localhost: return .localhost(8080)
case .test: return .init(IP(126, 251, 20, 32))
case .production: return .init(.https, "myproductionserver.com", 3000)
}
}
}
enum Route: RouteType {
case auth, me
case posts(for: Date)
var route: URL.Route {
switch self {
case .me: return .init(at: "me")
case .auth: return .init(at: "auth")
case let .posts(for: date):
return URL.Route(at: "posts").query(("date", date), ("userId", "someId"))
}
}
}
static let current: Environment = .localhost
}
사용 방법은 다음과 같습니다.
Alamofire.request(Router<Api>(at: .me))
Alamofire.request(Router<Api>(.test, at: .auth))
Alamofire.request(Router<Api>(.production, at: .posts(for: Date())))
답변
작업 방법을 찾았고 라우터가있는 클래스를 만들었습니다. 요청에서 클래스 상속
request.swift 파일
class request{
func login(user: String, password: String){
/*use Router.login(params)*/
}
/*...*/
enum Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
static let OAuthToken: String?
case Login([String: AnyObject])
/*...*/
var method: Alamofire.Method {
switch self {
case .Login:
return .POST
/*...*/
}
var path: String {
switch self {
case .Login:
return "/login"
/*...*/
}
}
var URLRequest: NSURLRequest {
switch self {
case .Login(let parameters):
return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0
/*...*/
default:
return mutableURLRequest
}
}
}
}
requestContacts.swift 파일
class requestContacts: api{
func getUser(id: String){
/*use Router.getUser(id)*/
}
/*...*/
enum Router: URLRequestConvertible {
case getUser(id: String)
case setUser([String: AnyObject])
var method: Alamofire.Method {
switch self {
case .getUser:
return .GET
case .setUser:
return .POST
/*...*/
}
}
var path: String {
switch self {
case .getUser(id: String):
return "/user\(id)/"
case .setUser(id: String):
return "/user/"
/*...*/
}
}
// MARK: URLRequestConvertible
var URLRequest: NSURLRequest {
//use same baseURLString seted before
let URL = NSURL(string: Router.baseURLString)!
let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path))
mutableURLRequest.HTTPMethod = method.rawValue
if let token = Router.OAuthToken {
mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
switch self {
/*...*/
case .setUser(let parameters):
return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0
default: //for GET methods, that doesent need more
return mutableURLRequest
}
}
}
}
그래서 아들 클래스는 부모로부터 Router의 매개 변수를 얻을 것이고, 당신은 어떤 아들에서도 Route.login을 사용할 수 있습니다. 여전히 짧은 URLRequest를 얻는 방법이 있는지 알지 못하므로 매개 변수를 반복해서 설정할 필요가 없습니다.
답변
URLRequestConvertible 프로토콜을 채택하는 유형을 사용하여 URL 요청을 구성 할 수 있습니다.
다음은 www.raywenderlich.com 에서 가져온 예입니다.
public enum ImaggaRouter : URLRequestConvertible{
static let baseURL = "http://api.imagga.com/v1"
static let authenticationToken = "XAFDSADGDFSG DAFGDSFGL"
case Content, Tags(String), Colors(String)
public var URLRequest: NSMutableURLRequest {
let result: (path: String, method: Alamofire.Method, parameters: [String: AnyObject]) = {
switch self {
case .Content:
return ("/content", .POST, [String: AnyObject]())
case .Tags(let contentID):
let params = [ "content" : contentID ]
return ("/tagging", .GET, params)
case .Colors(let contentID):
let params = [ "content" : contentID, "extract_object_colors" : NSNumber(int: 0) ]
return ("/colors", .GET, params)
}
}()
let URL = NSURL(string: ImaggaRouter.baseURL)!
let URLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(result.path))
URLRequest.HTTPMethod = result.method.rawValue
URLRequest.setValue(ImaggaRouter.authenticationToken, forHTTPHeaderField: "Authorization")
URLRequest.timeoutInterval = NSTimeInterval(10 * 1000)
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: result.parameters).0
}
}
이 ImmageRouter를 다음과 같이 사용할 수 있습니다.
Alamofire.request(ImaggaRouter.Tags(contentID))
.responseJSON{ response in
답변
대 / 소문자 대신 UpdateUser (username : String, firstName : String, lastName : String, email : String)
당신은 만들 것입니다
struct UserAttributes
{
let username: String
....
}
명명되지 않은 읽을 수없는 문자열의 클러스터 대신 매개 변수로 해당 모델 객체를 공급합니다.
case UpdateUser (매개 변수 : UserAttributes)