diff --git a/Dear-World/Dear-World.xcodeproj/project.pbxproj b/Dear-World/Dear-World.xcodeproj/project.pbxproj index a96fe82..432d144 100644 --- a/Dear-World/Dear-World.xcodeproj/project.pbxproj +++ b/Dear-World/Dear-World.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ 3902F1132597049D00A3DF8C /* SendMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3902F1122597049D00A3DF8C /* SendMessageViewController.swift */; }; 3902F118259704AF00A3DF8C /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3902F117259704AF00A3DF8C /* AboutViewController.swift */; }; 3902F12425970E5600A3DF8C /* Int+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3902F12325970E5600A3DF8C /* Int+.swift */; }; - 3902F12A259714D800A3DF8C /* CountryTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3902F129259714D800A3DF8C /* CountryTableViewCell.swift */; }; + 3902F12A259714D800A3DF8C /* RankerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3902F129259714D800A3DF8C /* RankerTableViewCell.swift */; }; 3958257B25948E41007325AB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3958257A25948E41007325AB /* AppDelegate.swift */; }; 3958258425948E43007325AB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3958258325948E43007325AB /* Assets.xcassets */; }; 3958258725948E43007325AB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3958258525948E43007325AB /* LaunchScreen.storyboard */; }; @@ -37,6 +37,10 @@ 39658F92259AD9B70050D180 /* SplashReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658F91259AD9B70050D180 /* SplashReactor.swift */; }; 39658F9B259ADB130050D180 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 39658F9A259ADB130050D180 /* Lottie */; }; 39658FA0259ADE770050D180 /* splash_1x.json in Resources */ = {isa = PBXBuildFile; fileRef = 39658F9F259ADE770050D180 /* splash_1x.json */; }; + 39658FAF259AE4E40050D180 /* World.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658FAE259AE4E40050D180 /* World.swift */; }; + 39658FB6259AE5250050D180 /* World.API.Rank.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658FB5259AE5250050D180 /* World.API.Rank.swift */; }; + 39658FBB259AE55C0050D180 /* World.Model.Rank.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658FBA259AE55C0050D180 /* World.Model.Rank.swift */; }; + 39658FC0259AE58C0050D180 /* World.Model.Country.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39658FBF259AE58C0050D180 /* World.Model.Country.swift */; }; 39672DB32598D139001D7E69 /* SelectCountryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39672DB22598D139001D7E69 /* SelectCountryView.swift */; }; 39672DBC2598E803001D7E69 /* UITextView+Placeholder in Frameworks */ = {isa = PBXBuildFile; productRef = 39672DBB2598E803001D7E69 /* UITextView+Placeholder */; }; 39672DC12598E95A001D7E69 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39672DC02598E95A001D7E69 /* TextView.swift */; }; @@ -51,6 +55,9 @@ 39C832282597651F00236DDF /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 39C832272597651F00236DDF /* RxRelay */; }; 39C8322A2597651F00236DDF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 39C832292597651F00236DDF /* RxSwift */; }; 39C8322C2597651F00236DDF /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 39C8322B2597651F00236DDF /* RxCocoa */; }; + 39EED22E259CFEB8007452E1 /* World.Model.Ranker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EED22D259CFEB8007452E1 /* World.Model.Ranker.swift */; }; + 39EED23C259D00F8007452E1 /* CheeringMapReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EED23B259D00F8007452E1 /* CheeringMapReactor.swift */; }; + 39EED241259D0227007452E1 /* Revision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EED240259D0227007452E1 /* Revision.swift */; }; 39F0C1632597B34F00A7001F /* UIView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0C1622597B34F00A7001F /* UIView+.swift */; }; 39F0C16B2597C7DF00A7001F /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0C16A2597C7DF00A7001F /* UIImage+.swift */; }; 39F0C176259860D700A7001F /* UIControl+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F0C175259860D700A7001F /* UIControl+Rx.swift */; }; @@ -89,13 +96,13 @@ 121BDB6B2597A49B0062B15A /* MessageMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageMock.swift; sourceTree = ""; }; 121BDB702597A5D60062B15A /* APIMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIMock.swift; sourceTree = ""; }; 121BDB8D259829840062B15A /* CountrySelectController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountrySelectController.swift; sourceTree = ""; }; - 128313E22598A08700BDF8A3 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Message.swift; path = "Dear-World/Source/Domain/Message/Message.swift"; sourceTree = SOURCE_ROOT; }; + 128313E22598A08700BDF8A3 /* Message.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; name = Message.swift; path = "Dear-World/Source/Domain/Message/Message.swift"; sourceTree = SOURCE_ROOT; tabWidth = 2; }; 128313ED2598A14E00BDF8A3 /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = ""; }; 3902F1042596F26D00A3DF8C /* CheeringMapViewController.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = CheeringMapViewController.swift; sourceTree = ""; tabWidth = 2; }; 3902F1122597049D00A3DF8C /* SendMessageViewController.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SendMessageViewController.swift; sourceTree = ""; tabWidth = 2; }; 3902F117259704AF00A3DF8C /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 3902F12325970E5600A3DF8C /* Int+.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = "Int+.swift"; sourceTree = ""; tabWidth = 2; }; - 3902F129259714D800A3DF8C /* CountryTableViewCell.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = CountryTableViewCell.swift; sourceTree = ""; tabWidth = 2; }; + 3902F129259714D800A3DF8C /* RankerTableViewCell.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = RankerTableViewCell.swift; sourceTree = ""; tabWidth = 2; }; 3958257725948E41007325AB /* Dear-World.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Dear-World.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 3958257A25948E41007325AB /* AppDelegate.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; tabWidth = 2; }; 3958258325948E43007325AB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -113,6 +120,10 @@ 39658F8C259AD9900050D180 /* SplashViewController.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SplashViewController.swift; sourceTree = ""; tabWidth = 2; }; 39658F91259AD9B70050D180 /* SplashReactor.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SplashReactor.swift; sourceTree = ""; tabWidth = 2; }; 39658F9F259ADE770050D180 /* splash_1x.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = splash_1x.json; sourceTree = ""; }; + 39658FAE259AE4E40050D180 /* World.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = World.swift; sourceTree = ""; tabWidth = 2; }; + 39658FB5259AE5250050D180 /* World.API.Rank.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = World.API.Rank.swift; sourceTree = ""; tabWidth = 2; }; + 39658FBA259AE55C0050D180 /* World.Model.Rank.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = World.Model.Rank.swift; sourceTree = ""; tabWidth = 2; }; + 39658FBF259AE58C0050D180 /* World.Model.Country.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = World.Model.Country.swift; sourceTree = ""; tabWidth = 2; }; 39672DB22598D139001D7E69 /* SelectCountryView.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SelectCountryView.swift; sourceTree = ""; tabWidth = 2; }; 39672DC02598E95A001D7E69 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = ""; }; 3971EB17259A7BC10084E6DC /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = ""; }; @@ -121,6 +132,9 @@ 3971EB28259A7D720084E6DC /* SendMessageReactor.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = SendMessageReactor.swift; sourceTree = ""; tabWidth = 2; }; 3971EB36259A9C550084E6DC /* Message.API.SendMessage.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = Message.API.SendMessage.swift; sourceTree = ""; tabWidth = 2; }; 3971EB3B259A9C860084E6DC /* Message.Model.SendMessage.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; name = Message.Model.SendMessage.swift; path = "Dear-World/Source/Domain/Message/API/Message.Model.SendMessage.swift"; sourceTree = SOURCE_ROOT; tabWidth = 2; }; + 39EED22D259CFEB8007452E1 /* World.Model.Ranker.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = World.Model.Ranker.swift; sourceTree = ""; tabWidth = 2; }; + 39EED23B259D00F8007452E1 /* CheeringMapReactor.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = CheeringMapReactor.swift; sourceTree = ""; tabWidth = 2; }; + 39EED240259D0227007452E1 /* Revision.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Revision.swift; sourceTree = ""; }; 39F0C1622597B34F00A7001F /* UIView+.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = "UIView+.swift"; sourceTree = ""; tabWidth = 2; }; 39F0C16A2597C7DF00A7001F /* UIImage+.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; tabWidth = 2; }; 39F0C175259860D700A7001F /* UIControl+Rx.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = "UIControl+Rx.swift"; sourceTree = ""; tabWidth = 2; }; @@ -174,6 +188,7 @@ isa = PBXGroup; children = ( 3902F14F2597531600A3DF8C /* Extension */, + 39EED240259D0227007452E1 /* Revision.swift */, ); path = Common; sourceTree = ""; @@ -216,7 +231,7 @@ 3902F128259714AB00A3DF8C /* Cell */ = { isa = PBXGroup; children = ( - 3902F129259714D800A3DF8C /* CountryTableViewCell.swift */, + 3902F129259714D800A3DF8C /* RankerTableViewCell.swift */, ); path = Cell; sourceTree = ""; @@ -324,6 +339,7 @@ 395825B125948E98007325AB /* Domain */ = { isa = PBXGroup; children = ( + 39658FAD259AE4D30050D180 /* World */, 3971EB16259A7BB10084E6DC /* Emoji */, 128313EA2598A0C800BDF8A3 /* Message */, ); @@ -381,6 +397,7 @@ children = ( 3902F128259714AB00A3DF8C /* Cell */, 3902F1042596F26D00A3DF8C /* CheeringMapViewController.swift */, + 39EED23B259D00F8007452E1 /* CheeringMapReactor.swift */, ); path = "Cheering Map"; sourceTree = ""; @@ -411,6 +428,34 @@ path = Splash; sourceTree = ""; }; + 39658FAD259AE4D30050D180 /* World */ = { + isa = PBXGroup; + children = ( + 39658FB4259AE50C0050D180 /* API */, + 39658FB3259AE5080050D180 /* Model */, + 39658FAE259AE4E40050D180 /* World.swift */, + ); + path = World; + sourceTree = ""; + }; + 39658FB3259AE5080050D180 /* Model */ = { + isa = PBXGroup; + children = ( + 39658FBA259AE55C0050D180 /* World.Model.Rank.swift */, + 39EED22D259CFEB8007452E1 /* World.Model.Ranker.swift */, + 39658FBF259AE58C0050D180 /* World.Model.Country.swift */, + ); + path = Model; + sourceTree = ""; + }; + 39658FB4259AE50C0050D180 /* API */ = { + isa = PBXGroup; + children = ( + 39658FB5259AE5250050D180 /* World.API.Rank.swift */, + ); + path = API; + sourceTree = ""; + }; 3971EB16259A7BB10084E6DC /* Emoji */ = { isa = PBXGroup; children = ( @@ -629,18 +674,21 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 39EED241259D0227007452E1 /* Revision.swift in Sources */, 121BDB712597A5D60062B15A /* APIMock.swift in Sources */, 39672DB32598D139001D7E69 /* SelectCountryView.swift in Sources */, 3902F118259704AF00A3DF8C /* AboutViewController.swift in Sources */, 3902F12425970E5600A3DF8C /* Int+.swift in Sources */, + 39EED23C259D00F8007452E1 /* CheeringMapReactor.swift in Sources */, 39F0C18B2598799200A7001F /* Network.swift in Sources */, 3971EB37259A9C550084E6DC /* Message.API.SendMessage.swift in Sources */, 395826122596322B007325AB /* DiscoverViewController.swift in Sources */, 3971EB18259A7BC10084E6DC /* Emoji.swift in Sources */, + 39EED22E259CFEB8007452E1 /* World.Model.Ranker.swift in Sources */, 3902F1132597049D00A3DF8C /* SendMessageViewController.swift in Sources */, 39F0C1A02598877600A7001F /* NetworkError.swift in Sources */, 121BDB592597652A0062B15A /* MessageTableViewCell.swift in Sources */, - 3902F12A259714D800A3DF8C /* CountryTableViewCell.swift in Sources */, + 3902F12A259714D800A3DF8C /* RankerTableViewCell.swift in Sources */, 395826332596E693007325AB /* Logger.swift in Sources */, 39F0C19025987A2B00A7001F /* ServiceAPI.swift in Sources */, 3958257B25948E41007325AB /* AppDelegate.swift in Sources */, @@ -648,10 +696,13 @@ 128313EE2598A14E00BDF8A3 /* List.swift in Sources */, 128313E32598A08700BDF8A3 /* Message.swift in Sources */, 121BDB44259735200062B15A /* UIColor+.swift in Sources */, + 39658FBB259AE55C0050D180 /* World.Model.Rank.swift in Sources */, + 39658FB6259AE5250050D180 /* World.API.Rank.swift in Sources */, 39658F8D259AD9900050D180 /* SplashViewController.swift in Sources */, 3971EB29259A7D720084E6DC /* SendMessageReactor.swift in Sources */, 39F0C19B2598860100A7001F /* ResponseWrapper.swift in Sources */, 3971EB3C259A9C860084E6DC /* Message.Model.SendMessage.swift in Sources */, + 39658FAF259AE4E40050D180 /* World.swift in Sources */, 3971EB24259A7C420084E6DC /* Emoji.Model.Random.swift in Sources */, 39F0C1632597B34F00A7001F /* UIView+.swift in Sources */, 39658F92259AD9B70050D180 /* SplashReactor.swift in Sources */, @@ -663,6 +714,7 @@ 121BDB2A2597259E0062B15A /* MessageCountBadgeView.swift in Sources */, 39F0C16B2597C7DF00A7001F /* UIImage+.swift in Sources */, 121BDB6C2597A49B0062B15A /* MessageMock.swift in Sources */, + 39658FC0259AE58C0050D180 /* World.Model.Country.swift in Sources */, 121BDB8E259829840062B15A /* CountrySelectController.swift in Sources */, 121BDB672597982F0062B15A /* DiscoverReactor.swift in Sources */, ); diff --git a/Dear-World/Dear-World/Source/Common/Revision.swift b/Dear-World/Dear-World/Source/Common/Revision.swift new file mode 100644 index 0000000..ff5f734 --- /dev/null +++ b/Dear-World/Dear-World/Source/Common/Revision.swift @@ -0,0 +1,29 @@ +// +// Revision.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/31. +// + +import Foundation + +@propertyWrapper +struct Revision: Equatable { + var projectedValue: UInt = 0 + var wrappedValue: T { + didSet { projectedValue += 1 } + } +} +extension Revision { + static func == (lhs: Revision, rhs: Revision) -> Bool { + let r1 = lhs.projectedValue == rhs.projectedValue + return r1 + } +} +extension Revision where T: Equatable { + static func == (lhs: Revision, rhs: Revision) -> Bool { + let r1 = lhs.projectedValue == rhs.projectedValue + let r2 = lhs.wrappedValue == rhs.wrappedValue + return r1 && r2 + } +} diff --git a/Dear-World/Dear-World/Source/Core/AppDelegate.swift b/Dear-World/Dear-World/Source/Core/AppDelegate.swift index 164f87c..c010afa 100644 --- a/Dear-World/Dear-World/Source/Core/AppDelegate.swift +++ b/Dear-World/Dear-World/Source/Core/AppDelegate.swift @@ -22,7 +22,10 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { let mainWindow: UIWindow = UIWindow() window = mainWindow - mainWindow.rootViewController = SplashViewController() + let viewController = CheeringMapViewController() + let reactor = CheeringMapReactor() + viewController.reactor = reactor + mainWindow.rootViewController = viewController mainWindow.makeKeyAndVisible() return true } diff --git a/Dear-World/Dear-World/Source/Domain/Emoji/API/Emoji.API.Random.swift b/Dear-World/Dear-World/Source/Domain/Emoji/API/Emoji.API.Random.swift index 3d618c0..007feed 100644 --- a/Dear-World/Dear-World/Source/Domain/Emoji/API/Emoji.API.Random.swift +++ b/Dear-World/Dear-World/Source/Domain/Emoji/API/Emoji.API.Random.swift @@ -13,6 +13,6 @@ extension Emoji.API { typealias Response = Emoji.Model.Random var method: HTTPMethod { .get } - var path: String { "emojis/random" } + var path: String { "api/v1/emojis/random" } } } diff --git a/Dear-World/Dear-World/Source/Domain/Message/API/Message.API.SendMessage.swift b/Dear-World/Dear-World/Source/Domain/Message/API/Message.API.SendMessage.swift index 065e417..830a54b 100644 --- a/Dear-World/Dear-World/Source/Domain/Message/API/Message.API.SendMessage.swift +++ b/Dear-World/Dear-World/Source/Domain/Message/API/Message.API.SendMessage.swift @@ -18,7 +18,7 @@ extension Message.API { private let message: String var method: HTTPMethod { .post } - var path: String { "messages" } + var path: String { "api/v1/messages" } var parameters: [String: Any]? { ["countryCode": countryCode, "emojiId": emojiId.description, diff --git a/Dear-World/Dear-World/Source/Domain/Message/Message.swift b/Dear-World/Dear-World/Source/Domain/Message/Message.swift index d59c9b4..1a17753 100644 --- a/Dear-World/Dear-World/Source/Domain/Message/Message.swift +++ b/Dear-World/Dear-World/Source/Domain/Message/Message.swift @@ -8,6 +8,6 @@ import Foundation enum Message { - enum API {} - enum Model {} + enum API {} + enum Model {} } diff --git a/Dear-World/Dear-World/Source/Domain/World/API/World.API.Rank.swift b/Dear-World/Dear-World/Source/Domain/World/API/World.API.Rank.swift new file mode 100644 index 0000000..680dced --- /dev/null +++ b/Dear-World/Dear-World/Source/Domain/World/API/World.API.Rank.swift @@ -0,0 +1,18 @@ +// +// World.API.Rank.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/29. +// + +import Alamofire +import Foundation + +extension World.API { + struct Rank: ServiceAPI { + typealias Response = World.Model.Rank + + var method: HTTPMethod { .get } + var path: String { "api/v1/countries/rank" } + } +} diff --git a/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Country.swift b/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Country.swift new file mode 100644 index 0000000..9439376 --- /dev/null +++ b/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Country.swift @@ -0,0 +1,24 @@ +// +// World.Model.Country.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/29. +// + +import Foundation + +extension World.Model { + struct Country: Decodable { + let id: Int + let code: String + let name: String + let emoji: String + + enum CodingKeys: String, CodingKey { + case id + case code + case name = "fullName" + case emoji = "emojiUnicode" + } + } +} diff --git a/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Rank.swift b/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Rank.swift new file mode 100644 index 0000000..58421bd --- /dev/null +++ b/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Rank.swift @@ -0,0 +1,15 @@ +// +// World.Model.Rank.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/29. +// + +import Foundation + +extension World.Model { + struct Rank: Decodable { + let ranking: [Ranker] + } + +} diff --git a/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Ranker.swift b/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Ranker.swift new file mode 100644 index 0000000..03fe753 --- /dev/null +++ b/Dear-World/Dear-World/Source/Domain/World/Model/World.Model.Ranker.swift @@ -0,0 +1,18 @@ +// +// World.Model.Ranker.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/31. +// + +import Foundation + +extension World.Model { + struct Ranker: Decodable { + let messageCount: Int + let likeCount: Int + let population: Int? + let level: String + let country: Country + } +} diff --git a/Dear-World/Dear-World/Source/Domain/World/World.swift b/Dear-World/Dear-World/Source/Domain/World/World.swift new file mode 100644 index 0000000..de401fb --- /dev/null +++ b/Dear-World/Dear-World/Source/Domain/World/World.swift @@ -0,0 +1,13 @@ +// +// World.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/29. +// + +import Foundation + +enum World { + enum API {} + enum Model {} +} diff --git a/Dear-World/Dear-World/Source/Network/ServiceAPI.swift b/Dear-World/Dear-World/Source/Network/ServiceAPI.swift index dc4af23..039b002 100644 --- a/Dear-World/Dear-World/Source/Network/ServiceAPI.swift +++ b/Dear-World/Dear-World/Source/Network/ServiceAPI.swift @@ -18,7 +18,7 @@ protocol ServiceAPI: URLRequestConvertible { } extension ServiceAPI { var baseURL: URL { - URL(string: "http://52.79.222.79/api/v1/")! + URL(string: "http://52.79.222.79/")! } var parameters: [String: Any]? { nil } diff --git a/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/Cell/CountryTableViewCell.swift b/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/Cell/RankerTableViewCell.swift similarity index 77% rename from Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/Cell/CountryTableViewCell.swift rename to Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/Cell/RankerTableViewCell.swift index 9341672..b245ca8 100644 --- a/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/Cell/CountryTableViewCell.swift +++ b/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/Cell/RankerTableViewCell.swift @@ -8,10 +8,22 @@ import RxCocoa import RxSwift import SnapKit +import SwiftRichString import Then import UIKit -final class CountryTableViewCell: UITableViewCell { +final class RankerTableViewCell: UITableViewCell { + + enum Styles { + static let topOf3 = Style { + $0.font = UIFont.boldSystemFont(ofSize: 12) + $0.color = UIColor.livelyBlue + } + static let normal = Style { + $0.font = UIFont.boldSystemFont(ofSize: 12) + $0.color = UIColor.warmBlue + } + } // MARK: 🖼 UI let rankLabel: UILabel = UILabel() @@ -108,9 +120,20 @@ final class CountryTableViewCell: UITableViewCell { } // MARK: 🔩 Configuration - func configure() { + func configure(with ranker: World.Model.Ranker, ranking: Int) { // FIXME: 🔮 더미 데이터 변경 - countryFlagLabel.text = "🇰🇷" - messageCountLabel.text = 952_682_252.formatted + rankLabel.attributedText = formatRank(ranking) + countryNameLabel.text = ranker.country.name + countryFlagLabel.text = ranker.country.emoji + messageCountLabel.text = ranker.messageCount.formatted + } + + private func formatRank(_ rank: Int) -> NSAttributedString { + switch rank { + case 1: return "\(rank)st".set(style: Styles.topOf3) + case 2: return "\(rank)nd".set(style: Styles.topOf3) + case 3: return "\(rank)rd".set(style: Styles.topOf3) + default: return "\(rank)th".set(style: Styles.normal) + } } } diff --git a/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapReactor.swift b/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapReactor.swift new file mode 100644 index 0000000..339789b --- /dev/null +++ b/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapReactor.swift @@ -0,0 +1,55 @@ +// +// CheeringMapReactor.swift +// Dear-World +// +// Created by dongyoung.lee on 2020/12/31. +// + +import Foundation +import ReactorKit + +final class CheeringMapReactor: Reactor { + + typealias API = World.API + typealias Model = World.Model + + enum Action { + case viewDidLoad + case tapLikeAt(index: Int) + } + + enum Mutation { + case setRankers([Model.Ranker]) + } + + struct State { + var messageCount: Int = -1 + @Revision var rankers: [Model.Ranker] = [] + @Revision var selectedCountries: [Model.Country] = [] + } + + var initialState: State = State() + func mutate(action: Action) -> Observable { + switch action { + case .viewDidLoad: + return Network.request(API.Rank()) + .map { $0?.ranking } + .filterNil() + .map { .setRankers($0) } + + case .tapLikeAt(let index): + return .empty() + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = currentState + switch mutation { + case .setRankers(let rankers): + newState.rankers = rankers + default: + () + } + return newState + } +} diff --git a/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapViewController.swift b/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapViewController.swift index c9b5f85..6a4d309 100644 --- a/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapViewController.swift +++ b/Dear-World/Dear-World/Source/Presentation/Scene/Cheering Map/CheeringMapViewController.swift @@ -5,6 +5,7 @@ // Created by dongyoung.lee on 2020/12/26. // +import ReactorKit import RxCocoa import RxGesture import RxOptional @@ -13,7 +14,10 @@ import SnapKit import Then import UIKit -final class CheeringMapViewController: UIViewController { +final class CheeringMapViewController: UIViewController, ReactorKit.View { + + typealias Reactor = CheeringMapReactor + typealias Action = Reactor.Action // MARK: 🖼 UI private let messageCountBadgeView: MessageCountBadgeView = MessageCountBadgeView() @@ -27,7 +31,7 @@ final class CheeringMapViewController: UIViewController { private var worldMapHeight: CGFloat = 208 private var canScrollTableView: Bool = false - private let disposeBag: DisposeBag = DisposeBag() + var disposeBag: DisposeBag = DisposeBag() // MARK: 🏁 Initialize init() { @@ -42,6 +46,30 @@ final class CheeringMapViewController: UIViewController { setupUI() } + func bind(reactor: CheeringMapReactor) { + reactor.state + .distinctUntilChanged(\.$rankers) + .map { $0.rankers } + .bind(to: rankingTableView.rx.items(cellIdentifier: "RankerTableViewCell", cellType: RankerTableViewCell.self)) { + [weak self] index, ranker, cell in + cell.configure(with: ranker, ranking: index + 1) + cell.cheerUpButton.anchorView = self?.view + }.disposed(by: disposeBag) + + rankingTableView.rx.didScroll + .map { [weak self] in self?.rankingTableView.contentOffset.y } + .filterNil() + .throttle(.milliseconds(40), scheduler: MainScheduler.instance) + .observeOn(MainScheduler.instance) + .subscribe(onNext: { [weak self] y in + guard let self = self else { return } + self.updateLayouts(y) + }) + .disposed(by: disposeBag) + + reactor.action.onNext(.viewDidLoad) + } + // MARK: 📍 Setup private func setupUI() { self.view.backgroundColor = .breathingWhite @@ -51,23 +79,6 @@ final class CheeringMapViewController: UIViewController { $0.top.equalToSuperview().inset(60) } - let logoImageView: UIImageView = UIImageView().then { - $0.image = UIImage(named: "earth") - } - self.view.addSubview(logoImageView) - logoImageView.snp.makeConstraints { - $0.bottom.equalToSuperview() - } - - cheeringCountLabel.do { - // FIXME: 🔮 더미 데이터 변경 - $0.text = 353_513.formatted - } - self.view.addSubview(cheeringCountLabel) - cheeringCountLabel.snp.makeConstraints { - $0.bottom.equalToSuperview() - } - titleLabel.do { $0.text = "Cheering Map" $0.font = .boldSystemFont(ofSize: 22) @@ -119,26 +130,17 @@ final class CheeringMapViewController: UIViewController { rankingTableView.do { $0.backgroundColor = .white $0.showsVerticalScrollIndicator = false - $0.register(CountryTableViewCell.self, forCellReuseIdentifier: "CountryTableViewCell") + $0.register(RankerTableViewCell.self, forCellReuseIdentifier: "RankerTableViewCell") $0.rowHeight = UITableView.automaticDimension $0.estimatedRowHeight = 64 - $0.dataSource = self $0.allowsSelection = false $0.separatorStyle = .none } rankingTableView.snp.makeConstraints { $0.leading.trailing.bottom.equalTo(self.view.safeAreaLayoutGuide) + $0.bottom.equalToSuperview() $0.top.equalTo(headerView.snp.bottom) } - rankingTableView.rx.contentOffset - .map { $0.y } - .throttle(.milliseconds(40), scheduler: MainScheduler.instance) - .observeOn(MainScheduler.instance) - .subscribe(onNext: { [weak self] y in - guard let self = self else { return } - self.updateLayouts(y) - }) - .disposed(by: disposeBag) } private func updateLayouts(_ tableViewOffsetY: CGFloat) { @@ -156,18 +158,3 @@ final class CheeringMapViewController: UIViewController { } } } -extension CheeringMapViewController: UITableViewDataSource { - func tableView( - _ tableView: UITableView, - numberOfRowsInSection section: Int - ) -> Int { 10 } - - func tableView( - _ tableView: UITableView, - cellForRowAt indexPath: IndexPath - ) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "CountryTableViewCell", for: indexPath) as! CountryTableViewCell - cell.cheerUpButton.anchorView = self.view - return cell - } -} diff --git a/Dear-World/Dear-World/Source/Presentation/Scene/Send Message/SendMessageReactor.swift b/Dear-World/Dear-World/Source/Presentation/Scene/Send Message/SendMessageReactor.swift index 48b38a4..8fd61ce 100644 --- a/Dear-World/Dear-World/Source/Presentation/Scene/Send Message/SendMessageReactor.swift +++ b/Dear-World/Dear-World/Source/Presentation/Scene/Send Message/SendMessageReactor.swift @@ -71,7 +71,7 @@ final class SendMessageReactor: Reactor { return .just(.setMessage(message)) case .tapSendMessage: - let api = Message.API.SendMessage( + let api: Message.API.SendMessage = Message.API.SendMessage( countryCode: "KR", emojiId: currentState.emojiId, name: currentState.name,