Giter Club home page Giter Club logo

weather-app's Introduction

weather-app

This is a project to create a simple weather app for iOS

🚨 Important note 🚨

This project is using cocoapods but a gitignore file is there so the third-party libraries are not part of the repo. Please be sure to run the pod install command before running the project.

If you have any doubt about cocoapods you can check the reference here.

To run the project you just need to add your APP ID in EndPoint swift file

static let appID: String = "ADD YOUR API ID HERE"

FAQS πŸ€”

  • Q: I want to test the project using the Xcode simulator. How could I get a location (latitude, longitude)?

You can use

LocationManager.shared.simulateLocation = true

At didFinishLaunchingWithOptions in AppDelegate file. In this case you'll use a simulated location (50.075539, 14.437800)

  • Q: WhatΒ΄s the logic to refresh the weather when you're using the app?

We have a few scenarios here:

  • You install the app: The app will get the wather information from the server

  • You open the app once installed: If more than 20 min has passed since the last synchronization, the app will get the weather information from the server. In any other case, it will display the information locally.

  • If the user has moved more than two kilometers since the last synchronization, the app will get the weather information from the server. In any other case, it will display the information locally.

  • Q: Wich information is shared using the app?

If the user decided to press the share option he / she will be able to share a simple text with link to this repo and a screenshot of the Today screen.

Project Architecture

alt tag

References:

How did I implement VIPER?

Basically I have a protocol file for each scene in the app. This file defines the interaction between each layer as following:

  • View - Presenter: protocols to notify changes and to inject information to the UI.
  • Presenter - Interactor: protocols to request / receive information to / from the interator.
  • Presenter - Router: protocol to define the transitions between scenes.

Whith this protocols file is really easy to know how each layer notify / request / information to the other ones so we don't have any other way to communicate all the layers.

Another important point is because I'm using protocols it's really easy to define mocks views / presenters / interactors / routers for testing.

// View / Presenter
protocol TodayViewInjection : class {
    func loadWeatherInformationWithViewModel(_ viewModel: TodayViewModel)
}

protocol TodayPresenterDelegate : class {
    func viewDidLoad()
}

// Presenter / Interactor
typealias getWeatherInteractorCompletionBlock = (_ viewModel: TodayViewModel?, _ success: Bool, _ error: ResultError?) -> Void

protocol TodayInteractorDelegate : class {
    func requestLocationAuthorizationIfNeeded()
    func getCurrentWeather(completion: @escaping getWeatherInteractorCompletionBlock)
    func getLocalWeatherInformation() -> TodayViewModel?
    func shouldGetLocalWeatherInformation() -> Bool
}

First at all. Where is the data came from?

I'm using the api from open weather map (you can check the api documentation here).

You just need to create an account to have access to the api. Once you do it you'll able to get information for movies in a JSON format like this:

{
    "cod": "200",
    "message": 0.178,
    "cnt": 40,
    "list": [
        {
            "dt": 1543687200,
            "main": {
                "temp": 8.52,
                "temp_min": 8.17,
                "temp_max": 8.52,
                "pressure": 992.89,
                "sea_level": 1034.86,
                "grnd_level": 992.89,
                "humidity": 72,
                "temp_kf": 0.35
            },
            "weather": [
                {
                    "id": 800,
                    "main": "Clear",
                    "description": "clear sky",
                    "icon": "01n"
                }
            ],
            "clouds": {
                "all": 0
            },
            "wind": {
                "speed": 0.96,
                "deg": 244.502
            },
            "sys": {
                "pod": "n"
            },
            "dt_txt": "2018-12-01 18:00:00"
        },
        ...
    ],
    "city": {
    	"id": 2509954,
        "name": "Valencia",
        "coord": {
            "lat": 39.4697,
            "lon": -0.3774
        },
        "country": "ES",
        "population": 814208
    }
}

This is an example of the api call: http://api.openweathermap.org/data/2.5/forecast?lat=50.075539&lon=14.4378&APPID=28bc6d7ace6e643065fd95756fae8b9c&units=imperial&lang=en

Data models

Network data models

These includes the following models:

public struct WeatherResponse: Codable {
    let cod: String
    let message: CGFloat
    let cnt: UInt
    let list: [WeatherListResponse]
    let city: WeatherCityResponse
}

public struct WeatherListResponse: Codable {
    let dt: Double
    let main: WeatherListMainResponse
    let weather: [WeatherListWeatherResponse]
    let wind: WeatherListWindResponse
    let rain: WeatherRainResponse?
}

public struct WeatherRainResponse: Codable {
    let rain3h: CGFloat?
    
    //__ This is little trick.
    //__ The "rain" field has another field inside called "3h"
    //__ The problem is we can't process this field using Swift
    //__ so we need to create an enum like a "bridge" to process the field
    enum CodingKeys: String, CodingKey {
        case rain3h = "3h"
    }
}

public struct WeatherListMainResponse: Codable {
    let temp: CGFloat
    let temp_min: CGFloat
    let temp_max: CGFloat
    let pressure: CGFloat
    let sea_level: CGFloat
    let grnd_level: CGFloat
    let humidity: Int
    let temp_kf: CGFloat
}

public struct WeatherListWeatherResponse: Codable {
    let id: Int
    let main: String
    let description: String
    let icon: String
}

public struct WeatherListWindResponse: Codable {
    let speed: CGFloat
    let deg: CGFloat
}

public struct WeatherCityResponse: Codable {
    let id: Int64
    let name: String
    let coord: WeatherCityCoordResponse
    let country: String
    let population: Int64?
}

public struct WeatherCityCoordResponse: Codable {
    let lat: CGFloat
    let lon: CGFloat
}

I'm using a Swift Standard Library decodable functionality in order to manage a type that can decode itself from an external representation (I really ❀ this from Swift).

Are more properties there??

Obviously the response has more properties. I decided to use only these ones.

Reference: Apple documentation

Local weather data model

This model is used for save the last weather information locally:

class LocalWeather: Object {
    @objc dynamic var weatherId: String?
    @objc dynamic var weatherData: Data? = nil
    
    override class func primaryKey() -> String? {
        return "weatherId"
    }
}

And this one is used to generate a user with an unique user id:

class User: Object {
    @objc dynamic var userId: String?
    
    override class func primaryKey() -> String? {
        return "userId"
    }
}

As I'm using Realm for this it's important to define a class to manage each model in the database. In this case we only have one model (IMSearchSuggestion)

Reference: Realm

Managers

I think using managers is a good idea but be careful!. Please don't create managers as if the world were going to end tomorrow.

I'm using only 5 here:

RemoteDabaBaseManager

Used to store information remotely (Firebase)

LocationManager

Used to manage all the location stuff (request auth, get the current location)

ReachabilityManager

Used to manage the reachability

LocalWeatherManager

Used to store the last weather information locally (Realm)

ShareManager

User to share the current weather using UIActivityViewController

How it looks like?

Today weather & Forecast

alt tag alt tag

No internet connection & share option

alt tag alt tag

What's left in the demo?

  • Location permission denied: The logic to manage if the user deny the location permission is not defined. Maybe I could show a message to the user in this case.
  • Realm migration process: It would be nice to add a process to migrate the realm database to a new model (just in case you need to add a new field into the database)

Programming languages && Development tools

  • Swift 4.2
  • Xcode 10.1
  • Cocoapods 1.5.3
  • Minimun iOS version: 11.0

Third-Party Libraries

  • RealmSwift (3.7.6): A mobile database that runs directly inside phones, tablets or wearables.
  • ReachabilitySwift (4.2.1): Replacement for Apple's Reachability re-written in Swift with callbacks.
  • CollectionViewCenteredFlowLayout (1.0.1): A layout for UICollectionView that aligns the cells to the center.
  • Firebase: A Backend as a Service β€”BaaSβ€”.

Support && contact

Email

You can contact me using my email: [email protected]

Twitter

Follow me @rcasanovan on twitter.

weather-app's People

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.