diff --git a/Clima.xcodeproj/project.pbxproj b/Clima.xcodeproj/project.pbxproj index 076632ce..c1255e25 100644 --- a/Clima.xcodeproj/project.pbxproj +++ b/Clima.xcodeproj/project.pbxproj @@ -7,18 +7,22 @@ objects = { /* Begin PBXBuildFile section */ + 95589E7125DAD5020002DF3A /* WeatherManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95589E7025DAD5020002DF3A /* WeatherManager.swift */; }; + 95589E7425DB02160002DF3A /* WeatherData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95589E7325DB02160002DF3A /* WeatherData.swift */; }; + 95E72F0525DC38150097412B /* WeatherModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95E72F0425DC38150097412B /* WeatherModel.swift */; }; ADAA27B1231BBFAF00365194 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADAA27B0231BBFAF00365194 /* AppDelegate.swift */; }; ADAA27B3231BBFAF00365194 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADAA27B2231BBFAF00365194 /* SceneDelegate.swift */; }; ADAA27B5231BBFAF00365194 /* WeatherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADAA27B4231BBFAF00365194 /* WeatherViewController.swift */; }; ADAA27B8231BBFAF00365194 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ADAA27B6231BBFAF00365194 /* Main.storyboard */; }; ADAA27BA231BBFB300365194 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ADAA27B9231BBFB300365194 /* Assets.xcassets */; }; ADAA27BD231BBFB300365194 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ADAA27BB231BBFB300365194 /* LaunchScreen.storyboard */; }; - ADAA27C6231BC02C00365194 /* light_background.pdf in Resources */ = {isa = PBXBuildFile; fileRef = ADAA27C4231BC02B00365194 /* light_background.pdf */; }; - ADAA27C7231BC02C00365194 /* dark_background.pdf in Resources */ = {isa = PBXBuildFile; fileRef = ADAA27C5231BC02B00365194 /* dark_background.pdf */; }; ADAA27CE231BC87F00365194 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = ADAA27CD231BC87F00365194 /* README.md */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 95589E7025DAD5020002DF3A /* WeatherManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherManager.swift; sourceTree = ""; }; + 95589E7325DB02160002DF3A /* WeatherData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherData.swift; sourceTree = ""; }; + 95E72F0425DC38150097412B /* WeatherModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherModel.swift; sourceTree = ""; }; ADAA27AD231BBFAF00365194 /* Clima.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Clima.app; sourceTree = BUILT_PRODUCTS_DIR; }; ADAA27B0231BBFAF00365194 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; ADAA27B2231BBFAF00365194 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -27,8 +31,6 @@ ADAA27B9231BBFB300365194 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; ADAA27BC231BBFB300365194 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; ADAA27BE231BBFB300365194 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - ADAA27C4231BC02B00365194 /* light_background.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = light_background.pdf; sourceTree = ""; }; - ADAA27C5231BC02B00365194 /* dark_background.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = dark_background.pdf; sourceTree = ""; }; ADAA27CD231BC87F00365194 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; /* End PBXFileReference section */ @@ -43,6 +45,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 95589E6F25DAD4F10002DF3A /* Model */ = { + isa = PBXGroup; + children = ( + 95589E7025DAD5020002DF3A /* WeatherManager.swift */, + 95589E7325DB02160002DF3A /* WeatherData.swift */, + 95E72F0425DC38150097412B /* WeatherModel.swift */, + ); + path = Model; + sourceTree = ""; + }; ADAA27A4231BBFAF00365194 = { isa = PBXGroup; children = ( @@ -65,14 +77,12 @@ children = ( ADAA27B0231BBFAF00365194 /* AppDelegate.swift */, ADAA27B2231BBFAF00365194 /* SceneDelegate.swift */, - ADAA27CA231BC3FA00365194 /* Model */, + 95589E6F25DAD4F10002DF3A /* Model */, ADAA27C9231BC3F200365194 /* View */, ADAA27C8231BC3E500365194 /* Controller */, ADAA27B9231BBFB300365194 /* Assets.xcassets */, ADAA27BB231BBFB300365194 /* LaunchScreen.storyboard */, ADAA27BE231BBFB300365194 /* Info.plist */, - ADAA27C5231BC02B00365194 /* dark_background.pdf */, - ADAA27C4231BC02B00365194 /* light_background.pdf */, ); path = Clima; sourceTree = ""; @@ -93,13 +103,6 @@ path = View; sourceTree = ""; }; - ADAA27CA231BC3FA00365194 /* Model */ = { - isa = PBXGroup; - children = ( - ); - path = Model; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -127,7 +130,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1100; - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1240; ORGANIZATIONNAME = "App Brewery"; TargetAttributes = { ADAA27AC231BBFAF00365194 = { @@ -159,11 +162,9 @@ buildActionMask = 2147483647; files = ( ADAA27CE231BC87F00365194 /* README.md in Resources */, - ADAA27C7231BC02C00365194 /* dark_background.pdf in Resources */, ADAA27BD231BBFB300365194 /* LaunchScreen.storyboard in Resources */, ADAA27BA231BBFB300365194 /* Assets.xcassets in Resources */, ADAA27B8231BBFAF00365194 /* Main.storyboard in Resources */, - ADAA27C6231BC02C00365194 /* light_background.pdf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -175,7 +176,10 @@ buildActionMask = 2147483647; files = ( ADAA27B5231BBFAF00365194 /* WeatherViewController.swift in Sources */, + 95589E7425DB02160002DF3A /* WeatherData.swift in Sources */, ADAA27B1231BBFAF00365194 /* AppDelegate.swift in Sources */, + 95589E7125DAD5020002DF3A /* WeatherManager.swift in Sources */, + 95E72F0525DC38150097412B /* WeatherModel.swift in Sources */, ADAA27B3231BBFAF00365194 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -228,6 +232,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -288,6 +293,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/Clima.xcodeproj/project.xcworkspace/xcuserdata/vhunter01.xcuserdatad/UserInterfaceState.xcuserstate b/Clima.xcodeproj/project.xcworkspace/xcuserdata/vhunter01.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..85464160 Binary files /dev/null and b/Clima.xcodeproj/project.xcworkspace/xcuserdata/vhunter01.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Clima.xcodeproj/xcuserdata/vhunter01.xcuserdatad/xcschemes/xcschememanagement.plist b/Clima.xcodeproj/xcuserdata/vhunter01.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..ab521101 --- /dev/null +++ b/Clima.xcodeproj/xcuserdata/vhunter01.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Clima.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Clima/Assets.xcassets/Contents.json b/Clima/Assets.xcassets/Contents.json index da4a164c..73c00596 100644 --- a/Clima/Assets.xcassets/Contents.json +++ b/Clima/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Clima/Assets.xcassets/background.imageset/Contents.json b/Clima/Assets.xcassets/background.imageset/Contents.json index 790c09f1..37044394 100644 --- a/Clima/Assets.xcassets/background.imageset/Contents.json +++ b/Clima/Assets.xcassets/background.imageset/Contents.json @@ -1,21 +1,35 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "background.png", - "scale" : "1x" + "filename" : "light_background.pdf", + "idiom" : "universal" }, { - "idiom" : "universal", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "light_background-1.pdf", + "idiom" : "universal" }, { - "idiom" : "universal", - "scale" : "3x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "dark_background.pdf", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true } -} \ No newline at end of file +} diff --git a/Clima/Assets.xcassets/background.imageset/background.png b/Clima/Assets.xcassets/background.imageset/background.png deleted file mode 100644 index 22e31fdb..00000000 Binary files a/Clima/Assets.xcassets/background.imageset/background.png and /dev/null differ diff --git a/Clima/dark_background.pdf b/Clima/Assets.xcassets/background.imageset/dark_background.pdf similarity index 100% rename from Clima/dark_background.pdf rename to Clima/Assets.xcassets/background.imageset/dark_background.pdf diff --git a/Clima/light_background.pdf b/Clima/Assets.xcassets/background.imageset/light_background-1.pdf similarity index 100% rename from Clima/light_background.pdf rename to Clima/Assets.xcassets/background.imageset/light_background-1.pdf diff --git a/Clima/Assets.xcassets/background.imageset/light_background.pdf b/Clima/Assets.xcassets/background.imageset/light_background.pdf new file mode 100644 index 00000000..1f3a3e7e Binary files /dev/null and b/Clima/Assets.xcassets/background.imageset/light_background.pdf differ diff --git a/Clima/Assets.xcassets/myweatherColor.colorset/Contents.json b/Clima/Assets.xcassets/myweatherColor.colorset/Contents.json new file mode 100644 index 00000000..fe7122e7 --- /dev/null +++ b/Clima/Assets.xcassets/myweatherColor.colorset/Contents.json @@ -0,0 +1,56 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.282", + "green" : "0.263", + "red" : "0.107" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.282", + "green" : "0.263", + "red" : "0.107" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Clima/Controller/WeatherViewController.swift b/Clima/Controller/WeatherViewController.swift index 038c2bba..ef050a4c 100644 --- a/Clima/Controller/WeatherViewController.swift +++ b/Clima/Controller/WeatherViewController.swift @@ -7,18 +7,123 @@ // import UIKit +import CoreLocation class WeatherViewController: UIViewController { - + + + @IBOutlet var txtFieldSearch: UITextField! + @IBOutlet weak var conditionImageView: UIImageView! @IBOutlet weak var temperatureLabel: UILabel! @IBOutlet weak var cityLabel: UILabel! + @IBOutlet var locationButton: UIButton! + + var weatherManager = WeatherManager() + let locationManager = CLLocationManager() + + override func viewDidLoad() { + locationManager.delegate = self + locationManager.requestWhenInUseAuthorization() + locationManager.requestLocation() + super.viewDidLoad() - // Do any additional setup after loading the view. + txtFieldSearch.delegate = self + weatherManager.delegate = self + + + } - + + @IBAction func getLocationBtn(_ sender: UIButton) { + locationManager.requestLocation() + + + + } } + +//MARK: - > Add a completion +// Add the name of the section + + + + extension WeatherViewController : UITextFieldDelegate { + @IBAction func searchPressed(_ sender: Any) { + txtFieldSearch.endEditing(true) + print(txtFieldSearch.text!) + } + // Lets app process the return button when pressed by user + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + txtFieldSearch.endEditing(true) + + print(txtFieldSearch.text!) + return true + } + + //Prevents the user from entering textfield with no text + func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { + if txtFieldSearch.text != ""{ + return true + } else { + txtFieldSearch.placeholder = "Type something here" + return false + } + } + + // Allows text field to clear once the return/search button is pressed + func textFieldDidEndEditing(_ textField: UITextField) { + + // Use textFieldSearch.text to Get the weather for the city + if let city = txtFieldSearch.text{ + weatherManager.retrieveWeather(cityName: city) + } + //Empties the textfield after the user presses button + txtFieldSearch.text = "" + } + + } + +//MARK: - Weather Manager Extension + + extension WeatherViewController: WeatherManagerDelegate { + + //Method that updates the UI tools with the weather conditions of desired location + func didUpdateWeather(_ weatherManager : WeatherManager, weather : WeatherModel){ + DispatchQueue.main.async { + self.temperatureLabel.text = weather.temperatureString + self.conditionImageView.image = UIImage(systemName: weather.conditionName) + self.cityLabel.text = weather.cityName + } + + + } + + func didFailWithError(error: Error) { + print(error) + } + + } + +// MARK: - CLLocationManagerDelegate +extension WeatherViewController : CLLocationManagerDelegate { + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.last{ + locationManager.stopUpdatingLocation() + let lat = location.coordinate.latitude + let lon = location.coordinate.longitude + weatherManager.retrieveWeather(latitude: lat, longitude: lon) + } + } + + func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { + print("Failed to find user's location \(error.localizedDescription)") + + } + } + + diff --git a/Clima/Info.plist b/Clima/Info.plist index b5f9c079..7957444c 100644 --- a/Clima/Info.plist +++ b/Clima/Info.plist @@ -2,6 +2,8 @@ + NSLocationWhenInUseUsageDescription + Your location is necssary for getting the weather of where you are CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/Clima/Model/WeatherData.swift b/Clima/Model/WeatherData.swift new file mode 100644 index 00000000..db1bfda5 --- /dev/null +++ b/Clima/Model/WeatherData.swift @@ -0,0 +1,26 @@ +// +// WeatherData.swift +// Clima +// +// Created by Vincent Hunter on 2/15/21. +// Copyright © 2021 App Brewery. All rights reserved. +// + +import Foundation + +struct WeatherData: Codable { + let name : String + let main : Main + let weather: [Weather] + + +} + +struct Main: Codable { + let temp : Double +} + +struct Weather: Codable { + let description : String + let id: Int +} diff --git a/Clima/Model/WeatherManager.swift b/Clima/Model/WeatherManager.swift new file mode 100644 index 00000000..914a4e69 --- /dev/null +++ b/Clima/Model/WeatherManager.swift @@ -0,0 +1,87 @@ +// +// WeatherManager.swift +// Clima +// +// Created by Vincent Hunter on 2/15/21. +// Copyright © 2021 App Brewery. All rights reserved. +// + +import Foundation +import CoreLocation + +protocol WeatherManagerDelegate { + + func didUpdateWeather(_ weatherManager : WeatherManager, weather : WeatherModel) + func didFailWithError(error: Error) +} + + +struct WeatherManager { + let weatherURL = "https://api.openweathermap.org/data/2.5/find?&appid=92cecb38c0696502b14142765f26c345&units=imperial" + + func retrieveWeather(cityName: String) { + + let urlString = "\(weatherURL)&q=\(cityName)" + performRequest(with: urlString) + + } + + //Swift is okay with having functions/methods share names as long as they don't share input parameters + func retrieveWeather(latitude: CLLocationDegrees, longitude: CLLocationDegrees) { + let urlString = "\(weatherURL)&lat=\(latitude)&lon=\(longitude)" + performRequest(with: urlString) + } + + var delegate : WeatherManagerDelegate? + + func performRequest(with urlString: String) { + //1.Create a URL + + if let url = URL( string: urlString){ + + //2.Create a URLSession + let session = URLSession(configuration: .default) + + //3. Give Session a task + let task = session.dataTask(with: url) { (data, response, error) in + if error != nil{ + self.delegate?.didFailWithError(error: error!) + return + } + + if let safeData = data { + if let weather = self.parseJSON(safeData){ + self.delegate?.didUpdateWeather(self, weather: weather) + } + } + } + + //Start the task + task.resume() + } + + + } + func parseJSON(_ weatherData: Data) -> WeatherModel? { + let decoder = JSONDecoder() + do { + let decodedData = try decoder.decode(WeatherData.self, from: weatherData) + + let id = decodedData.weather[0].id + let temp = decodedData.main.temp + let name = decodedData.name + + let weather = WeatherModel(conditionID: id, cityName: name, temperature: temp) + + return weather + } catch { + self.delegate?.didFailWithError(error: error) + return nil + } + } + + + +} + + diff --git a/Clima/Model/WeatherModel.swift b/Clima/Model/WeatherModel.swift new file mode 100644 index 00000000..92bb3ece --- /dev/null +++ b/Clima/Model/WeatherModel.swift @@ -0,0 +1,40 @@ +// +// WeatherModel.swift +// Clima +// +// Created by Vincent Hunter on 2/16/21. +// Copyright © 2021 App Brewery. All rights reserved. +// + +import Foundation + +struct WeatherModel { + let conditionID : Int + let cityName : String + let temperature : Double + + var temperatureString : String { + return String(format: "%.1f", temperature) + } + + var conditionName : String { + switch conditionID { + case 200...232: + return "cloud.bolt" + case 300...321: + return " cloud.drizzle" + case 500...521 : + return "cloud.rain" + case 600...622 : + return "cloud.snow" + case 700...723: + return "cloud.fog" + case 800: + return "sun.max" + default: + return "cloud" + } + } + + +} diff --git a/Clima/View/Base.lproj/Main.storyboard b/Clima/View/Base.lproj/Main.storyboard index fec3bb4e..97e99bbb 100644 --- a/Clima/View/Base.lproj/Main.storyboard +++ b/Clima/View/Base.lproj/Main.storyboard @@ -1,9 +1,12 @@ - - + + - + + + + @@ -24,42 +27,48 @@ - - + - + - + - - - - + + + - + - - + + @@ -104,12 +114,13 @@ - + + @@ -118,9 +129,21 @@ - - - - + + + + + + + + + + + + + + + +