Skip to content

Swift Codable #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

#### Added

* Type safe resources generated by R.swift
* Syntax check with SwiftLint [#3](https://github.com/polydice/iCook-tvOS/pull/3)
* Code coverage
* A little help from [Danger](http://danger.systems/) [#4](https://github.com/polydice/iCook-tvOS/pull/4)
* Protocol extended features
* Swift 3 syntax updates [#5](https://github.com/polydice/iCook-tvOS/pull/5)
* Simpler project quick start [#6](https://github.com/polydice/iCook-tvOS/pull/6)
* Replace Freddy with Swift Codable [#11](https://github.com/polydice/iCook-tvOS/pull/11)

## v1.0.0

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ bootstrap:
gem install bundler -v 2.0.2
bundle install
# pod install
bundle exec pod keys set BaseAPIURL "https://polydice.com/iCook-tvOS/demo/"
bundle exec pod keys set BaseAPIURL "https://cdn.jsdelivr.net/gh/polydice/iCook-tvOS@gh-pages/demo/"
bundle exec pod keys set CrashlyticsAPIKey "-"
bundle exec pod keys set TreasureDataAPIKey "-"
bundle exec pod install
Expand Down
1 change: 0 additions & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ target :iCookTV do
pod "Alamofire", "~> 4.2.0"
pod "Crashlytics"
pod "Fabric"
pod "Freddy", "~> 3.0.0"
pod "HCYoutubeParser"
pod "Hue", "~> 2.0.0"
pod "Kingfisher", "~> 3.2.0"
Expand Down
5 changes: 1 addition & 4 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ PODS:
- Crashlytics (3.8.3):
- Fabric (~> 1.6.3)
- Fabric (1.6.10)
- Freddy (3.0.0)
- HCYoutubeParser (0.0.5)
- Hue (2.0.1)
- KeenClientTD (3.2.27)
Expand All @@ -19,7 +18,6 @@ DEPENDENCIES:
- Alamofire (~> 4.2.0)
- Crashlytics
- Fabric
- Freddy (~> 3.0.0)
- HCYoutubeParser
- Hue (~> 2.0.0)
- Keys (from `Pods/CocoaPodsKeys`)
Expand All @@ -37,7 +35,6 @@ SPEC CHECKSUMS:
Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe
Crashlytics: 2b6dbe138a42395577cfa73dfa1aa7248cadf39e
Fabric: c73f371ee543e3f0b80608f2674750e4910d1669
Freddy: 367f994c88a90dd3987293b4b1bec94a4530745a
HCYoutubeParser: bc1db8c062e4b835eda381f2be2f9970f6a8d071
Hue: 354caec055fdc9d38b5ef33ca2e7224721843baf
KeenClientTD: ddb29a702bd4cfce5d526519ca8959d70c324c6a
Expand All @@ -48,6 +45,6 @@ SPEC CHECKSUMS:
SwiftLint: a014c92b4664e8b13f380f8640a51bb1733778ba
TreasureData-iOS-SDK: cc878af36b85ae3540a9a5bdb36b7bdc8707b719

PODFILE CHECKSUM: a39d73a29a507453d7f26041b291e321718ffeff
PODFILE CHECKSUM: 3ed7b21c8acb46a6e14e06ff31e882ac9d392b95

COCOAPODS: 1.3.1
8 changes: 4 additions & 4 deletions iCookTV.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
B5761A581CCF5752008CCC08 /* ResourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5761A571CCF5752008CCC08 /* ResourceHelper.swift */; };
B5761A5A1CCF6CCD008CCC08 /* VideoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5761A591CCF6CCD008CCC08 /* VideoSpec.swift */; };
B5761A5C1CCF6D78008CCC08 /* Video.json in Resources */ = {isa = PBXBuildFile; fileRef = B5761A5B1CCF6D78008CCC08 /* Video.json */; };
B57C930A22FF149800BBAC40 /* KeyPathDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CD05422FEFF1C00492E27 /* KeyPathDecoding.swift */; };
B586EFDB1CCA21B300EA8218 /* InsetLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B586EFDA1CCA21B300EA8218 /* InsetLabel.swift */; };
B58DE38B1CB8B54200C00266 /* CoverBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58DE38A1CB8B54200C00266 /* CoverBuilder.swift */; };
B58E906D1D5F806600AC184D /* VideosDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58E906C1D5F806600AC184D /* VideosDataSource.swift */; };
Expand Down Expand Up @@ -104,6 +105,7 @@
B52EC3B01CB26F1B0072762C /* CGRect+Grid.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGRect+Grid.swift"; sourceTree = "<group>"; };
B52EC3B21CB3A02E0072762C /* UIFont+TV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+TV.swift"; sourceTree = "<group>"; };
B53115921CC69E7C00E75292 /* HistoryManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryManager.swift; sourceTree = "<group>"; };
B53CD05422FEFF1C00492E27 /* KeyPathDecoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPathDecoding.swift; sourceTree = "<group>"; };
B53E39771CA1494000EB1EEE /* UIColor+TV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+TV.swift"; sourceTree = "<group>"; };
B53E397A1CA14B4200EB1EEE /* CategoriesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CategoriesViewController.swift; sourceTree = "<group>"; };
B543C3E91CD1E85E008C512B /* OverlayViewPresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverlayViewPresentable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -299,6 +301,7 @@
B58DE38A1CB8B54200C00266 /* CoverBuilder.swift */,
B543C3EE1CD1FD0B008C512B /* GroundControl.swift */,
B53115921CC69E7C00E75292 /* HistoryManager.swift */,
B53CD05422FEFF1C00492E27 /* KeyPathDecoding.swift */,
B543C3F01CD21530008C512B /* Tracker.swift */,
);
path = Helpers;
Expand Down Expand Up @@ -530,7 +533,6 @@
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-iCookTV/Pods-iCookTV-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/Freddy/Freddy.framework",
"${BUILT_PRODUCTS_DIR}/HCYoutubeParser/HCYoutubeParser.framework",
"${BUILT_PRODUCTS_DIR}/Hue/Hue.framework",
"${BUILT_PRODUCTS_DIR}/KeenClientTD/KeenClientTD.framework",
Expand All @@ -541,7 +543,6 @@
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Freddy.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HCYoutubeParser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Hue.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KeenClientTD.framework",
Expand Down Expand Up @@ -590,7 +591,6 @@
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-iCookTV-iCookTVTests/Pods-iCookTV-iCookTVTests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
"${BUILT_PRODUCTS_DIR}/Freddy/Freddy.framework",
"${BUILT_PRODUCTS_DIR}/HCYoutubeParser/HCYoutubeParser.framework",
"${BUILT_PRODUCTS_DIR}/Hue/Hue.framework",
"${BUILT_PRODUCTS_DIR}/KeenClientTD/KeenClientTD.framework",
Expand All @@ -603,7 +603,6 @@
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Freddy.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HCYoutubeParser.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Hue.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KeenClientTD.framework",
Expand Down Expand Up @@ -661,6 +660,7 @@
B50BFC071CC8AF14004F853D /* HistoryViewController.swift in Sources */,
B51A95391CCF1C5100E5ED97 /* iCookTVKeys.swift in Sources */,
B586EFDB1CCA21B300EA8218 /* InsetLabel.swift in Sources */,
B57C930A22FF149800BBAC40 /* KeyPathDecoding.swift in Sources */,
B500D9AF1CBA3B7900622198 /* LaunchViewController.swift in Sources */,
B50A202B1DC4B53C00ACBF1B /* LoadingIndicatorPresentable.swift in Sources */,
B500D99F1CB93D0600622198 /* MainMenuView.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion iCookTV/Controllers/HistoryViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class HistoryViewController: UIViewController,
isLoading = true
DispatchQueue.global().async {
do {
let history = try HistoryManager.history.map(Video.init)
let decoder = JSONDecoder()
let history = try HistoryManager.history.map { try decoder.decode(Video.self, from: $0) }
DispatchQueue.main.sync {
self.dataSource.append(history, toCollectionView: self.collectionView)
self.setOverlayViewHidden(self.dataSource.numberOfItems > 0, animated: true)
Expand Down
1 change: 0 additions & 1 deletion iCookTV/Controllers/LaunchViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import UIKit
import Alamofire
import Freddy

class LaunchViewController: UIViewController, DataFetching {

Expand Down
6 changes: 3 additions & 3 deletions iCookTV/Controllers/VideoPlayerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import UIKit
import AVKit
import Alamofire
import Freddy

class VideoPlayerController: AVPlayerViewController, Trackable {

Expand Down Expand Up @@ -90,8 +89,9 @@ class VideoPlayerController: AVPlayerViewController, Trackable {
}

do {
let json = try JSON(data: data)
let video = try Video(json: json["data"] ?? nil)
let decoder = JSONDecoder()
let parsed = try decoder.decode(DataKeyPathDecoding<Video>.self, from: data)
let video = parsed.data
video.convertToPlayerItemWithCover(self?.coverImage) { [weak self] in
self?.setPlayerItem($0)
}
Expand Down
1 change: 0 additions & 1 deletion iCookTV/Controllers/VideosViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import UIKit
import Alamofire
import Freddy
import Keys

class VideosViewController: UIViewController,
Expand Down
13 changes: 3 additions & 10 deletions iCookTV/Extensions/DataRequest+Result.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import Foundation
import Alamofire
import Freddy

enum Result<T> {
case success(T)
Expand Down Expand Up @@ -55,10 +54,10 @@ enum Result<T> {

extension DataRequest {

func responseJSONObject(
func responseResult(
queue: DispatchQueue? = nil,
options: JSONSerialization.ReadingOptions = .allowFragments,
completion: @escaping (Result<JSON>) -> Void
completion: @escaping (Result<Data>) -> Void
) -> Self {
let serializer = DataRequest.jsonResponseSerializer(options: options)

Expand All @@ -68,13 +67,7 @@ extension DataRequest {
completion(.failure(error))
return
}

do {
let json = try JSON(data: data)
completion(.success(json))
} catch {
completion(.failure(error))
}
completion(.success(data))
}
}

Expand Down
26 changes: 9 additions & 17 deletions iCookTV/Helpers/HistoryManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
//

import Foundation
import Freddy

struct HistoryManager {

Expand All @@ -46,16 +45,11 @@ struct HistoryManager {
// MARK: - Public Methods

/// Returns the deserialized video history read from the cache directory.
static var history: [JSON] {
static var history: [Data] {
if let path = cache?.path, let records = NSArray(contentsOfFile: path) as? [Data] {
do {
return try records.map { try JSON(data: $0) }
} catch {
Tracker.track(error)
return [JSON]()
}
return records
} else {
return [JSON]()
return []
}
}

Expand All @@ -71,19 +65,17 @@ struct HistoryManager {
}
Debug.print(path)

let json = video.toJSON()
var records = history
let decoder = JSONDecoder()
var records: [Video] = history.flatMap { try? decoder.decode(Video.self, from: $0) }

// Keep the latest video at top.
for (index, element) in self.history.enumerated() where element["id"] == json["id"] {
records.remove(at: index)
break
}
records.insert(json, at: 0)
records = records.filter { $0.id != video.id }
records.insert(video, at: 0)
Debug.print("records.count =", records.count)

do {
let data = try records.map { try $0.serialize() } as NSArray
let encoder = JSONEncoder()
let data = try records.map { try encoder.encode($0) } as NSArray
data.write(toFile: path, atomically: true)
} catch {
Tracker.track(error)
Expand Down
38 changes: 38 additions & 0 deletions iCookTV/Helpers/KeyPathDecoding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// KeyPathDecoding.swift
// TryTVOS
//
// Created by Ben on 10/08/2019.
// Copyright © 2019 bcylin.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

import Foundation

/// A type for decoding nested data structure.
struct DataKeyPathDecoding<T: Decodable>: Decodable {

let data: T
let links: Links?

struct Links: Decodable {
let next: String?
}
}
36 changes: 29 additions & 7 deletions iCookTV/Models/Category.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,42 @@
// SOFTWARE.
//

import Freddy
import Foundation

struct Category: JSONDecodable {
struct Category: Codable {

let id: String
let name: String
let coverURLs: [String]

// MARK: - JSONDecodable
// MARK: - Codable

init(json value: JSON) throws {
id = try value.getString(at: "id")
name = try value.getString(at: "attributes", "name", or: "")
coverURLs = try value.getArray(at: "attributes", "cover-urls").map(String.init)
private enum CodingKeys: String, CodingKey {
case id
case attributes
}

private enum AttributesCodingKeys: String, CodingKey {
case name
case coverURLs = "cover-urls"
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)

let attributes = try container.nestedContainer(keyedBy: AttributesCodingKeys.self, forKey: .attributes)
name = try attributes.decode(String.self, forKey: .name)
coverURLs = try attributes.decode([String].self, forKey: .coverURLs)
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)

var attributes = container.nestedContainer(keyedBy: AttributesCodingKeys.self, forKey: .attributes)
try attributes.encode(name, forKey: .name)
try attributes.encode(coverURLs, forKey: .coverURLs)
}

}
Loading