NewsBlur/clients/ios/Widget Extension/WidgetStory.swift

124 lines
4.1 KiB
Swift
Raw Normal View History

//
// WidgetStory.swift
// Widget Extension
//
// Created by David Sinclair on 2019-11-29.
// Copyright © 2019 NewsBlur. All rights reserved.
//
import UIKit
/// A story to display in the widget.
struct Story: Codable, Identifiable {
/// The version number.
let version = 1
/// The story hash.
let id: String
/// The feed ID.
let feed: String
/// The date and/or time as a string.
let date: String
/// The author of the story.
let author: String
/// The title of the story.
let title: String
/// The content of the story.
let content: String
/// The URL of the image, or `nil` if none.
let imageURL: URL?
/// Keys for the dictionary representation.
struct DictionaryKeys {
static let id = "story_hash"
static let feed = "story_feed_id"
static let date = "short_parsed_date"
static let author = "story_authors"
static let title = "story_title"
static let content = "story_content"
static let imageURLs = "image_urls"
static let secureImageURLs = "secure_image_thumbnails"
}
/// Initializer from a dictionary.
///
/// - Parameter dictionary: Dictionary from the server.
init(from dictionary: [String : Any]) {
id = dictionary[DictionaryKeys.id] as? String ?? ""
feed = dictionary[DictionaryKeys.feed] as? String ?? "\(dictionary[DictionaryKeys.feed] as? Int ?? 0)"
date = dictionary[DictionaryKeys.date] as? String ?? ""
author = dictionary[DictionaryKeys.author] as? String ?? ""
title = dictionary[DictionaryKeys.title] as? String ?? ""
content = dictionary[DictionaryKeys.content] as? String ?? ""
if let imageURLs = dictionary[DictionaryKeys.imageURLs] as? [String], let first = imageURLs.first, let secureImages = dictionary[DictionaryKeys.secureImageURLs] as? [String : String], let url = secureImages[first] {
imageURL = URL(string: url)
} else {
imageURL = nil
}
}
/// Keys for the codable representation.
enum CodingKeys: String, CodingKey {
case version = "version"
case id = "id"
case feed = "feed"
case date = "date"
case author = "author"
case title = "title"
case content = "content"
case imageURL = "imageURL"
}
/// Initializer to load from the JSON data.
///
/// - Parameter decoder: The decoder from which to read data.
/// - Throws: An error if the data is invalid.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
feed = try container.decode(String.self, forKey: .feed)
date = try container.decode(String.self, forKey: .date)
author = try container.decode(String.self, forKey: .author)
title = try container.decode(String.self, forKey: .title)
content = try container.decode(String.self, forKey: .content)
imageURL = try container.decodeIfPresent(URL.self, forKey: .imageURL)
}
/// Encodes the story into the given encoder.
///
/// - Parameter encoder: The encoder to which to write data.
/// - Throws: An error if the data is invalid.
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(version, forKey: .version)
try container.encode(id, forKey: .id)
try container.encode(feed, forKey: .feed)
try container.encode(date, forKey: .date)
try container.encode(author, forKey: .author)
try container.encode(title, forKey: .title)
try container.encode(content, forKey: .content)
try container.encodeIfPresent(imageURL, forKey: .imageURL)
}
}
extension Story: Equatable {
static func ==(lhs: Story, rhs: Story) -> Bool {
return lhs.id == rhs.id
}
}
extension Story: CustomStringConvertible {
var description: String {
return "Story \(title) by \(author) (\(id))"
}
}