Default decoding values

This commit is contained in:
Justin Mazzocchi 2020-08-23 16:39:52 -07:00
parent cabbe30c2f
commit 8ca6f43610
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
9 changed files with 149 additions and 33 deletions

View File

@ -15,6 +15,8 @@
D0091B6F24DD68090040E8D2 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6D24DD68090040E8D2 /* PreferencesView.swift */; }; D0091B6F24DD68090040E8D2 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B6D24DD68090040E8D2 /* PreferencesView.swift */; };
D0091B7124DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; }; D0091B7124DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; };
D0091B7224DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; }; D0091B7224DD68220040E8D2 /* PreferencesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */; };
D009CCF024F3260300F410E7 /* DecodableDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = D009CCEF24F3260300F410E7 /* DecodableDefault.swift */; };
D009CCF124F3260300F410E7 /* DecodableDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = D009CCEF24F3260300F410E7 /* DecodableDefault.swift */; };
D0159F8624DE742F00E78478 /* TabNavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0159F8324DE742F00E78478 /* TabNavigationViewModel.swift */; }; D0159F8624DE742F00E78478 /* TabNavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0159F8324DE742F00E78478 /* TabNavigationViewModel.swift */; };
D0159F8824DE742F00E78478 /* SecondaryNavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0159F8424DE742F00E78478 /* SecondaryNavigationViewModel.swift */; }; D0159F8824DE742F00E78478 /* SecondaryNavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0159F8424DE742F00E78478 /* SecondaryNavigationViewModel.swift */; };
D0159F8A24DE742F00E78478 /* IdentitiesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0159F8524DE742F00E78478 /* IdentitiesViewModel.swift */; }; D0159F8A24DE742F00E78478 /* IdentitiesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0159F8524DE742F00E78478 /* IdentitiesViewModel.swift */; };
@ -279,6 +281,7 @@
D0091B6A24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesViewModel.swift; sourceTree = "<group>"; }; D0091B6A24DC10CE0040E8D2 /* PostingReadingPreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingReadingPreferencesViewModel.swift; sourceTree = "<group>"; };
D0091B6D24DD68090040E8D2 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; }; D0091B6D24DD68090040E8D2 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewModel.swift; sourceTree = "<group>"; }; D0091B7024DD68220040E8D2 /* PreferencesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewModel.swift; sourceTree = "<group>"; };
D009CCEF24F3260300F410E7 /* DecodableDefault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableDefault.swift; sourceTree = "<group>"; };
D0159F8324DE742F00E78478 /* TabNavigationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabNavigationViewModel.swift; sourceTree = "<group>"; }; D0159F8324DE742F00E78478 /* TabNavigationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabNavigationViewModel.swift; sourceTree = "<group>"; };
D0159F8424DE742F00E78478 /* SecondaryNavigationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryNavigationViewModel.swift; sourceTree = "<group>"; }; D0159F8424DE742F00E78478 /* SecondaryNavigationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecondaryNavigationViewModel.swift; sourceTree = "<group>"; };
D0159F8524DE742F00E78478 /* IdentitiesViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentitiesViewModel.swift; sourceTree = "<group>"; }; D0159F8524DE742F00E78478 /* IdentitiesViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentitiesViewModel.swift; sourceTree = "<group>"; };
@ -447,6 +450,14 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
D009CCEE24F325AF00F410E7 /* Property Wrappers */ = {
isa = PBXGroup;
children = (
D009CCEF24F3260300F410E7 /* DecodableDefault.swift */,
);
path = "Property Wrappers";
sourceTree = "<group>";
};
D0159F7F24DE739000E78478 /* Views */ = { D0159F7F24DE739000E78478 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -564,6 +575,7 @@
D047FA8524C3E21000AF17C5 /* MetatextApp.swift */, D047FA8524C3E21000AF17C5 /* MetatextApp.swift */,
D0666A3A24C6B56200F3F04B /* Model */, D0666A3A24C6B56200F3F04B /* Model */,
D0DB6EFA24C5730600D965FE /* Networking */, D0DB6EFA24C5730600D965FE /* Networking */,
D009CCEE24F325AF00F410E7 /* Property Wrappers */,
D019E6F224DF7C9E00697C7D /* Services */, D019E6F224DF7C9E00697C7D /* Services */,
D0DB6EFB24C658E400D965FE /* View Models */, D0DB6EFB24C658E400D965FE /* View Models */,
D0DB6EF024C5224F00D965FE /* Views */, D0DB6EF024C5224F00D965FE /* Views */,
@ -1125,6 +1137,7 @@
D074577A24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */, D074577A24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */,
D0ED1BB724CE47F400B4899C /* WebAuthSession.swift in Sources */, D0ED1BB724CE47F400B4899C /* WebAuthSession.swift in Sources */,
D0A1CA7424DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */, D0A1CA7424DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */,
D009CCF024F3260300F410E7 /* DecodableDefault.swift in Sources */,
D054951224EB1041008B00A5 /* StatusListService.swift in Sources */, D054951224EB1041008B00A5 /* StatusListService.swift in Sources */,
D0159F9124DE743700E78478 /* TabNavigationView.swift in Sources */, D0159F9124DE743700E78478 /* TabNavigationView.swift in Sources */,
D0ED1BC424CED54D00B4899C /* HTTPTarget.swift in Sources */, D0ED1BC424CED54D00B4899C /* HTTPTarget.swift in Sources */,
@ -1229,6 +1242,7 @@
D0ED1BC524CED54D00B4899C /* HTTPTarget.swift in Sources */, D0ED1BC524CED54D00B4899C /* HTTPTarget.swift in Sources */,
D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */, D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */,
D075817D24E6659A0081F6A3 /* NotificationTypesPreferencesView.swift in Sources */, D075817D24E6659A0081F6A3 /* NotificationTypesPreferencesView.swift in Sources */,
D009CCF124F3260300F410E7 /* DecodableDefault.swift in Sources */,
D019E6E824DF72E700697C7D /* AccessTokenEndpoint.swift in Sources */, D019E6E824DF72E700697C7D /* AccessTokenEndpoint.swift in Sources */,
D04FD73D24D4A83A007D572D /* InstanceEndpoint+Stubbing.swift in Sources */, D04FD73D24D4A83A007D572D /* InstanceEndpoint+Stubbing.swift in Sources */,
D0DC175C24D0154F00A75C65 /* MastodonAPI.swift in Sources */, D0DC175C24D0154F00A75C65 /* MastodonAPI.swift in Sources */,

View File

@ -135,10 +135,10 @@ private extension ContentDatabase {
t.column("card", .blob) t.column("card", .blob)
t.column("language", .text) t.column("language", .text)
t.column("text", .text) t.column("text", .text)
t.column("favourited", .boolean) t.column("favourited", .boolean).notNull()
t.column("reblogged", .boolean) t.column("reblogged", .boolean).notNull()
t.column("muted", .boolean) t.column("muted", .boolean).notNull()
t.column("bookmarked", .boolean) t.column("bookmarked", .boolean).notNull()
t.column("pinned", .boolean) t.column("pinned", .boolean)
} }
@ -309,10 +309,10 @@ private struct StoredStatus: Codable, Hashable {
let card: Card? let card: Card?
let language: String? let language: String?
let text: String? let text: String?
let favourited: Bool? let favourited: Bool
let reblogged: Bool? let reblogged: Bool
let muted: Bool? let muted: Bool
let bookmarked: Bool? let bookmarked: Bool
let pinned: Bool? let pinned: Bool?
} }

View File

@ -26,7 +26,7 @@ struct Account: Codable, Hashable {
let headerStatic: URL let headerStatic: URL
let fields: [Field] let fields: [Field]
let emojis: [Emoji] let emojis: [Emoji]
let bot: Bool? @DecodableDefault.False private(set) var bot: Bool
let moved: Bool? @DecodableDefault.False private(set) var moved: Bool
let discoverable: Bool? @DecodableDefault.False private(set) var discoverable: Bool
} }

View File

@ -35,12 +35,12 @@ extension Identity {
} }
struct Preferences: Codable, Hashable { struct Preferences: Codable, Hashable {
var useServerPostingReadingPreferences = true @DecodableDefault.True var useServerPostingReadingPreferences
var postingDefaultVisibility = Status.Visibility.public @DecodableDefault.StatusVisibilityPublic var postingDefaultVisibility: Status.Visibility
var postingDefaultSensitive = false @DecodableDefault.False var postingDefaultSensitive
var postingDefaultLanguage: String? var postingDefaultLanguage: String?
var readingExpandMedia = MastodonPreferences.ExpandMedia.default @DecodableDefault.ExpandMediaDefault var readingExpandMedia: MastodonPreferences.ExpandMedia
var readingExpandSpoilers = false @DecodableDefault.False var readingExpandSpoilers
} }
} }

View File

@ -19,10 +19,10 @@ struct Instance: Codable, Hashable {
let shortDescription: String? let shortDescription: String?
let email: String let email: String
let version: String let version: String
let languages: [String] @DecodableDefault.EmptyList private(set) var languages: [String]
let registrations: Bool? @DecodableDefault.False private(set) var registrations: Bool
let approvalRequired: Bool? @DecodableDefault.False private(set) var approvalRequired: Bool
let invitesEnabled: Bool? @DecodableDefault.False private(set) var invitesEnabled: Bool
let urls: URLs let urls: URLs
let stats: Stats let stats: Stats
let thumbnail: URL? let thumbnail: URL?

View File

@ -14,8 +14,8 @@ struct Poll: Codable, Hashable {
let multiple: Bool let multiple: Bool
let votesCount: Int let votesCount: Int
let votersCount: Int? let votersCount: Int?
let voted: Bool? @DecodableDefault.False private(set) var voted: Bool
let ownVotes: [Int]? @DecodableDefault.EmptyList private(set) var ownVotes: [Int]
let options: [Option] let options: [Option]
let emojis: [Emoji] let emojis: [Emoji]
} }

View File

@ -8,7 +8,7 @@ struct PushSubscription: Codable {
var favourite: Bool var favourite: Bool
var reblog: Bool var reblog: Bool
var mention: Bool var mention: Bool
var poll: Bool @DecodableDefault.True var poll: Bool
} }
let endpoint: URL let endpoint: URL
@ -17,5 +17,10 @@ struct PushSubscription: Codable {
} }
extension PushSubscription.Alerts { extension PushSubscription.Alerts {
static let initial: Self = Self(follow: true, favourite: true, reblog: true, mention: true, poll: true) static let initial: Self = Self(
follow: true,
favourite: true,
reblog: true,
mention: true,
poll: DecodableDefault.True())
} }

View File

@ -27,7 +27,7 @@ class Status: Codable, Identifiable {
let emojis: [Emoji] let emojis: [Emoji]
let reblogsCount: Int let reblogsCount: Int
let favouritesCount: Int let favouritesCount: Int
let repliesCount: Int @DecodableDefault.Zero private(set) var repliesCount: Int
let application: Application? let application: Application?
let url: URL? let url: URL?
let inReplyToId: String? let inReplyToId: String?
@ -37,10 +37,10 @@ class Status: Codable, Identifiable {
let card: Card? let card: Card?
let language: String? let language: String?
let text: String? let text: String?
let favourited: Bool? @DecodableDefault.False private(set) var favourited: Bool
let reblogged: Bool? @DecodableDefault.False private(set) var reblogged: Bool
let muted: Bool? @DecodableDefault.False private(set) var muted: Bool
let bookmarked: Bool? @DecodableDefault.False private(set) var bookmarked: Bool
let pinned: Bool? let pinned: Bool?
// Xcode-generated memberwise initializer // Xcode-generated memberwise initializer
@ -69,10 +69,10 @@ class Status: Codable, Identifiable {
card: Card?, card: Card?,
language: String?, language: String?,
text: String?, text: String?,
favourited: Bool?, favourited: Bool,
reblogged: Bool?, reblogged: Bool,
muted: Bool?, muted: Bool,
bookmarked: Bool?, bookmarked: Bool,
pinned: Bool?) { pinned: Bool?) {
self.id = id self.id = id
self.uri = uri self.uri = uri

View File

@ -0,0 +1,97 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
// Thank you https://www.swiftbysundell.com/tips/default-decoding-values/
protocol DecodableDefaultSource {
associatedtype Value: Decodable
static var defaultValue: Value { get }
}
enum DecodableDefault {}
// swiftlint:disable nesting
extension DecodableDefault {
@propertyWrapper
struct Wrapper<Source: DecodableDefaultSource> {
typealias Value = Source.Value
var wrappedValue = Source.defaultValue
}
}
extension DecodableDefault {
typealias Source = DecodableDefaultSource
typealias List = Decodable & ExpressibleByArrayLiteral
typealias Map = Decodable & ExpressibleByDictionaryLiteral
enum Sources {
enum True: Source {
static var defaultValue: Bool { true }
}
enum False: Source {
static var defaultValue: Bool { false }
}
enum EmptyString: Source {
static var defaultValue: String { "" }
}
enum EmptyList<T: List>: Source {
static var defaultValue: T { [] }
}
enum EmptyMap<T: Map>: Source {
static var defaultValue: T { [:] }
}
enum Zero: Source {
static var defaultValue: Int { 0 }
}
enum StatusVisibilityPublic: Source {
static var defaultValue: Status.Visibility { .public }
}
enum ExpandMediaDefault: Source {
static var defaultValue: MastodonPreferences.ExpandMedia { .default }
}
}
}
// swiftlint:enable nesting
extension DecodableDefault {
typealias True = Wrapper<Sources.True>
typealias False = Wrapper<Sources.False>
typealias EmptyString = Wrapper<Sources.EmptyString>
typealias EmptyList<T: List> = Wrapper<Sources.EmptyList<T>>
typealias EmptyMap<T: Map> = Wrapper<Sources.EmptyMap<T>>
typealias Zero = Wrapper<Sources.Zero>
typealias StatusVisibilityPublic = Wrapper<Sources.StatusVisibilityPublic>
typealias ExpandMediaDefault = Wrapper<Sources.ExpandMediaDefault>
}
extension DecodableDefault.Wrapper: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try container.decode(Value.self)
}
}
extension DecodableDefault.Wrapper: Equatable where Value: Equatable {}
extension DecodableDefault.Wrapper: Hashable where Value: Hashable {}
extension DecodableDefault.Wrapper: Encodable where Value: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
}
extension KeyedDecodingContainer {
func decode<T>(_ type: DecodableDefault.Wrapper<T>.Type,
forKey key: Key) throws -> DecodableDefault.Wrapper<T> {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}