Now I want query Result with different object(using generic)
I create DataType enum to handle different status
enum DataType{
case Case
case Product
case Category
case Composition
}
and query fuction called query<T:Object>
func query<T:Object>(type: DataType,id: Int,complete: #escaping (_ success:Bool,_ results:Results<T>?,_ error:String?) -> ()) {
switch type {
case .Case:
guard let caseResult = realm.objects(Case.self).filter("id = \(id)") as? Results<T> else {
complete(false ,nil ,"Query case failed.")
return
}
complete(true ,caseResult,nil)
return
case .Category:
guard let categoryResult = realm.objects(Category.self).filter("id = \(id)") as? Results<T> else {
complete(false ,nil ,"Query category failed.")
return
}
complete(true ,categoryResult,nil)
return
case .Product:
guard let productResult = realm.objects(Product.self).filter("id = \(id)") as? Results<T> else {
complete(false ,nil ,"Query category failed.")
return
}
complete(true ,productResult,nil)
return
case .Composition:
guard let compositionResult = realm.objects(Composition.self).filter("id = \(id)") as? Results<T> else {
complete(false ,nil ,"Query category failed.")
return
}
complete(true ,compositionResult,nil)
return
}
}
But when I created this fuction, and Xcode said
Will never be execut
It means the closure can never execute from this fuction?
Can anyone help with this problem?Thanks!
Related
Im trying to follow the Documentation to pass the result of a promise to the next promise
https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#saving-previous-results
I keep getting
Contextual closure type '(_) throws -> _' expects 1 argument, but 2 were used in closure body
Here my Code
class func loginInOrSignup() -> Promise<PFUser> {
let lm = LoginManager()
lm.logOut()
return logInInBackground().then{user in
if user.isNew{
return getUserInfo().map{ ($0, user) }
}
else{
user.fetchIfNeededInBackground()
return Promise.value(user)
}
}.then{(userInfo, user) -> Promise<PFUser> in
let name = userInfo["name"] as! String
if let email = userInfo["email"] as? String {
user.email = email
}
let username = G8FacebookLogin.generateSuggestedUsername(name: name)
user.username = username
return Promise.value(user)
}
}
private class func logInInBackground() -> Promise<PFUser>{
return Promise {seal in
PFFacebookUtils.logInInBackground(withReadPermissions: ["public_profile", "email"]){user, error in
guard let user = user else {
seal.reject(error ?? AuthError.msg(reason: "Some FB Error"))
return
}
seal.fulfill(user)
}
}
}
private class func getUserInfo() -> Promise<Dictionary<String,Any>> {
return Promise {seal in
G8FacebookLogin.getUserInfo { (userDict, error) in
guard let userInfo = userDict else{
PFUser.logOut()
seal.reject(AuthError.loginFailed(reason: "no user Info retrieved"))
return
}
seal.fulfill(userInfo)
}
}
}
It's pretty much the same as the Snippet code in the documentation. I don't understand what is the right way to do it.
I have a few functions that are basically the same apart from a few variable names that they reference. I want to abstract the function so that I don't have to keep duplicating the code. This is an example function:
func listenToParticipantNumber() {
guard let reference = participantNumberReference else {
return
}
guard participantNumberListener == nil else {
return
}
participantNumberListener = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Int, RequestError>) in
guard let strongSelf = self else {
return
}
switch result {
case .success(let participantNumber):
strongSelf.participantNumber = participantNumber
case .failure:
break
}
}
}
In another function, I'd switch out participantNumberReference, participantNumber, and participantNumberListener for different variables (which are all private to my class), and the block return type of Int. But the core layout of the function would be the same.
How can I make this process cleaner to reuse this code rather than having to duplicate it? Is it possible to somehow use KeyPaths to reference different variables of my class?
It takes a lot of pre-amble to achieve this abstraction level, so you have to ask larger questions about What You're Really Trying To Do, and Is It Worth It, but here's an outline. The essence is to use a dictionary with properties instead of explicitly declared variables.
Now you can add future cases to PersonType and initialize them (which may be automate-able) and the function will apply to all of them regardless. PersonType could conceivably be declared outside the class to increase separation.
This is probably a better suited for Code Review, where you can post a lot more context.
enum PersonType { case Participant, case Leader, case Observer }
struct TrackedObject { var number: Int; var numberReference: ReferenceProtocol; var numberListener: ListenerProtocol; }
// Instead of 9 private variables, make a Dictionary of 3 objects each of
// which have 3 properties
private var persons: [ PersonType: TrackedObject ] = ...
func listenToPersonOfType(personType: PersonType) {
guard let reference = persons[personType].numberReference else {
return
}
guard persons[personType].numberListener == nil else {
return
}
persons[personType].numberListener = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Int, RequestError>) in
guard let strongSelf = self else {
return
}
switch result {
case .success(let number):
strongSelf.persons[personType].number = number
case .failure:
break
}
}
}
I think you're on the right road with a KeyPath. I would probably refactor this a little bit so that the listen method returns Listener? (whatever type that is), and hoist the "if listener is already set, don't do this," outside of this function.
That would leave you something along these lines:
func listen<Response>(reference: Int?, // Or whatever type this is
updating keyPath: WritableKeyPath<C, Response>,
completion: (Result<Response, Error>) -> Void) -> Listener? {
guard let reference = reference else { return nil }
return backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
guard var strongSelf = self else {
return
}
switch result {
case .success(let result):
strongSelf[keyPath: keyPath] = result
case .failure:
break
}
}
Or you can keep your existing structure with something like this:
func listen<Response>(reference: Int?,
updating keyPath: WritableKeyPath<C, Response>,
forListener listener: WritableKeyPath<C, Listener?>,
completion: (Result<Response, Error>) -> Void) {
guard let reference = reference else { return }
guard self[keyPath: listener] == nil else {
return
}
var mutableSelf = self
mutableSelf[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
guard var strongSelf = self else {
return
}
switch result {
case .success(let result):
strongSelf[keyPath: keyPath] = result
case .failure:
break
}
}
}
(C in all of this code is "this class." We can't yet write Self to mean "the current class.")
So I solved it like this:
private func listen<T>(
to property: ReferenceWritableKeyPath<LivestreamModel, T?>,
withReference propertyReference: ReferenceWritableKeyPath<LivestreamModel, String?>,
listener: ReferenceWritableKeyPath<LivestreamModel, DatabaseHandle?>) {
guard let reference = self[keyPath: propertyReference] else {
return
}
guard self[keyPath: listener] == nil else {
return
}
self[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<T, RequestError>) in
switch result {
case .success(let value):
self?[keyPath: property] = value
case .failure:
self?[keyPath: property] = nil
}
}
}
I actually did this before Rob replied, but I had to use ReferenceWritableKeyPath because it gives a warning that self has no subscript members when using WritableKeyPath.
can someone explain what is the error here ?
Member 'success' in 'SingleEvent<_>' produces result of type 'SingleEvent<Element>', but context expects 'SingleEvent<_>'
I return a Single either error or success with a generic type
if the result case is .success I want to pass JSON in the single event
this is the full code
import Foundation
import RxSwift
public class APIClient<T: APIProtocol> {
var disposeBag: DisposeBag = DisposeBag()
}
extension APIClient {
public func get<U>(apiService: T, key: String) -> Single<U> {
return Single.create { single in
let task = TaskRequestManager.run(apiService, completion: { result in
DispatchQueue.main.async {
switch result {
case .error(let error):
single(.error(error))
case .success(let json):
guard let JSON = json[key] as? [String: Any] else {
single(.error(ApiError.jsonParsingFailure))
return
}
single(.success(JSON))
}
}
})
task.resume()
return Disposables.create {
task.cancel()
}
}
}
}
Return type of get function is Single<U>, while calling of single(.success(JSON)) violates this by specifying concrete type Single<[String: Any]>. Either replace generic type U with Dictionary<String, Any> in get function, either make TaskRequestManager.run accept generic type.
change this
return Single.create { single in
into this
return Single<U>.create { single in
Reason For Post
There are so many different solutions & examples on how to build a proper networking layer, but every app has different constraints, and design decisions are made based on trade-offs, leaving me uncertain about the quality of code I've written. If there are any Anti-Patterns, redundancies, or flat out bad solutions within my code that I have overlooked or simply lacked the knowledge to address, please do critique. This is a project I'd like to add to my portfolio, so I'm posting it here to get eyes on it, with some advice/tips.
Thanks for your time in advanced!
Some characteristics of my networking layer that I think could raise eyebrows:
Method contains a GETALL case, to indicate a list of data that must be fetched. I have not seen this in any of the open source code I've read. Is this a code smell?
enum Method {
case GET
/// Indicates how JSON response should be handled differently to abastract a list of entities
case GETALL
case PUT
case DELETE
}
I've made it, so each Swift Entity conforms to JSONable protocol, meaning it can be initialized with json and converted to json.
protocol JSONable {
init?(json: [String: AnyObject])
func toJSON() -> Data?
}
JSONable in practice with one of my entities:
struct User {
var id: String
var name: String
var location: String
var rating: Double
var keywords: NSArray
var profileImageUrl: String
}
extension User: JSONable {
init?(json: [String : AnyObject]) {
guard let id = json[Constant.id] as? String, let name = json[Constant.name] as? String, let location = json[Constant.location] as? String, let rating = json[Constant.rating] as? Double, let keywords = json[Constant.keywords] as? NSArray, let profileImageUrl = json[Constant.profileImageUrl] as? String else {
return nil
}
self.init(id: id, name: name, location: location, rating: rating, keywords: keywords, profileImageUrl: profileImageUrl)
}
func toJSON() -> Data? {
let data: [String: Any] = [Constant.id: id, Constant.name: name, Constant.location: location, Constant.rating: rating, Constant.keywords: keywords, Constant.profileImageUrl: profileImageUrl]
let jsonData = try? JSONSerialization.data(withJSONObject: data, options: [])
return jsonData
}
}
This allows me to use generics to initialize all my entities in my client- FirebaseAPI, after I retrieve JSON response. I also haven't seen this technique in the code I've read.
In the code below, notice how GETALL is implemented to flatten the list of JSON objects. Should I have to do this at all? Is there a better way to handle any type of Json structure response?
AND Entities are initialized generically, and returned as an Observable ( Using RxSwift ).
Do you sense any code smells?
/// Responsible for Making actual API requests & Handling response
/// Returns an observable object that conforms to JSONable protocol.
/// Entities that confrom to JSONable just means they can be initialized with json & transformed from swift to JSON.
func rx_fireRequest<Entity: JSONable>(_ endpoint: FirebaseEndpoint, ofType _: Entity.Type ) -> Observable<[Entity]> {
return Observable.create { [weak self] observer in
self?.session.dataTask(with: endpoint.request, completionHandler: { (data, response, error) in
/// Parse response from request.
let parsedResponse = Parser(data: data, response: response, error: error)
.parse()
switch parsedResponse {
case .error(let error):
observer.onError(error)
return
case .success(let data):
var entities = [Entity]()
switch endpoint.method {
/// Flatten JSON strucuture to retrieve a list of entities.
/// Denoted by 'GETALL' method.
case .GETALL:
/// Key (underscored) is unique identifier for each entity
/// value is k/v pairs of entity attributes.
for (_, value) in data {
if let value = value as? [String: AnyObject], let entity = Entity(json: value) {
entities.append(entity)
}
}
/// Force downcast for generic type inference.
observer.onNext(entities as! [Entity])
//observer.onCompleted()
/// All other methods return JSON that can be used to initialize JSONable entities
default:
if let entity = Entity(json: data) {
observer.onNext([entity] as! [Entity])
//observer.onCompleted()
} else {
observer.onError(NetworkError.initializationFailure)
}
}
}
}).resume()
return Disposables.create()
}
}
}
I manage different endpoints like so:
enum FirebaseEndpoint {
case saveUser(data: [String: AnyObject])
case fetchUser(id: String)
case removeUser(id: String)
case saveItem(data: [String: AnyObject])
case fetchItem(id: String)
case fetchItems
case removeItem(id: String)
case saveMessage(data: [String: AnyObject])
case fetchMessages(chatroomId: String)
case removeMessage(id: String)
}
extension FirebaseEndpoint: Endpoint {
var base: String {
// Add this as a constant to APP Secrts struct & dont include secrets file when pushed to github.
return "https://AppName.firebaseio.com"
}
var path: String {
switch self {
case .saveUser(let data): return "/\(Constant.users)/\(data[Constant.id])"
case .fetchUser(let id): return "/\(Constant.users)/\(id)"
case .removeUser(let id): return "/\(Constant.users)/\(id)"
case .saveItem(let data): return "/\(Constant.items)/\(data[Constant.id])"
case .fetchItem(let id): return "/\(Constant.items)/\(id)"
case .fetchItems: return "/\(Constant.items)"
case .removeItem(let id): return "/\(Constant.items)/\(id)"
case .saveMessage(let data): return "/\(Constant.messages)/\(data[Constant.id])"
case .fetchMessages(let chatroomId): return "\(Constant.messages)/\(chatroomId)"
case .removeMessage(let id): return "/\(Constant.messages)/\(id)"
}
}
var method: Method {
switch self {
case .fetchUser, .fetchItem: return .GET
case .fetchItems, .fetchMessages: return .GETALL
case .saveUser, .saveItem, .saveMessage: return .PUT
case .removeUser, .removeItem, .removeMessage: return .DELETE
}
}
var body: [String : AnyObject]? {
switch self {
case .saveItem(let data), .saveUser(let data), .saveMessage(let data): return data
default: return nil
}
}
}
Last thing, I'd like someone with professional eyes to look at is, how I use MVVM. I make all network requests from view model, which comes out looking something like this:
struct SearchViewModel {
// Outputs
var collectionItems: Observable<[Item]>
var error: Observable<Error>
init(controlValue: Observable<Int>, api: FirebaseAPI, user: User) {
let serverItems = controlValue
.map { ItemCategory(rawValue: $0) }
.filter { $0 != nil }.map { $0! }
.flatMap { api.rx_fetchItems(for: user, category: $0)
.materialize()
}
.filter { !$0.isCompleted }
.shareReplayLatestWhileConnected()
collectionItems = serverItems.filter { $0.element != nil }.dematerialize()
error = serverItems.filter { $0.error != nil }.map { $0.error! }
}
}
In order to call api requests in a more expressive, formalized way, I am able to call api.rx_fetchItems(for:) inside flatmap above, because I extend FirebaseAPI to conform to FetchItemsAPI. I will probably have to follow the same pattern for most other requests.
extension FirebaseAPI: FetchItemsAPI {
// MARK: Fetch Items Protocol
func rx_fetchItems(for user: User, category: ItemCategory) -> Observable<[Item]> {
// fetched items returns all items in database as Observable<[Item]>
let fetchedItems = rx_fireRequest(.fetchItems, ofType: Item.self)
switch category {
case .Local:
let localItems = fetchedItems
.flatMapLatest { (itemList) -> Observable<[Item]> in
return self.rx_localItems(user: user, items: itemList)
}
return localItems
case .RecentlyAdded:
// Compare current date to creation date of item. If its within 24 hours, It makes the cut.
let recentlyAddedItems = fetchedItems
.flatMapLatest { (itemList) -> Observable<[Item]> in
return self.rx_recentlyAddedItems(items: itemList)
}
return recentlyAddedItems
case .Trending:
let trendingItems = fetchedItems
.flatMapLatest { (itemList) -> Observable<[Item]> in
return self.rx_trendingItems(items: itemList)
}
return trendingItems
default:
let stubItem = Item(id: "DEFAULT", createdById: "createdBy", creationDate: 1.3, expirationDate: 2.4, title: "title", price: 2, info: "info", imageUrl: "url", bidCount: 4, location: "LA")
return Observable.just([stubItem])
}
}
// MARK: Helper Methods
private func rx_localItems(user: User, items: [Item]) -> Observable<[Item]> {
return Observable<[Item]>.create { observer in
observer.onNext(items.filter { $0.location == user.location }) // LA Matches stubs in db
return Disposables.create()
}
}
func rx_recentlyAddedItems(items: [Item]) -> Observable<[Item]> {
return Observable<[Item]>.create { observer in
let recentItems = items
.filter {
let now = Date(timeIntervalSinceReferenceDate: 0)
let creationDate = Date(timeIntervalSince1970: $0.creationDate)
if let hoursAgo = now.offset(from: creationDate, units: [.hour], maxUnits: 1) {
return Int(hoursAgo)! < 24
} else {
return false
}
}
observer.onNext(recentItems)
return Disposables.create()
}
}
func rx_trendingItems(items: [Item]) -> Observable<[Item]> {
return Observable<[Item]>.create { observer in
observer.onNext(items.filter { $0.bidCount > 8 })
return Disposables.create()
}
}
}
I'm attempting to follow SOLID principles, and level up with RxSWift + MVVM, so I'm still unsure about the best OOP design for clean, maintainable code.
I'm trying to check whether the statusCode is equal to 2xx, however i'm not quite sure how to create this regex code for that type of error handling. Here is my code at the moment with a variable that retrieve the statusCode from header?
func SignUpWithPassword(email: String, password: String, delegate: CreateUserDelegate) {
testProvider.request(.SignIn(email, password), completion: { result in
switch result {
case let .Success(response):
let statusCode = response.statusCode
case let .Failure(error):
print(error)
let description = "Error! Please try again!"
delegate.CreateUser(didError: description)
}
})
delegate.CreateUser(didSucceed: SignUpUser(email: email, password: password))
}
I believe that NSHTTPURLResponse status code is an Int.
you should be able to use a ranged expression on this in your branching logic:
switch response.statusCode
{
case 200 ... 299 :
print("Success")
default:
print("Failure")
}
I'm using this enum:
enum HTTPStatusCodeGroup: Int {
case Info = 100
case Success = 200
case Redirect = 300
case Client = 400
case Server = 500
case Unknown = 999
init(code: Int) {
switch code {
case 100...199: self = .Info
case 200...299: self = .Success
case 300...399: self = .Redirect
case 400...499: self = .Client
case 500...599: self = .Server
default: self = .Unknown
}
}
}
In Swift, I have created the own Regax class.
// Custom Regex class
class Regex {
let internalExpression: NSRegularExpression
let pattern: String
init(_ pattern: String) {
self.pattern = pattern
do {
self.internalExpression = try NSRegularExpression(pattern: self.pattern, options: .CaseInsensitive)
} catch _ {
self.internalExpression = NSRegularExpression.init();
}
}
func test(input: String) -> Bool {
let matches = self.internalExpression.matchesInString(input, options: .WithTransparentBounds, range: NSMakeRange(0, input.characters.count)) as Array<NSTextCheckingResult>
return matches.count > 0
}
}
// Override operator
infix operator =~ {}
func =~ (input: NSInteger, pattern: String) -> Int {
return (Regex(pattern).test(String(input))) ? input : -1;
}
// Example add it viewDidLoad method
let statusCode = 200
switch statusCode
{
case statusCode =~ "2[0-9][0-9]" :
print("2[0-9][0-9] Success")
break
case statusCode =~ "3[0-9][0-9]" :
print("3[0-9][0-9] Success")
break
case statusCode =~ "4[0-9][0-9]" :
print("4[0-9][0-9] Success")
break
case statusCode =~ "5[0-9][0-9]" :
print("5[0-9][0-9] Success")
break
default:
print("Failure")
}