Skip to content

Conversation

@Ostrenkiy
Copy link
Contributor

Задача: #APPS-1830

Коротко для Release Notes, в формате «Сделали/Добавили/Исправили N»:
Рефакторинг слоя работы с сетью

Описание:

  • Для того, чтобы перестать использовать параметр headers в запросах, теперь используется класс ApiRequestRetrier, который отвечает за повторение запроса в случае, если мы поймали error, а также подставляет нужные авторизационные хедеры в запросы.

  • В APIEndpoint добавлены поля update: UpdateRequestMaker, delete: DeleteRequestMaker, create: CreateRequestMaker и retrieve: RetrieveRequestMaker, отвечающие за различные запросы к серверу.

  • Все checkToken() вызовы делаются теперь при запросах внутри RequestMaker классов.

  • Для фетча данных из БД добавлен класс DatabaseFetchService c generic методом fetchAsync<T: IDFetchable>(), который асинхронно фетчит нужные объекты из базы данных.

  • Протокол JSONInitializable переименован в JSONSerializable. Добавлен протокол IDFetchable для моделей, обновляющихся из базы данных при загрузке.

  • Некоторые API-классы пока не трогаем, так как там есть различные моменты, требующие большего дополнительного вмешательства.

  • Добавилось больше deprecated методов. Где была возможность, подменял реализацию методов на новую.

  • Еще много где есть комментарии, которые хорошо бы учесть при дальнейших попытках рефакторить все, что связано с Network слоем.

  • Остались еще классы, которые я не успел поменять, но я работаю над этим в поте лица.

@Ostrenkiy Ostrenkiy added the main label Mar 29, 2018
@Ostrenkiy Ostrenkiy added this to the 1.56 milestone Mar 29, 2018
@Ostrenkiy Ostrenkiy self-assigned this Mar 29, 2018
@Ostrenkiy Ostrenkiy requested a review from kvld March 29, 2018 18:26
kvld
kvld previously approved these changes Mar 30, 2018
Copy link
Contributor

@kvld kvld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В качестве небольшого вброса: может быть заюзать Codable из Swift 4 и повыпиливать SwiftyJSON?

}

func getObjectsByIds<T: JSONInitializable>(ids: [T.idType], updating: [T], headers: [String: String] = AuthInfo.shared.initialHTTPHeaders, printOutput: Bool = false) -> Promise<([T])> {
func getObjectsByIds<T: JSONSerializable>(ids: [T.idType], updating: [T], printOutput: Bool = false) -> Promise<([T])> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В этом методе мы и дёргаем API, и работаем с CoreData. Может вынести работу с кордатой в отдельный слой?
То есть, я предлагаю на этом уровне только работать с сетью и получать JSON, а мердж и создание сущностей делать в отдельном классе.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Конкретно про этот метод - думаю это просто в RetrieveRequestRetrier запилить.
Вынести работу с кордатой в отдельный слой обязательно надо, но на данном этапе это пока сложно и долго реализовывать. Думаю, постепенно будем переделывать все на работу с Plain Object-ами, да.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На самом деле, вообще во всех местах, которые относятся к работе с сетью.

if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 && request.retryCount == 0 {
checkToken().then {
completion(true, 0.0)
}.catch {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сломалось форматирование. Может нам завезти какой-нибудь code formatter перед коммитом?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SwiftLint есть, он просто на это не реагирует

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ок, но давай поправим всё же?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, пропустил

} else {
failure(SignInError.other(error: error, code: nil, message: nil))
}
}.catch { error in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тоже беда с форматированием (здесь и ниже).

}

static func fetchAsync(ids: [Int]) -> Promise<[Notification]> {
return DatabaseFetchService.fetchAsync(entityName: "Notification", ids: ids)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если правда, что реализация fetchAsync в разных сущностях отличается только разными entityName, то можно сделать дефолтную реализацию и передавать туда String(describing: self)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

отличная идея!

reject(error)
}
)
// This is a correct replacement after CoreData duplicates fix for this
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пока не очень понятно, бага это или нет, и на что она влияет. Если бага, то надо будет заменить то, что выше, на то, что сейчас закомменчено

@Ostrenkiy
Copy link
Contributor Author

Осталось только выпилить все ненужные вызовы checkToken() из не-network слоя и все готово

Copy link
Contributor

@kvld kvld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Некоторые вещи не стал комментировать по несколько раз, обрати внимание (force try, форматирование, обращение по несуществующему индексу, ...).

}

var json: JSON {
return []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А почему здесь []? Assignment не сериализуется по-другому?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так в очень многих сущностях. Будем переделывать по мере необходимости (нужно только для create и update запросов)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сейчас добавлю дефолтную реализацию

import SwiftyJSON
import CoreData

class DatabaseFetchService {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Файл AsyncFetchService.swift, а класс DatabaseFetchService.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Спасибо, подловил, я проверял твою внимательность :trollface:

let descriptor = NSSortDescriptor(key: "managedId", ascending: false)

let idPredicates = ids.map {
NSPredicate(format: "managedId == %@", $0 as? NSNumber ?? $0 as! String)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот это какая-то странная запись, особенно "хвост" с nil-coalescing. Почему мы значение типа T.idType force-кастуем в String, если нам вообще не известно, что это за тип и может ли он кастоваться?
Может в объявлении associatedtype idType стоит обозначить, что этот тип должен конвертиться в строку, реализуя какой-то протокол?

idType, кстати, неплохо бы переименовать во всех местах в IdType

func create(stepName: String, stepId: Int) -> Promise<Attempt> {
let attempt = Attempt(step: stepId)
return Promise { fulfill, reject in
create.request(requestEndpoint: "attempts", paramName: "attempt", creatingObject: attempt, withManager: manager).then {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем нам name, если мы передаем отдельно эндпойнт?
paramName может быть тоже стоит вынести в APIEndpoint (это ведь просто название сущности)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, от name надо избавляться, но пока есть проблемные классы с этим повременим

return id == json["id"].int
}

var json: JSON {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогично Assignment (и может быть дальше тоже есть такие места).


private func submit(reply: Reply, completion: @escaping (() -> Void), error errorHandler: @escaping ((String) -> Void)) {
let id = attempt!.id!
let id = attempt!.id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional attempt, на котором делаем force-unwrapping.

if updatingObject != nil {
updatingObject?.update(json: json[paramName].arrayValue[0])
} else {
fulfill(T(json: json[paramName].arrayValue[0]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А arrayValue может быть пустым?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Возможно. Надо будет это убрать при рефакторинге ошибок

import Foundation
import SwiftyJSON

class StepikModelView: JSONSerializable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А что раньше было вместо View? Ничего не было?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, ничего не было

lastName = json["last_name"].stringValue
}

init(sample: Bool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

старая фигня, надо убрать

}

//TODO: Add parameters
//TODO: This is never used, remove
Copy link
Contributor

@kvld kvld Apr 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Давай удалим?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

неплохая идея

Copy link
Contributor

@kvld kvld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Теперь хорошо, но нужно починить тесты падающие и после можно мерджить.

}
let response = response.response
retrieve(user: userId).then {
UserActivity in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserActivity -> userActivity

@Ostrenkiy Ostrenkiy merged commit e12221b into dev Apr 3, 2018
@Ostrenkiy Ostrenkiy deleted the feature/API-refactor branch April 3, 2018 10:14
@kvld kvld mentioned this pull request Apr 4, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants