Skip to content

Instantly share code, notes, and snippets.

@MostafaMazrouh
Last active January 24, 2022 15:58
Show Gist options
  • Save MostafaMazrouh/7aa4e3a17829e7e9b6b346b727397986 to your computer and use it in GitHub Desktop.
Save MostafaMazrouh/7aa4e3a17829e7e9b6b346b727397986 to your computer and use it in GitHub Desktop.
MassiveToBe: Part 1
import Foundation
import presentation
class AppDI: AppDIInterface {
static let shared = AppDI(appEnvironment: AppEnvironment())
let appEnvironment: AppEnvironment
init(appEnvironment: AppEnvironment) {
self.appEnvironment = appEnvironment
}
func postDependencies() -> PostVM {
let postDI: PostDI = PostDI(appEnvironment: appEnvironment)
let postVM = postDI.postDependencies()
return postVM
}
}
import Foundation
import presentation
class AppDI: AppDIInterface {
static let shared = AppDI(appEnvironment: AppEnvironment())
let appEnvironment: AppEnvironment
init(appEnvironment: AppEnvironment) {
self.appEnvironment = appEnvironment
}
func postDependencies() -> PostVM {
let postDI: PostDI = PostDI(appEnvironment: appEnvironment)
let postVM = postDI.postDependencies()
return postVM
}
func postDetailsDependencies() -> PostDetailsVM {
let postDetailsDI: PostDetailsDI = PostDetailsDI(appEnvironment: appEnvironment)
let postDetailsVM = postDetailsDI.postDetailsDependencies()
return postDetailsVM
}
}
public protocol AppDIInterface {
func postDependencies() -> PostVM
func postDetailsDependencies() -> PostDetailsVM
}
import Foundation
class AppEnvironment {
let baseURL = "https://jsonplaceholder.typicode.com/posts"
}
import Foundation
import domain
public class PostDataRepo: PostDomainRepoInterface {
let postRemoteDataSource: PostRemoteDataSourceInterface
let postLocalDataSource: PostLocalDataSourceInterface?
let coder: Coder
public init(postRemoteDataSource: PostRemoteDataSourceInterface,
postLocalDataSource: PostLocalDataSourceInterface? = nil,
coder: Coder = Coder()) {
self.postRemoteDataSource = postRemoteDataSource
self.postLocalDataSource = postLocalDataSource
self.coder = coder
}
public func getPosts(handler: @escaping ([PostEntity]) -> ()) {
postRemoteDataSource.getPosts { (postModels) in
var postEntities = [PostEntity]()
for postModel in postModels {
postEntities.append(postModel.dotPostEntity())
}
handler(postEntities)
}
}
}
import presentation
import domain
import data
class PostDetailsDI {
let appEnvironment: AppEnvironment
init(appEnvironment: AppEnvironment) {
self.appEnvironment = appEnvironment
}
func postDependencies() -> PostDetailsVM{
// Presentation
let postDetailsVM = PostDetailsVM()
return postDetailsVM
}
}
struct PostDetailsView: View {
@ObservedObject public var postDetailsVM: PostDetailsVM
public init(postDetailsVM: PostDetailsVM) {
self.postDetailsVM = postDetailsVM
}
var body: some View {
Text(postDetailsVM.details)
}
}
public class PostDetailsVM: ObservableObject {
@Published var details = "My details"
// So we can initialize it from the app layer
public init() { }
}
import presentation
import domain
import data
class PostDI {
let appEnvironment: AppEnvironment
init(appEnvironment: AppEnvironment) {
self.appEnvironment = appEnvironment
}
func postDependencies() -> PostVM {
// Data Layer
let baseURL = appEnvironment.baseURL
// Post Data Source
let postRemoteDataSource = PostRemoteDataSource(urlString: baseURL)
// Post Data Repo
let postDataRepo = PostDataRepo(postRemoteDataSource: postRemoteDataSource)
// Domain Layer
let postInteractor = PostInteractor(postDomainRepo: postDataRepo)
// Presentation
let postVM = PostVM(postInteractor: postInteractor)
return postVM
}
}
import Foundation
public protocol PostDomainRepoInterface {
func getPosts(handler: @escaping ([PostEntity]) -> ())
}
import Foundation
public struct PostEntity: Identifiable {
public let userId: Int?
public let id: Int?
public let title: String?
public let body: String
public init(userId: Int?, id: Int?, title: String?, body: String) {
self.userId = userId
self.id = id
self.title = title
self.body = body
}
}
import Foundation
public protocol PostInteractorInterface {
func getPosts(handler: @escaping ([PostEntity]) -> ())
}
public class PostInteractor: PostInteractorInterface {
let postDomainRepo: PostDomainRepoInterface
public init(postDomainRepo: PostDomainRepoInterface) {
self.postDomainRepo = postDomainRepo
}
public func getPosts(handler: @escaping ([PostEntity]) -> ()) {
postDomainRepo.getPosts { (PostDomainModelArray) in
handler(PostDomainModelArray)
}
}
}
import Foundation
import domain
public protocol PostLocalDataSourceInterface {
func getCachedPosts(handler: @escaping ([PostEntity]) -> ())
}
import Foundation
import domain
public struct PostModel: Codable {
public let userId: Int?
public let id: Int?
public let title: String?
public let body: String
// DOT: Data Object Transfer
public func dotPostEntity() -> PostEntity {
return PostEntity(userId: userId, id: id, title: title, body: body)
}
}
import Foundation
import domain
public protocol PostRemoteDataSourceInterface {
init(urlString: String, coder: Coder)
func getPosts(handler: @escaping ([PostModel]) -> ())
}
public class PostRemoteDataSource: PostRemoteDataSourceInterface {
let urlString: String
let coder: Coder
required public init(urlString: String, coder: Coder = Coder()) {
self.urlString = urlString
self.coder = coder
}
public func getPosts(handler: @escaping ([PostModel]) -> ()) {
guard let url = URL(string: urlString) else {
return handler([])
}
let task = URLSession.shared.dataTask(with: url) { [weak self] (data, urlResponse, error) in
guard let data = data else {
return handler([])
}
guard let postModels = self?.coder.decode(toType: [PostModel].self, from: data) else {
return handler([])
}
handler(postModels)
}
task.resume()
}
}
import SwiftUI
struct PostView: View {
@ObservedObject var postVM: PostVM
public var body: some View {
NavigationView {
List {
ForEach(postVM.posts) { post in
VStack{
Text(post.title ?? "")
.font(.largeTitle)
.multilineTextAlignment(.center)
Text(post.body )
.font(.body)
.multilineTextAlignment(.center)
}
}
}
.navigationBarTitle("Posts")
}
.onAppear {
self.postVM.getPosts()
}
}
}
import SwiftUI
public struct PostView: View {
var appDI: AppDIInterface
@ObservedObject public var postVM: PostVM
@State var detailsIsPresented: Bool = false
public init(appDI: AppDIInterface, postVM: PostVM) {
self.appDI = appDI
self.postVM = postVM
}
public var body: some View {
NavigationView {
List {
ForEach(postVM.posts) { post in
VStack{
Text(post.title ?? "")
.font(.largeTitle)
.multilineTextAlignment(.center)
Text(post.body )
.font(.body)
.multilineTextAlignment(.center)
}
}.onTapGesture {
self.detailsIsPresented = true
}
}
.sheet(isPresented: $detailsIsPresented, content: {
PostDetailsView(postDetailsVM: self.appDI.postDetailsDependencies())
})
.navigationBarTitle("Posts")
}
.onAppear {
self.postVM.getPosts()
}
}
}
import Foundation
import domain
public class PostVM: ObservableObject {
@Published var posts: [PostEntity] = []
private var postInteractor: PostInteractorInterface
public init(postInteractor: PostInteractorInterface) {
self.postInteractor = postInteractor
}
func getPosts() {
self.postInteractor.getPosts { [weak self] (postArray) in
DispatchQueue.main.async {
self?.posts = postArray
}
}
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let postView = PostView(postVM: AppDI.shared.postDependencies())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: postView)
self.window = window
window.makeKeyAndVisible()
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let postView = PostView(appDI: AppDI.shared, postVM: AppDI.shared.postDependencies())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: postView)
self.window = window
window.makeKeyAndVisible()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment