Skip to content

Commit

Permalink
fix: More defensive enum decoding (#1420)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien-coye authored Feb 4, 2025
2 parents 042e4fe + 0140d08 commit 0735319
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 23 deletions.
2 changes: 1 addition & 1 deletion kDrive/UI/Controller/Menu/StoreViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ final class StoreViewController: UICollectionViewController, SceneStateRestorabl

private var purchaseEnabled = true
private var items = Item.allItems
private lazy var selectedPackId = DrivePackId(rawValue: driveFileManager.drive.pack.id)
private lazy var selectedPackId = DrivePackId(rawValue: driveFileManager.drive.pack.name)
private var selectedStorage = 1
private var selectedPeriod = PeriodTab.yearly {
didSet { updateOffers() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class FileDetailActivityTableViewCell: InsetTableViewCell {
localizedKey = "fileDetailsActivityFileColorUpdate"
case .fileColorDelete:
localizedKey = "fileDetailsActivityFileColorDelete"
case .none:
case .unknown:
localizedKey = "fileActivityUnknown"
}
detailLabel.text = localizedKey.localized
Expand Down
14 changes: 9 additions & 5 deletions kDriveCore/Data/Models/Drive/Drive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public enum MaintenanceReason: String, PersistableEnum, Codable {
case demoEnd = "demo_end"
case invoiceOverdue = "invoice_overdue"
case technical
case unknown

public init(from decoder: any Decoder) throws {
let singleKeyContainer = try decoder.singleValueContainer()
let value = try singleKeyContainer.decode(String.self)

self = MaintenanceReason(rawValue: value) ?? .unknown
}
}

public final class DrivePreferences: EmbeddedObject, Codable {
Expand Down Expand Up @@ -134,11 +142,7 @@ public final class Drive: Object, Codable {
}

public var isFreePack: Bool {
guard let packId = pack.drivePackId else {
return false
}

return packId == .free
return pack.drivePackId == .free
}

public var isInTechnicalMaintenance: Bool {
Expand Down
27 changes: 17 additions & 10 deletions kDriveCore/Data/Models/Drive/DrivePack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,21 @@
import Foundation
import RealmSwift

public enum DrivePackId: Int {
case solo = 1
case team = 2
case pro = 3
case free = 6
case kSuiteStandard = 8
case kSuitePro = 11
case kSuiteEntreprise = 14
public enum DrivePackId: String {
case solo
case team
case pro
case free
case kSuiteStandard = "ksuite_standard"
case kSuitePro = "ksuite_pro"
case kSuiteEntreprise = "ksuite_entreprise"
case myKSuite = "my_ksuite"
case myKSuitePlus = "my_ksuite_plus"
case unknown

public init(apiRawValue: String) {
self = .init(rawValue: apiRawValue) ?? .unknown
}
}

public class DrivePack: EmbeddedObject, Codable {
Expand All @@ -39,8 +46,8 @@ public class DrivePack: EmbeddedObject, Codable {
}

/// Convenience enum bridge
public var drivePackId: DrivePackId? {
DrivePackId(rawValue: id)
public var drivePackId: DrivePackId {
DrivePackId(apiRawValue: name)
}

enum CodingKeys: String, CodingKey {
Expand Down
8 changes: 8 additions & 0 deletions kDriveCore/Data/Models/DriveUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ public enum DriveUserRole: String, Codable {
case admin
case user
case external
case unknown

public init(from decoder: any Decoder) throws {
let singleKeyContainer = try decoder.singleValueContainer()
let value = try singleKeyContainer.decode(String.self)

self = DriveUserRole(rawValue: value) ?? .unknown
}
}

public enum UserPermission: String, Codable, CaseIterable {
Expand Down
34 changes: 30 additions & 4 deletions kDriveCore/Data/Models/File.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ public enum ConvertedType: String, CaseIterable {
public static let ignoreThumbnailTypes = downloadableTypes
/// Documents that can be previewed by the OS but not necessarily handled by OnlyOffice (eg. .pages)
public static let documentTypes: Set<ConvertedType> = [.presentation, .spreadsheet, .text]

public init(apiRawValue: String) {
self = .init(rawValue: apiRawValue) ?? .unknown
}
}

/// Minimal data needed to query a PublicShare
Expand Down Expand Up @@ -321,7 +325,20 @@ public enum FileStatus: String {

public enum FileImportStatus: String, PersistableEnum, Codable {
// ⚠️ For some reason PersistableEnum breaks something with key decoding, that's why we are explicitly writing snake case
case waiting, inProgress = "in_progress", done, failed, canceling, canceled
case waiting
case inProgress = "in_progress"
case done
case failed
case canceling
case canceled
case unknown

public init(from decoder: any Decoder) throws {
let singleKeyContainer = try decoder.singleValueContainer()
let value = try singleKeyContainer.decode(String.self)

self = FileImportStatus(rawValue: value) ?? .unknown
}
}

public final class FileExternalImport: EmbeddedObject, Codable {
Expand Down Expand Up @@ -370,6 +387,15 @@ public enum FileSupportedBy: String, PersistableEnum, Codable {
case thumbnail
/// This file can be read by OnlyOffice
case onlyOffice = "onlyoffice"

case unknown

public init(from decoder: any Decoder) throws {
let singleKeyContainer = try decoder.singleValueContainer()
let value = try singleKeyContainer.decode(String.self)

self = FileSupportedBy(rawValue: value) ?? .unknown
}
}

public typealias FileCursor = String
Expand Down Expand Up @@ -666,7 +692,7 @@ public final class File: Object, Codable {
} else if isBookmark {
return .url
} else {
return ConvertedType(rawValue: extensionType ?? "") ?? .unknown
return ConvertedType(apiRawValue: extensionType ?? "")
}
}

Expand Down Expand Up @@ -840,9 +866,9 @@ public final class File: Object, Codable {
dropbox = try container.decodeIfPresent(DropBox.self, forKey: .dropbox)
externalImport = try container.decodeIfPresent(FileExternalImport.self, forKey: .externalImport)
size = try container.decodeIfPresent(Int.self, forKey: .size)
let rawSupportedBy = try container.decodeIfPresent([String].self, forKey: .supportedBy) ?? []
let rawSupportedBy = try container.decodeIfPresent([FileSupportedBy].self, forKey: .supportedBy) ?? []
supportedBy = MutableSet()
supportedBy.insert(objectsIn: rawSupportedBy.compactMap { FileSupportedBy(rawValue: $0) })
supportedBy.insert(objectsIn: rawSupportedBy.filter { $0 != .unknown })
extensionType = try container.decodeIfPresent(String.self, forKey: .extensionType)
version = try container.decodeIfPresent(FileVersion.self, forKey: .version)
conversion = try container.decodeIfPresent(FileConversion.self, forKey: .conversion)
Expand Down
9 changes: 7 additions & 2 deletions kDriveCore/Data/Models/FileActivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public enum FileActivityType: String, Codable, CaseIterable {
case collaborativeUserDelete = "collaborative_user_delete"
case fileColorUpdate = "file_color_update"
case fileColorDelete = "file_color_delete"
case unknown

public static let displayedFileActivities: [FileActivityType] = [
.fileCreate,
Expand Down Expand Up @@ -85,6 +86,10 @@ public enum FileActivityType: String, Codable, CaseIterable {
.fileColorUpdate,
.fileColorDelete
]

public init(apiRawValue: String) {
self = .init(rawValue: apiRawValue) ?? .unknown
}
}

public class FileActivity: Object, Decodable {
Expand All @@ -109,8 +114,8 @@ public class FileActivity: Object, Decodable {
public var mergedFileActivities: [FileActivity] = []

/// Activity type
public var action: FileActivityType? {
return FileActivityType(rawValue: rawAction)
public var action: FileActivityType {
return FileActivityType(apiRawValue: rawAction)
}

public var user: DriveUser? {
Expand Down

0 comments on commit 0735319

Please sign in to comment.