Skip to content
This repository was archived by the owner on Sep 8, 2022. It is now read-only.
Open
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
12 changes: 12 additions & 0 deletions Secretly.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
30C77CB6266AF48300A888DC /* CurrentUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C77CB5266AF48300A888DC /* CurrentUser.swift */; };
30C77CB8266BD44300A888DC /* CreatePostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C77CB7266BD44300A888DC /* CreatePostViewController.swift */; };
30FD0E722659645A006E309A /* Faker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FD0E712659645A006E309A /* Faker.swift */; };
657AE4DF26BE032B00953718 /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4DE26BE032B00953718 /* Author.swift */; };
657AE4E126BE037300953718 /* Like.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4E026BE037300953718 /* Like.swift */; };
657AE4E926BE229E00953718 /* LikesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657AE4E826BE229E00953718 /* LikesService.swift */; };
E021984723FA35E00025C28E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984623FA35E00025C28E /* AppDelegate.swift */; };
E021984923FA35E00025C28E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984823FA35E00025C28E /* SceneDelegate.swift */; };
E021984B23FA35E00025C28E /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E021984A23FA35E00025C28E /* WelcomeViewController.swift */; };
Expand Down Expand Up @@ -125,6 +128,9 @@
30C77CB5266AF48300A888DC /* CurrentUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentUser.swift; sourceTree = "<group>"; };
30C77CB7266BD44300A888DC /* CreatePostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatePostViewController.swift; sourceTree = "<group>"; };
30FD0E712659645A006E309A /* Faker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Faker.swift; sourceTree = "<group>"; };
657AE4DE26BE032B00953718 /* Author.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = "<group>"; };
657AE4E026BE037300953718 /* Like.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Like.swift; sourceTree = "<group>"; };
657AE4E826BE229E00953718 /* LikesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikesService.swift; sourceTree = "<group>"; };
E021984323FA35E00025C28E /* Secretly.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretly.app; sourceTree = BUILT_PRODUCTS_DIR; };
E021984623FA35E00025C28E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
E021984823FA35E00025C28E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -222,6 +228,7 @@
30C77CAF266AD69700A888DC /* CurrentUserService.swift */,
304E06C726742BDA00A99128 /* CreatePostService.swift */,
304E06C926742CC500A99128 /* FeedService.swift */,
657AE4E826BE229E00953718 /* LikesService.swift */,
);
path = Services;
sourceTree = "<group>";
Expand All @@ -248,6 +255,8 @@
307A30572661AD540020DF8B /* User.swift */,
30C77CB3266AF47300A888DC /* Credentials.swift */,
30C77CB5266AF48300A888DC /* CurrentUser.swift */,
657AE4DE26BE032B00953718 /* Author.swift */,
657AE4E026BE037300953718 /* Like.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -445,6 +454,7 @@
302B5849267E658E007133E6 /* RequestError.swift in Sources */,
302B5846267E658E007133E6 /* AmacaConfig.swift in Sources */,
302BB61C267D7CC800FD74F5 /* PreviewPostVIew.swift in Sources */,
657AE4E126BE037300953718 /* Like.swift in Sources */,
3033795D267537B40066D94A /* FeedCollectionViewController+UICollectionViewDelegateFlowLayout .swift in Sources */,
30BC8BA82662CEBA00F7E6A5 /* Checksum.swift in Sources */,
30FD0E722659645A006E309A /* Faker.swift in Sources */,
Expand All @@ -453,11 +463,13 @@
307A305E2661CD510020DF8B /* PostCollectionViewCell.swift in Sources */,
302BB626267E447900FD74F5 /* PostInputViewController+UITextFieldDelegate.swift in Sources */,
30BC8BA02662B8A700F7E6A5 /* StorageType.swift in Sources */,
657AE4E926BE229E00953718 /* LikesService.swift in Sources */,
307A305B2661B7A20020DF8B /* FeedCollectionViewController.swift in Sources */,
304E06CC2674442800A99128 /* UIImage+encodeBase64.swift in Sources */,
30BC8BA62662C02300F7E6A5 /* CacheImage.swift in Sources */,
302B5847267E658E007133E6 /* StatusCode.swift in Sources */,
302B584B267E658E007133E6 /* RequestBuilder.swift in Sources */,
657AE4DF26BE032B00953718 /* Author.swift in Sources */,
307A306526629B990020DF8B /* AuthorView.swift in Sources */,
30C77CB4266AF47300A888DC /* Credentials.swift in Sources */,
30BC8BA22662BB0000F7E6A5 /* DataContainer.swift in Sources */,
Expand Down
20 changes: 20 additions & 0 deletions Secretly/Models/Author.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Author.swift
// Secretly
//
// Created by Victor Aceves on 06/08/21.
// Copyright © 2021 3zcurdia. All rights reserved.
//

import Foundation
import UIKit

struct Author: Restable {
let name: String?
let id: String?

init(id: String, name: String){
self.name = name
self.id = id
}
}
19 changes: 19 additions & 0 deletions Secretly/Models/Like.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Like.swift
// Secretly
//
// Created by Victor Aceves on 06/08/21.
// Copyright © 2021 3zcurdia. All rights reserved.
//

import Foundation
import UIKit

struct Like: Restable {
let id: Int
let likeableType: String
let likeableId: Int
let createdAt: String
let updatedAt: String
let user: User
}
16 changes: 13 additions & 3 deletions Secretly/Models/Post.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ struct Post: Restable {
let longitude: Double?
let createdAt: Date?
let updatedAt: Date?

init(content: String, backgroundColor: String, latitude: Double? = nil, longitude: Double? = nil, image: UIImage? = nil) {
var likesCount: Int?
var liked: Bool?

init(content: String, backgroundColor: String, latitude: Double? = nil, longitude: Double? = nil, image: UIImage? = nil, likesCount: Int? = nil, liked:Bool?=false) {
self.content = content
self.backgroundColor = backgroundColor
self.id = nil
Expand All @@ -34,8 +36,10 @@ struct Post: Restable {
self.commentsCount = nil
self.createdAt = nil
self.updatedAt = nil
self.likesCount = likesCount
self.liked = liked
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(content, forKey: .content)
Expand All @@ -44,4 +48,10 @@ struct Post: Restable {
try container.encode(latitude, forKey: .latitude)
try container.encode(longitude, forKey: .longitude)
}

mutating func toggleLike(isLiked:Bool){
likesCount = isLiked ? (likesCount ?? 0) + 1 : (likesCount ?? 0) - 1
self.liked = isLiked
}

}
16 changes: 10 additions & 6 deletions Secretly/Network/HttpResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@ import Foundation

struct HttpResponse {
let httpUrlResponse: HTTPURLResponse

init(response: URLResponse?) {
self.httpUrlResponse = (response as? HTTPURLResponse) ?? HTTPURLResponse()
httpUrlResponse = (response as? HTTPURLResponse) ?? HTTPURLResponse()
}

var status: StatusCode {
return StatusCode(rawValue: self.httpUrlResponse.statusCode)
return StatusCode(rawValue: httpUrlResponse.statusCode)
}

func result(for data: Data?) -> Result<Data?, Error> {
return status.result().map { _ in data }
if let udata = data, !udata.isEmpty {
return status.result().map { _ in data }
} else {
return status.result().map { _ in nil }
}
}
}
32 changes: 23 additions & 9 deletions Secretly/Network/RestClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ typealias Restable = Codable & Identifiable
struct RestClient<T: Restable> {
let client: HttpClient
let path: String

public var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
Expand All @@ -32,56 +32,70 @@ struct RestClient<T: Restable> {
encoder.keyEncodingStrategy = .convertToSnakeCase
return encoder
}()

func list(complete: @escaping (Result<[T], Error>) -> Void) {
client.get(path: path) { result in
let newResult = result.flatMap { parseList(data: $0) }
complete(newResult)
}
}

func show(complete: @escaping (Result<T?, Error>) -> Void) {
show("", complete: complete)
}

func show(_ identifier: String, complete: @escaping (Result<T?, Error>) -> Void) {
client.get(path: "\(path)/\(identifier)") { result in
let newResult = result.flatMap { parse(data: $0) }
complete(newResult)
}
}


func create(complete: @escaping (Result<T?, Error>) -> Void) throws {
client.post(path: path, body: nil) { result in
let newResult = result.flatMap { parse(data: $0) }
complete(newResult)
}
}

func create(model: T, complete: @escaping (Result<T?, Error>) -> Void) throws {
let data = try encoder.encode(model)
client.post(path: path, body: data) { result in
let newResult = result.flatMap { parse(data: $0) }
complete(newResult)
}
}

func update(model: T, complete: @escaping (Result<T?, Error>) -> Void) throws {
let data = try encoder.encode(model)
client.put(path: "\(path)/\(model.id)", body: data) { result in
let newResult = result.flatMap { parse(data: $0) }
complete(newResult)
}
}


func delete(complete: @escaping (Result<T?, Error>) -> Void) {
client.delete(path: path) { result in
let newResult = result.flatMap { parse(data: $0) }
complete(newResult)
}
}

func delete(model: T, complete: @escaping (Result<T?, Error>) -> Void) {
client.delete(path: "\(path)/\(model.id)") { result in
let newResult = result.flatMap { parse(data: $0) }
complete(newResult)
}
}

private func parseList(data: Data?) -> Result<[T], Error> {
if let data = data {
return Result { try self.decoder.decode([T].self, from: data) }
} else {
return .success([])
}
}

private func parse(data: Data?) -> Result<T?, Error> {
if let data = data {
return Result { try self.decoder.decode(T.self, from: data) }
Expand Down
37 changes: 37 additions & 0 deletions Secretly/Services/LikesService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// LikesService.swift
// Secretly
//
// Created by Victor Aceves on 06/08/21.
// Copyright © 2021 3zcurdia. All rights reserved.
//

import Foundation

struct LikesService {
private var likesEndpoint: RestClient<Like>?
var liked = false

init(post: Post?){
guard let post = post, let postId = post.id else {
likesEndpoint = nil
return
}
likesEndpoint = RestClient<Like>(client: AmacaConfig.shared.httpClient, path: "/api/v1/posts/\(postId)/likes")
liked = post.liked ?? false
}

mutating func toggleLike(complete: @escaping (Result<Like?, Error>) -> Void) {
if liked {
liked = !liked
likesEndpoint?.delete { result in
DispatchQueue.main.async { complete(result) }
}
} else {
liked = !liked
try? likesEndpoint?.create { result in
DispatchQueue.main.async { complete(result) }
}
}
}
}
36 changes: 32 additions & 4 deletions Secretly/Views/PostCollectionViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,24 @@ import UIKit

class PostCollectionViewCell: UICollectionViewCell {
static let reuseIdentifier = "feedPostCell"
var likesService: LikesService?
var post: Post? {
didSet {
updateView()
updateView()
likesService = LikesService(post: post)
}
}
@IBOutlet weak var authorView: AuthorView!
@IBOutlet weak var contentLabel: UILabel!
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var likeState: UIImageView!
@IBOutlet weak var commentCounter: UILabel!

@IBOutlet weak var likesCounter: UILabel!
@IBOutlet weak var btnLike: UIButton!

override func awakeFromNib() {
super.awakeFromNib()
}

func updateView() {
imageView.image = nil
guard let post = post else { return }
Expand All @@ -37,5 +40,30 @@ class PostCollectionViewCell: UICollectionViewCell {
ImageLoader.load(postImg.mediumUrl) { img in self.imageView.image = img }
}
self.authorView.author = post.user
// Update likes counter label
self.likesCounter.text = "\(post.likesCount ?? 0) Me gusta"
// Update like image
if post.liked ?? false{
btnLike.setImage(UIImage(systemName: "heart.fill"), for: .normal)
} else{
btnLike.setImage(UIImage(systemName: "heart"), for: .normal)
}
}

@IBAction func toggleLike(_ sender: UIButton) {
likesService?.toggleLike { [unowned self] result in
switch result {
case .success(nil):
btnLike.setImage(UIImage(systemName: "heart"), for: .normal)
post?.toggleLike(isLiked: false)
case .success:
btnLike.setImage(UIImage(systemName: "heart.fill"), for: .normal)
post?.toggleLike(isLiked: true)
case .failure:
print("Failure request \(result)")
}
self.likesCounter.text = "\(post?.likesCount ?? 0) Me gusta"
}
}

}
Loading