weareopensource/Swift

View on GitHub
waosSwift/modules/users/reactors/UserViewReactor.swift

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * Dependencies
 */

import UIKit
import ReactorKit

/**
 * Reactor
 */

final class UserViewReactor: Reactor {

    // MARK: Constants

    // user actions
    enum Action {
        // inputs
        case updateFirstName(String)
        case validateFirstName(String)
        case updateLastName(String)
        case validateLastName(String)
        case updateEmail(String)
        case validateEmail(String)
        // extra
        case updateBio(String)
        case validateBio(String)
        // avatar
        case updateAvatar(Data)
        case deleteAvatar
        // social Networks
        case updateInstagram(String?)
        case validateInstagram(String)
        case updateTwitter(String?)
        case validateTwitter(String)
        case updateFacebook(String?)
        case validateFacebook(String)
        // default
        case done
    }

    // state changes
    enum Mutation {
        // inputs
        case updateFirstName(String)
        case updateLastName(String)
        case updateEmail(String)
        // extra
        case updateBio(String)
        // avatar
        case updateAvatar(String)
        // social Networks
        case updateInstagram(String?)
        case updateTwitter(String?)
        case updateFacebook(String?)
        // default
        case dismiss
        case setRefreshing(Bool)
        case validationError(CustomError)
        case success(String)
        case error(CustomError)
    }

    // the current view state
    struct State {
        var user: User
        var isDismissed: Bool
        var isRefreshing: Bool
        var errors: [DisplayError]
        var error: DisplayError?

        init(user: User) {
            self.user = user
            self.isDismissed = false
            self.isRefreshing = false
            self.errors = []
        }
    }

    // MARK: Properties

    let provider: AppServicesProviderType
    let initialState: State

    // MARK: Initialization

    init(provider: AppServicesProviderType, user: User) {
        self.provider = provider
        self.initialState = State(user: user)
    }

    // MARK: Action -> Mutation (mutate() receives an Action and generates an Observable<Mutation>)

    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        // inputs
        case let .updateFirstName(firstName):
            return .just(.updateFirstName(firstName))
        case let .validateFirstName(section):
            switch currentState.user.validate(.firstname, section) {
            case .valid: return .just(.success("\(User.Validators.firstname)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        case let .updateLastName(lastName):
            return .just(.updateLastName(lastName))
        case let .validateLastName(section):
            switch currentState.user.validate(.lastname, section) {
            case .valid: return .just(.success("\(User.Validators.lastname)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        case let .updateEmail(email):
            return .just(.updateEmail(email))
        case let .validateEmail(section):
            switch currentState.user.validate(.email, section) {
            case .valid: return .just(.success("\(User.Validators.email)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        // extra
        case let .updateBio(bio):
            return .just(.updateBio(bio))
        case let .validateBio(section):
            switch currentState.user.validate(.bio, section) {
            case .valid: return .just(.success("\(User.Validators.bio)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        // avatar
        case let .updateAvatar(data):
            log.verbose("♻️ Action -> Mutation : update Avatar")
            return .concat([
                .just(.setRefreshing(true)),
                self.provider.userService
                    .updateAvatar(file: data, partName: "img", fileName: "test.\(data.imgExtension)", mimeType: data.mimeType)
                    .map { result in
                        switch result {
                        case let .success(result):
                            return .updateAvatar(result.data.avatar)
                        case let .error(err): return .error(err)
                        }
                },
                .just(.setRefreshing(false))
            ])
        case .deleteAvatar:
            log.verbose("♻️ Action -> Mutation : delete Avatar")
            return .concat([
                .just(.setRefreshing(true)),
                self.provider.userService
                    .deleteAvatar()
                    .map { result in
                        switch result {
                        case .success: return .success("avatar deleted")
                        case let .error(err): return .error(err)
                        }
                },
                .just(.setRefreshing(false))
            ])
        // social networks
        case let .updateInstagram(avatar):
            return .just(.updateInstagram(avatar))
        case let .validateInstagram(section):
            switch currentState.user.validate(.instagram, section) {
            case .valid: return .just(.success("\(User.Validators.instagram)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        case let .updateTwitter(firstName):
            return .just(.updateTwitter(firstName))
        case let .validateTwitter(section):
            switch currentState.user.validate(.twitter, section) {
            case .valid: return .just(.success("\(User.Validators.twitter)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        case let .updateFacebook(firstName):
            return .just(.updateFacebook(firstName))
        case let .validateFacebook(section):
            switch currentState.user.validate(.facebook, section) {
            case .valid: return .just(.success("\(User.Validators.facebook)"))
            case let .invalid(err): return .just(.validationError(err[0] as! CustomError))
            }
        // done
        case .done:
            return self.provider.userService
                .update(self.currentState.user)
                .map { result in
                    switch result {
                    case .success: return .dismiss
                    case let .error(err): return .error(err)
                    }
            }
        }
    }

    // MARK: Mutation -> State (reduce() generates a new State from a previous State and a Mutation)

    func reduce(state: State, mutation: Mutation) -> State {
        var state = state
        switch mutation {
        // inputs
        case let .updateFirstName(firstName):
            state.user.firstName = firstName
        case let .updateLastName(lastName):
            state.user.lastName = lastName
        case let .updateEmail(email):
            state.user.email = email
        // extra
        case let .updateBio(bio):
            state.user.bio = bio
        // avatar
        case let .updateAvatar(avatar):
            state.user.avatar = avatar
        // social networks
        case let .updateInstagram(instagram):
            if state.user.complementary != nil {
                if state.user.complementary!.socialNetworks != nil {
                    state.user.complementary?.socialNetworks?.instagram = instagram?.lowercased()
                } else {
                    state.user.complementary?.socialNetworks = SocialNetworks(instagram: instagram?.lowercased())
                }
            } else {
                state.user.complementary = Complementary(socialNetworks: SocialNetworks(instagram: instagram?.lowercased()))
            }
        case let .updateTwitter(twitter):
            if state.user.complementary != nil {
                if state.user.complementary!.socialNetworks != nil {
                    state.user.complementary?.socialNetworks?.twitter = twitter?.lowercased()
                } else {
                    state.user.complementary?.socialNetworks = SocialNetworks(twitter: twitter?.lowercased())
                }
            } else {
                state.user.complementary = Complementary(socialNetworks: SocialNetworks(twitter: twitter?.lowercased()))
            }
        case let .updateFacebook(facebook):
            if state.user.complementary != nil {
                if state.user.complementary!.socialNetworks != nil {
                    state.user.complementary?.socialNetworks?.facebook = facebook?.lowercased()
                } else {
                    state.user.complementary?.socialNetworks = SocialNetworks(facebook: facebook?.lowercased())
                }
            } else {
                state.user.complementary = Complementary(socialNetworks: SocialNetworks(facebook: facebook?.lowercased()))
            }
        // refreshing
        case let .setRefreshing(isRefreshing):
            log.verbose("♻️ Mutation -> State : setRefreshing")
            state.isRefreshing = isRefreshing
        // dissmiss
        case .dismiss:
            log.verbose("♻️ Mutation -> State : dismiss")
            state.isDismissed = true
            state.errors = []
        // success
        case let .success(success):
            log.verbose("♻️ Mutation -> State : succes \(success)")
            state.error = nil
            state.errors = purgeErrors(errors: state.errors, specificTitles: [success])
        // error
        case let .validationError(error):
            log.verbose("♻️ Mutation -> State : validation error \(error)")
            if state.errors.firstIndex(where: { $0.title == error.message }) == nil {
                state.errors.insert(getDisplayError(error, self.provider.preferencesService.isLogged), at: 0)
            }
        case let .error(error):
            log.verbose("♻️ Mutation -> State : error \(error)")
            let _error: DisplayError = getDisplayError(error, self.provider.preferencesService.isLogged)
            self.provider.preferencesService.isLogged = _error.code == 401 ? false : true
            state.error = _error
        }
        return state
    }

}