Tolle Fragen. Lassen Sie uns jeden einzeln aufschlüsseln.
Was ist die richtige Verwendung von URLRequestConvertible in der realen API?
Das URLRequestConvertible
Protokoll ist eine einfache Methode, um sicherzustellen, dass ein bestimmtes Objekt eine gültige erstellen kann NSURLRequest
. Es gibt nicht wirklich strenge Regeln oder Richtlinien, die Sie dazu zwingen, dieses Protokoll auf eine bestimmte Weise zu verwenden. Es ist lediglich ein Komfortprotokoll, mit dem andere Objekte den Status speichern können, der zum ordnungsgemäßen Erstellen des Status erforderlich ist NSURLRequest
. Weitere Informationen zu Alamofire finden Sie hier .
Sollte ich einen Router pro Endpunkt erstellen?
Definitiv nicht. Das würde den gesamten Zweck der Verwendung eines Enum
. Swift Enum-Objekte sind erstaunlich leistungsfähig, sodass Sie eine große Menge gemeinsamer Zustände gemeinsam nutzen und die Teile einschalten können, die sich tatsächlich unterscheiden. Es NSURLRequest
ist wirklich mächtig , mit etwas so Einfachem wie dem Folgenden etwas erstellen zu können !
let URLRequest: NSURLRequest = Router.ReadUser("cnoon")
Ich kann nicht herausfinden, warum enum zum Erstellen von Routern verwendet wird. Warum verwenden wir keine Klasse mit statischen Methoden?
Eine Aufzählung wird verwendet, da dies eine viel präzisere Möglichkeit ist, mehrere verwandte Objekte unter einer gemeinsamen Schnittstelle auszudrücken. Alle Methoden werden von allen Fällen gemeinsam genutzt. Wenn Sie statische Methoden verwenden, müssen Sie für jeden Fall für jede Methode eine statische Methode haben. Oder Sie müssten eine Aufzählung im Obj-C-Stil innerhalb des Objekts verwenden. Hier ist ein kurzes Beispiel dafür, was ich meine.
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)"
}
}
}
Um die Methode eines der verschiedenen Endpunkte abzurufen, können Sie dieselbe Methode aufrufen, ohne Parameter übergeben zu müssen, um zu definieren, nach welchem Endpunkttyp Sie suchen. Dies wird bereits von dem von Ihnen ausgewählten Fall behandelt.
let createUserMethod = Router.CreateUser.method
let updateUserMethod = Router.UpdateUser.method
Oder wenn Sie den Pfad erhalten möchten, die gleichen Arten von Anrufen.
let updateUserPath = Router.UpdateUser.path
let destroyUserPath = Router.DestroyUser.path
Versuchen wir nun den gleichen Ansatz mit statischen Methoden.
struct Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
static var method: Method {
}
static func methodForEndpoint(endpoint: String) -> Method {
}
static var path: String {
}
static func pathForEndpoint(endpoint: String) -> String {
}
static var pathForCreateUser: String {
return "/create/user/path"
}
static var pathForUpdateUser: String {
return "/update/user/path"
}
}
HINWEIS: Wenn Sie nicht viele Eigenschaften oder Funktionen haben, die die Fälle aktivieren, bietet eine Aufzählung nicht viele Vorteile gegenüber einer Struktur. Es ist einfach ein alternativer Ansatz mit unterschiedlichem syntaktischem Zucker.
Aufzählungen können die Wiederverwendung von Status und Code maximieren. Mit den zugehörigen Werten können Sie auch einige wirklich leistungsstarke Aufgaben ausführen, z. B. Objekte gruppieren, die etwas ähnlich sind, aber unglaublich unterschiedliche Anforderungen haben ... wie z. B. die NSURLRequest
Erstellung.
Was ist der richtige Weg, um Parameter für Enum-Fälle zu erstellen, um die Lesbarkeit zu verbessern? (musste dieses zusammen pürieren)
Das ist eine tolle Frage. Sie haben bereits zwei mögliche Optionen festgelegt. Lassen Sie mich ein Drittel hinzufügen, das Ihren Bedürfnissen etwas besser entspricht.
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)
In Fällen, in denen Sie Werte zugeordnet haben, kann es hilfreich sein, explizite Namen für alle Werte im Tupel hinzuzufügen. Dies hilft wirklich dabei, den Kontext aufzubauen. Der Nachteil ist, dass Sie diese Werte dann in Ihren switch-Anweisungen wie folgt neu deklarieren müssen.
static var method: String {
switch self {
case let CreateUser(username: username, firstName: firstName, lastName: lastName, email: email):
return "POST"
default:
return "GET"
}
}
Dies gibt Ihnen zwar einen schönen, konsistenten Kontext, wird aber ziemlich ausführlich. Dies sind derzeit Ihre drei Optionen in Swift. Welche Option die richtige ist, hängt von Ihrem Anwendungsfall ab.
Aktualisieren
Mit der Veröffentlichung von 🔥🔥 Alamofire 4.0 🔥🔥 URLRequestConvertible
kann das jetzt VIEL schlauer sein und auch werfen. Wir haben Alamofire voll unterstützt, um ungültige Anforderungen zu verarbeiten und über die Antworthandler sinnvolle Fehler zu generieren. Dieses neue System ist in unserer README ausführlich dokumentiert .
case
Aussagen. Das sieht für mich nach einer riesigen Methode aus. Ich bin nicht sicher, ob das zu dem lesbaren Code führen wird ...Router.createUser("[email protected]", "....")
und einen Block für die Interpretation der Ergebnisse für den Server haben. Alle Details (Methoden, Pfade, API-Root usw.) können für Router privat sein - das ist in Ordnung.Hier ist das aktuelle
enum Router
in Swift 3, das auf Alamofires Github empfohlen wird . Ich hoffe, Sie finden es nützlich, um einen Router ordnungsgemäß zu implementierenURLRequestConvertible
.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 } }
quelle
Warum versuchen Sie nicht verwenden SweetRouter . Es hilft Ihnen dabei, alle Boilerplates zu entfernen, die Sie beim Deklarieren eines Routers haben, und es unterstützt auch Dinge wie mehrere Umgebungen, und Ihr Code ist wirklich lesbar.
Hier ist ein Beispiel für den Router mit süßem Router:
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 }
Und so würden Sie es verwenden:
Alamofire.request(Router<Api>(at: .me)) Alamofire.request(Router<Api>(.test, at: .auth)) Alamofire.request(Router<Api>(.production, at: .posts(for: Date())))
quelle
Ich habe einen Weg gefunden, damit zu arbeiten. Ich habe eine Klasse mit dem Router darin erstellt: Klassen von einer Anfrage erben
Datei 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 } } } }
Datei 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 } } } }
Daher erhält die Sohnklasse vom übergeordneten Parameter die Router-Parameter, und Sie können Route.login sogar in jedem Sohn verwenden. Trotzdem weiß ich nicht, ob es eine Möglichkeit gibt, eine kurze URLRequest zu erhalten, daher muss ich keine Parameter immer wieder einstellen
quelle
mutableURLRequest.HTTPMethod = method.rawValue
ohne Änderungen ändern.Typen, die das URLRequestConvertible-Protokoll verwenden, können zum Erstellen von URL-Anforderungen verwendet werden.
Hier ist ein Beispiel von 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 } }
und wir können diesen ImmageRouter wie folgt verwenden:
Alamofire.request(ImaggaRouter.Tags(contentID)) .responseJSON{ response in
quelle
anstelle von case UpdateUser (Benutzername: String, Vorname: String, Nachname: String, E-Mail: String)
du würdest machen
struct UserAttributes { let username: String .... }
und füttere DIESES Modellobjekt als Parameter anstelle eines Clusters unbenannter unlesbarer Zeichenfolgen
case UpdateUser (Parameter: UserAttributes)
quelle