Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // SceneDelegate.swift
- func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
- // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
- // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
- // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
- // Use a UIHostingController as window root view controller
- if let windowScene = scene as? UIWindowScene {
- let window = UIWindow(windowScene: windowScene)
- let store = Store()
- window.rootViewController = UIHostingController(rootView: RepositoryList(store: store))
- self.window = window
- window.makeKeyAndVisible()
- }
- }
- //
- // App.swift
- import SwiftUI
- import Combine
- struct RepositoryList : View {
- @ObservedObject var store: Store
- func move(from source: IndexSet, to destination: Int) {
- store.repositories.swapAt(source.first!, destination)
- }
- @State private var isTapped = false
- @State private var bgColor = Color.white
- var body: some View {
- NavigationView {
- VStack {
- List {
- Section(header: Text("\(String(store.repositories.count)) repositories")) {
- ForEach(store.repositories) { repository in
- NavigationLink(destination: RepositoryDetail(store: self.store, repository: repository)) {
- RepositoryRow(repository: repository)
- }.padding(.vertical, 8.0)
- }.onDelete { index in
- self.store.repositories.remove(at: index.first!)
- }.onMove(perform: move)
- }
- }
- }
- }.navigationBarTitle("Github user").navigationBarItems(trailing: EditButton())
- }
- }
- struct RepositoryDetail : View {
- @ObservedObject var store: Store
- var repository: Repository
- var repoIndex: Int {
- let repoIndex = store.repositories.firstIndex(where: {$0.id == repository.id})!
- print("### IS FAVORITE \(String(store.repositories[repoIndex].isFavorite))")
- return repoIndex
- }
- var body: some View {
- VStack(alignment: .leading) {
- Text(String(store.repositories[repoIndex].isFavorite))
- Button(action: { self.store.repositories[self.repoIndex].isFavorite.toggle() }) {
- if (self.store.repositories[self.repoIndex].isFavorite) {
- Image(systemName: "star.fill").foregroundColor(Color.yellow)
- } else {
- Image(systemName: "star").foregroundColor(Color.gray)
- }
- }
- }.navigationBarTitle(Text(repository.name), displayMode: .inline)
- }
- }
- class Store: ObservableObject {
- private var cancellable: AnyCancellable? = nil
- @Published var repositories: [Repository] = []
- init () {
- var urlComponents = URLComponents(string: "https://api.github.com/users/Hurobaki/repos")!
- var request = URLRequest(url: urlComponents.url!)
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
- cancellable = URLSession.shared.send(request: request)
- .decode(type: [Repository].self, decoder: JSONDecoder())
- .receive(on: DispatchQueue.main)
- .sink(receiveCompletion: { completion in
- print("### .sink() received the completion", String(describing: completion))
- switch completion {
- case .finished:
- break
- case .failure(_):
- print("### ERROR")
- }
- }, receiveValue: { repositories in
- self.repositories = repositories
- })
- }
- }
- class Repository: Decodable, Identifiable {
- var id: Int = 0
- var name: String = ""
- var desc: String? = nil
- var isFavorite = false
- private enum CodingKeys: String, CodingKey {
- case id, name
- case desc = "description"
- }
- }
- struct RepositoryRow : View {
- var repository: Repository
- var body: some View {
- HStack(spacing: 12.0) {
- Image(systemName: "circle")
- .resizable()
- .aspectRatio(contentMode: .fit)
- .frame(width: 80, height: 80)
- .cornerRadius(20)
- VStack(alignment: .leading) {
- Text(repository.name).font(.headline)
- Text(repository.desc ?? "No description provided.").lineLimit(2).lineSpacing(4).font(.subheadline).frame(height: 50.0)
- }
- }
- }
- }
- struct LoadingView : UIViewRepresentable {
- typealias UIViewType = UIActivityIndicatorView
- func makeUIView(context: UIViewRepresentableContext<LoadingView>) -> LoadingView.UIViewType {
- let view = UIActivityIndicatorView(style: .large)
- view.startAnimating()
- return view
- }
- func updateUIView(_ uiView: LoadingView.UIViewType, context: UIViewRepresentableContext<LoadingView>) {
- //Todo
- }
- }
- extension URLSession {
- enum URLSessionError: Error {
- case invalidResponse
- case serverErrorMessage(statusCode: Int, data: Data)
- case urlError(URLError)
- }
- func send(request: URLRequest) -> AnyPublisher<Data, URLSessionError> {
- dataTaskPublisher(for: request)
- .mapError { URLSessionError.urlError($0) }
- .flatMap { data, response -> AnyPublisher<Data, URLSessionError> in
- guard let response = response as? HTTPURLResponse else {
- return .fail(.invalidResponse)
- }
- guard 200..<300 ~= response.statusCode else {
- return .fail(.serverErrorMessage(statusCode: response.statusCode,
- data: data))
- }
- return .just(data)
- }.eraseToAnyPublisher()
- }
- }
- extension Publisher {
- static func empty() -> AnyPublisher<Output, Failure> {
- return Empty()
- .eraseToAnyPublisher()
- }
- static func just(_ output: Output) -> AnyPublisher<Output, Failure> {
- return Just(output)
- .catch { _ in AnyPublisher<Output, Failure>.empty() }
- .eraseToAnyPublisher()
- }
- static func fail(_ error: Failure) -> AnyPublisher<Output, Failure> {
- return Fail(error: error)
- .eraseToAnyPublisher()
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement