Skip to content
This repository has been archived by the owner on Mar 8, 2020. It is now read-only.

Commit

Permalink
store the model used by the UI for rendering
Browse files Browse the repository at this point in the history
We are using a flat file on the system, which is working but should be
replaced with either db solution like SQLite[0], Realm[1], Sync[2], etc.
I am doing it this way to save some time for other topics.

Also added updated tests to check this and use new class names. Totally
missed the naming breakage earlier...

[0]: https://www.sqlite.org/index.html
[1]: https://github.com/realm/realm-cocoa
[2]: https://github.com/3lvis/Sync

Closes: #9 (persist minimal data required to present the something)
  • Loading branch information
aalemayhu committed Nov 14, 2018
1 parent 0852039 commit 31394de
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 14 deletions.
16 changes: 16 additions & 0 deletions cantera.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
848933E2219CC49A003728F3 /* AdsDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848933DD219CBE89003728F3 /* AdsDetailViewController.swift */; };
848933E3219CC51E003728F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848933DF219CC1A2003728F3 /* String.swift */; };
848933E4219CC520003728F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848933DF219CC1A2003728F3 /* String.swift */; };
848933E7219CDB1B003728F3 /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848933E6219CDB1B003728F3 /* StorageManager.swift */; };
848933E8219CE05F003728F3 /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848933E6219CDB1B003728F3 /* StorageManager.swift */; };
848933E9219CE061003728F3 /* StorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848933E6219CDB1B003728F3 /* StorageManager.swift */; };
84A18E00219C0FE400403C42 /* AdResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A18DFF219C0FE400403C42 /* AdResponse.swift */; };
84A18E01219C0FE400403C42 /* AdResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A18DFF219C0FE400403C42 /* AdResponse.swift */; };
84A18E02219C0FE400403C42 /* AdResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A18DFF219C0FE400403C42 /* AdResponse.swift */; };
Expand Down Expand Up @@ -86,6 +89,7 @@
8442CAE4219C082D006F8A30 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
848933DD219CBE89003728F3 /* AdsDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsDetailViewController.swift; sourceTree = "<group>"; };
848933DF219CC1A2003728F3 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
848933E6219CDB1B003728F3 /* StorageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageManager.swift; sourceTree = "<group>"; };
84A18DFF219C0FE400403C42 /* AdResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdResponse.swift; sourceTree = "<group>"; };
84A18E03219C10EE00403C42 /* PriceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PriceResponse.swift; sourceTree = "<group>"; };
84A18E07219C124500403C42 /* ImageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageResponse.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -145,6 +149,7 @@
8442CAC1219C082C006F8A30 /* cantera */ = {
isa = PBXGroup;
children = (
848933E5219CD892003728F3 /* Storage */,
84DDD343219C47DB00CDC4F0 /* Extensions */,
84E6854C219C15E40004A2FA /* Networking */,
84A18DFE219C0B5400403C42 /* Controller */,
Expand Down Expand Up @@ -176,6 +181,14 @@
path = canteraUITests;
sourceTree = "<group>";
};
848933E5219CD892003728F3 /* Storage */ = {
isa = PBXGroup;
children = (
848933E6219CDB1B003728F3 /* StorageManager.swift */,
);
path = Storage;
sourceTree = "<group>";
};
84A18DFC219C0B4A00403C42 /* Model */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -394,6 +407,7 @@
84E68550219C18500004A2FA /* AdsAPIHandler.swift in Sources */,
848933E0219CC1A2003728F3 /* String.swift in Sources */,
84DDD34B219C913E00CDC4F0 /* AdObject.swift in Sources */,
848933E7219CDB1B003728F3 /* StorageManager.swift in Sources */,
84DDD345219C47EF00CDC4F0 /* Colors.swift in Sources */,
8442CAC5219C082C006F8A30 /* AdsCollectionViewController.swift in Sources */,
84A18E04219C10EE00403C42 /* PriceResponse.swift in Sources */,
Expand All @@ -411,6 +425,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
848933E8219CE05F003728F3 /* StorageManager.swift in Sources */,
848933E4219CC520003728F3 /* String.swift in Sources */,
848933E1219CC498003728F3 /* AdsDetailViewController.swift in Sources */,
84E68559219C1D0A0004A2FA /* AdsCollectionViewController.swift in Sources */,
Expand All @@ -433,6 +448,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
848933E9219CE061003728F3 /* StorageManager.swift in Sources */,
848933E2219CC49A003728F3 /* AdsDetailViewController.swift in Sources */,
84DDD34D219C935700CDC4F0 /* AdObject.swift in Sources */,
84DDD342219C33FB00CDC4F0 /* AdViewCollectionViewCell.swift in Sources */,
Expand Down
30 changes: 19 additions & 11 deletions cantera/Controller/AdsCollectionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class AdsCollectionViewController: UICollectionViewController, AdViewCollectionV
case favorites = "Favoritter"
}

private let storage = StorageManager()
private let api = AdsAPIHandler()
private var favoritedAds = [AdObject]()
private var allAds = [AdObject]()
Expand Down Expand Up @@ -57,16 +58,20 @@ class AdsCollectionViewController: UICollectionViewController, AdViewCollectionV
collectionView.backgroundColor = .white

// todo should we use the safe guard?

// Should we persist the ads?
api.fetch { (response) in
if let response = response {
// We have to update UI in the main thread otherwise the main thread checker will kill us
DispatchQueue.main.async {
self.allAds = response.items.map { AdObject(adResponse: $0) }
// Drop all of the ads that are still under construction
self.allAds.removeAll { $0.price == nil }
self.collectionView.reloadData()
if let savedAds = storage.savedAds(), savedAds.count > 0 {
self.allAds = savedAds
self.collectionView.reloadData()
} else {
api.fetch { (response) in
if let response = response {
// We have to update UI in the main thread otherwise the main thread checker will kill us
DispatchQueue.main.async {
self.allAds = response.items.map { AdObject(adResponse: $0) }
// Drop all of the ads that are still under construction
self.allAds.removeAll { $0.price == nil }
self.collectionView.reloadData()
self.storage.persist(ads: self.allAds)
}
}
}
}
Expand Down Expand Up @@ -119,6 +124,7 @@ class AdsCollectionViewController: UICollectionViewController, AdViewCollectionV
@objc func pressedFavoritesItem() {
self.title = States.favorites.rawValue
self.navigationItem.leftBarButtonItem = leftBarButtonItem
self.favoritedAds = self.allAds.filter { $0.liked }
self.collectionView.reloadData()
}

Expand All @@ -130,11 +136,13 @@ class AdsCollectionViewController: UICollectionViewController, AdViewCollectionV

func toogleFavorite(for ad: AdObject, checked: Bool) {
ad.liked = checked
self.favoritedAds = self.allAds.filter { $0.liked }

// If the user is manipulating favorites, drop them from the immediately by triggering a reload
if self.title == States.favorites.rawValue {
self.collectionView.reloadData()
}

// Note: this will trigger FS sycalls for every change, should be optimized.
storage.persist(ads: self.allAds)
}
}
2 changes: 1 addition & 1 deletion cantera/Model/Response/PriceResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@

struct PriceResponse: Codable {
// Note: We are assuming .value is a integer based on the output of the API.
// There is probably some canonical documentation from FINN.no that can answer this.
// There is probably some canonical documentation from FINN.no somewhere, that can answer this.
let value: Int
}
2 changes: 1 addition & 1 deletion cantera/Model/UI/AdObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

class AdObject {
class AdObject: Codable {

public let price: Int?
public let location: String
Expand Down
49 changes: 49 additions & 0 deletions cantera/Storage/StorageManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// StorageManager.swift
// cantera
//
// Created by Alexander Alemayhu on 14/11/2018.
// Copyright © 2018 Alexander Alemayhu. All rights reserved.
//

import Foundation

class StorageManager {

private let persistedFilePath: String = {
let dir = NSHomeDirectory()
return "\(dir)/cached_payload.json"
}()

func persist (ads: [AdObject]) {
let url = URL(fileURLWithPath: persistedFilePath)
let encoder = JSONEncoder()
do {
let data = try encoder.encode(ads)
try data.write(to: url, options: .atomic)
print("Everyting should at \(persistedFilePath)")
} catch {
fatalError(error.localizedDescription)
}
}

func savedAds() -> [AdObject]? {
let url = URL(fileURLWithPath: persistedFilePath)
do {
let data = try Data(contentsOf: url)
let ads = try JSONDecoder().decode([AdObject].self, from: data)
return ads
} catch {
return nil
}
}

func purge() {
let url = URL(fileURLWithPath: persistedFilePath)
do {
try FileManager.default.removeItem(at: url)
} catch {
print("\(#function): \(error)")
}
}
}
10 changes: 9 additions & 1 deletion canteraTests/canteraTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class CanteraTests: XCTestCase {

guard let payload = data else { XCTFail("Failed to setup payload"); return }
do {
let ad = try JSONDecoder().decode(Ad.self, from: payload)
let ad = try JSONDecoder().decode(AdResponse.self, from: payload)
XCTAssertEqual(ad.description, "3-roms leilightet leies!")
if let price = ad.price?.value {
XCTAssertEqual(price, 15000)
Expand All @@ -67,6 +67,14 @@ class CanteraTests: XCTestCase {
}
XCTAssertEqual(ad.location, "Oslo")
XCTAssertEqual(ad.image.url, "2017/9/vertical-2/29/3/105/376/_9531505.jpg")

// Check storage manager is acting sane
let items: [AdObject] = [AdObject(adResponse: ad)]
let sm = StorageManager()
sm.persist(ads: items)
let actual = sm.savedAds()
XCTAssertNotNil(actual)
sm.purge()
} catch {
XCTFail(error.localizedDescription)
}
Expand Down

0 comments on commit 31394de

Please sign in to comment.