swift: convert long/int64 to date - swift

I want to convert 1529704800000 to a Date object but I don't get the right date. I'm getting the value out of a json. I'm converting the value on this way:
class Example: Decodable {
var id: Int64?
var date: Date?
init(json: [String: Any]) {
id = json["id"] as? Int64 ?? -1
var dateTime = (json["date"] as AnyObject? as? Int64) ?? 0
date = Date(timeIntervalSince1970: (TimeInterval(dateTime / 1000)))
}
static func fetchReportsForUser(authorId: Int64) -> [Report]? {
let urlString = "http://localhost:8080/test-application/rest/example/"
let url = URL(string: urlString)!
var examples = [Example]()
let group = DispatchGroup()
group.enter()
DispatchQueue.global(qos: .default).async {
URLSession.shared.dataTask(with: url) { (data, response, error) -> Void in
if error != nil {
print(error!)
return
}
guard let data = data else {
return
}
if(data.isEmpty) {
group.leave()
return
}
do {
examples = try JSONDecoder().decode([Example].self, from: data)
} catch let err {
print(err)
}
group.leave()
}.resume()
}
group.wait()
return examples
}
}
When I do it like that I still get 50472-09-04 16:00:00 +0000 as date. I tried it with Double instead of Int64 but I got the same result.

Just let JSONDecoder do the job by using the appropriate date decoding strategy
let json = """
{"id" : 1, "date" : 1529704800000}
"""
struct Example : Decodable {
let id : Int
let date : Date
}
let data = Data(json.utf8)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .millisecondsSince1970
do {
let result = try decoder.decode(Example.self, from: data)
print(result) // Example(id: 1, date: 2018-06-22 22:00:00 +0000)
} catch {
print(error)
}
Note: Don't make an asynchronous task synchronous. Learn to understand asynchronous data processing and use a completion handler.

Your integer seems to represent milliseconds rather than seconds, this it is giving a date thousands of years in the future!
Dividing it by 1000 (removing the last 3 zeros) gives 22 June 2018 10pm UTC.
Also, try changing the lines where you cast from the json to a date:
if let dateTime = json["date"] as? Int {
date = Date(timeIntervalSince1970: dateTime/1000)
}

Related

Prevent lost of data on AppStorage when changing a struct

I have a data model that handles a structure and the data the app uses. I'm saving that data using AppStorage.
I recently needed to add an extra value to the struct, and when I did that, all the data saved was gone.
is there any way to prevent this? I can't find anything on Apple's documentation, or other Swift or SwiftUI sites about this.
Here's my data structure and how I save it.
let dateFormatter = DateFormatter()
struct NoteItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
var tags: [String] = []
//var starred: Int = 0 // if I add this, it wipes all the data the app has saved
}
final class DataModel: ObservableObject {
#AppStorage("myappdata") public var notes: [NoteItem] = []
init() {
self.notes = self.notes.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
}
func sortList() {
self.notes = self.notes.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
}
}
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}
I certainly agree that UserDefaults (AppStorage) is no the best choice for this but whatever storage solution you choose you are going to need a migration strategy. So here are two routes you can take to migrate a changed json struct.
The first one is to add a custom init(from:) to your struct and handle the new property separately
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
text = try container.decode(String.self, forKey: .text)
date = try container.decode(Date.self, forKey: .date)
tags = try container.decode([String].self, forKey: .tags)
if let value = try? container.decode(Int.self, forKey: .starred) {
starred = value
} else {
starred = 0
}
}
The other option is to keep the old version of the struct with another name and use it if the decoding fails for the ordinary struct and then convert the result to the new struct
extension NoteItem {
static func decode(string: String) -> [NoteItem]? {
guard let data = string.data(using: .utf8) else { return nil }
if let result = try? JSONDecoder().decode([NoteItem].self, from: data) {
return result
} else if let result = try? JSONDecoder().decode([NoteItemOld].self, from: data) {
return result.map { NoteItem(id: $0.id, text: $0.text, date: $0.date, tags: $0.tags, starred: 0)}
}
return nil
}
}

How to set a value as a condition in while loop using DispatchGroup?

I'm currently developing an application using SwiftUI.
I want to use a value created from a while loop using a DispatchGroup as a condition for while loop.
But my code doesn't work...
My goal is to get a date over a set total temperature from a start date.
How could I solve this problem?
UPDATED
For example:
If I call the method like below.
makeGetCallDateOverSetTemp(start_date:"2020-11-01", set_temp:100)
1'st loop -> totalTemp = 20 (a temperature of 2020-11-01 is 20℃)
2'nd loop -> totalTemp = 50 (a temperature of 2020-11-02 is 30℃)
3'rd loop -> totalTemp = 80 (a temperature of 2020-11-03 is 30℃)
4'th loop -> totalTemp = 105 (a temperature of 2020-11-04 is 25℃)
the while loop stops here and gets 2020-11-04 as the day over the set temperature.
AppState.swift
#Published var weatherInfos:[WeatherInfos]?
func makeGetCallDateOverSetTemp(start_date:String, set_temp:Int){
let start_date = self.dateFromString(string: start_date, format: "yyyy/MM/dd")
var addDays = 0
var totalTemp:Float = 0.0
let group = DispatchGroup()
// Set up the URL request
while Float(set_temp) < totalTemp {
let start_date = Calendar.current.date(byAdding: .day, value: addDays, to: start_date)
let url_start_date = self.stringFromDate(date: start_date!, format: "yyyy-MM-dd")
let endpoint: String = "https://sample.com/api/weather/?start_date=\(url_start_date)"
addDays += 1
guard let url = URL(string: endpoint) else {
print("Error: cannot create URL")
continue
}
var urlRequest = URLRequest(url: url)
urlRequest.addValue("token xxxxxxxxxxx", forHTTPHeaderField: "authorization")
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
group.enter()
let task = session.dataTask(with: urlRequest) {(data, response, error) in
guard error == nil else {
print("error calling GET")
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// check for any errors
defer { group.leave()}
// parse the result as JSON, since that's what the API provides
DispatchQueue.main.async {
do{
self.weatherInfos = try JSONDecoder().decode([WeatherInfos].self, from: responseData)
for info in self.weatherInfos!{
totalTemp += info.temp
}
}catch{
print("Error: did not decode")
return
}
}
}
task.resume()
}
group.notify(queue: .main){
print(url_start_date)
}
}
func stringFromDate(date: Date, format: String) -> String {
let formatter: DateFormatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.dateFormat = format
return formatter.string(from: date)
}
func dateFromString(string: String, format: String) -> Date {
let formatter: DateFormatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.dateFormat = format
return formatter.date(from: string) ?? Date()
}
jsonModel.swift
struct WeatherInfos:Codable,Identifiable {
var id: Int
var temp: Float
}
A while loop whose condition relies on the result of an asynchronous task is impossible.
This is a stand-alone generic example with recursion to run in a Playground.
The static data is a struct, an array, a queue and a threshold value
struct Item {
let date : String
let values : [Int]
}
let items = [Item(date: "2020-02-01", values: [1, 3, 5]),
Item(date:"2020-02-02", values:[2, 4, 6]),
Item(date: "2020-02-03", values:[3, 5, 7])]
let queue = DispatchQueue(label: "Foo")
let threshold = 25
The variables are an index and the accumulated temperature
var temp = 0
var index = 0
The function getData calls itself passing the next item if the threshold is not reached yet. The asynchronous task is simulated with asyncAfter.
Finally the function notify is called.
func notify(date : String) {
DispatchQueue.main.async{ print(date, temp) }
}
func getData(date: String, values :[Int]) {
queue.asyncAfter(deadline: .now() + 1) {
for value in values {
temp += value
if temp >= threshold {
notify(date: date)
return
}
}
index += 1
if index < items.count {
let nextItem = items[index]
getData(date: nextItem.date, values: nextItem.values)
} else {
notify(date: "\(date) – temperature below threshold")
}
}
}
let firstItem = items[index]
getData(date: firstItem.date, values: firstItem.values)

Updating screen fields when background task ends

I am using some APIs to get data. These are initiated as session.dataTask and I am using classes to encapsulate the API calls, methods and returned properties for each different API. How should I configure my code so as to update the relevant screen labels and subViews when the API sessions have concluded and the data is available?
The relevant section of the AstronomicalTimes class init is:
init (date: Date, lat: Float, long: Float) {
let coreURL = "https://api.sunrise-sunset.org/json?"
let position = "lat=\(lat)&lng=\(long)"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateString = "&date=" + dateFormatter.string(from: date)
//let dateString = "&date=2020-06-21"
let urlString = coreURL + position + dateString + "&formatted=0"
let session = URLSession.shared
let url = URL(string: urlString)!
let request = URLRequest(url: url)
session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
let nsError = error as NSError
print("Astronomical Times API call failed with error \(nsError.code)")
return
}
if let response = response as? HTTPURLResponse {
print("Astronomical Times API call response is \(response.statusCode)")
}
if let data = data {
do {
let astronomicalTimesResponse = try JSONDecoder().decode(AstronomicalTimesResponse.self, from: data)
print("Astronomical times successfully parsed")
self.fillFields(astronomicalTimesResponse.results) //completes all class properties from parsed data
} catch {
print("Error while tide details parsing: \(error)")
}
}
}).resume()
A label is assigned the result of the API call in viewDidLoad() with:
currentAstronomicalTimes = AstronomicalTimes(date: savedDate, lat: currentSelection.station.lat, long: currentSelection.station.long)
lblAstDawn.text = currentAstronomicalTimes.strings.astronomicalTwilightBegin
Clearly this doesn't work as the screen is rendered with the labels and subViews blank before the API returns the data. I can't figure out how to signal the ViewController when the API has completed and then how to redraw the labels etc. I have tried updating the viewController fields in the API call closure expression but I can't update the UILabels from another class (and I think this approach is messy as the label update logic should really be in the ViewController)
Any help appreciated.
UPDATE after Rob's comments:
I have changed my class definition as advised and it successfully loads the data from the API. The class definition is as below, note I add a function which takes the loaded data and turns it into time strings and date() for ease of use in viewController (these all appear to be correctly populated after the API call)
import Foundation
enum AstronomicalTimesError: Error {
case invalidResponse(Data?, URLResponse?)
}
class AstronomicalTimes {
//structures for decoding daylight times
struct AstronomicalTimesResponse: Decodable {
public var results: AstronomicalTimes
public var status: String
}
struct AstronomicalTimes: Decodable {
var sunrise = String()
var sunset = String()
var solarNoon = String()
var dayLength = 0
var civilTwilightBegin = String()
var civilTwilightEnd = String()
var nauticalTwilightBegin = String()
var nauticalTwilightEnd = String()
var astronomicalTwilightBegin = String()
var astronomicalTwilightEnd = String()
private enum CodingKeys : String, CodingKey {
case sunrise = "sunrise"
case sunset = "sunset"
case solarNoon = "solar_noon"
case dayLength = "day_length"
case civilTwilightBegin = "civil_twilight_begin"
case civilTwilightEnd = "civil_twilight_end"
case nauticalTwilightBegin = "nautical_twilight_begin"
case nauticalTwilightEnd = "nautical_twilight_end"
case astronomicalTwilightBegin = "astronomical_twilight_begin"
case astronomicalTwilightEnd = "astronomical_twilight_end"
}
}
//used to hold string values to enter to label, i.e. time strings for labels
var strings = AstronomicalTimes()
//struct and variable used to hold specific date/times for gradient calculation
struct Times {
var sunrise = Date()
var sunset = Date()
var solarNoon = Date()
var dayLength = 0
var civilTwilightBegin = Date()
var civilTwilightEnd = Date()
var nauticalTwilightBegin = Date()
var nauticalTwilightEnd = Date()
var astronomicalTwilightBegin = Date()
var astronomicalTwilightEnd = Date()
}
var times = Times()
let date: Date
let latitude: Float
let longitude: Float
init (date: Date, latitude: Float, longitude: Float) {
self.date = date
self.latitude = latitude
self.longitude = longitude
}
func start(completion: #escaping (Result<AstronomicalTimesResponse, Error>) -> Void) {
var components = URLComponents(string: "https://api.sunrise-sunset.org/json")!
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // just in case your end user isn't using Gregorian calendar
dateFormatter.dateFormat = "yyyy-MM-dd"
components.queryItems = [
URLQueryItem(name: "lat", value: "\(latitude)"),
URLQueryItem(name: "lng", value: "\(longitude)"),
URLQueryItem(name: "date", value: dateFormatter.string(from: date)),
URLQueryItem(name: "formatted", value: "0")
]
let session = URLSession.shared
let url = components.url!
let request = URLRequest(url: url)
session.dataTask(with: request) { data, response, error in
if let error = error {
DispatchQueue.main.async {
completion(.failure(error))
}
return
}
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
DispatchQueue.main.async {
completion(.failure(AstronomicalTimesError.invalidResponse(data, response)))
}
return
}
do {
print("Astronomical times api completed with status code ", httpResponse.statusCode)
let astronomicalTimesResponse = try JSONDecoder().decode(AstronomicalTimesResponse.self, from: responseData)
DispatchQueue.main.async {
completion(.success(astronomicalTimesResponse))
self.fillFields(astronomicalTimesResponse.results)
}
} catch let jsonError {
DispatchQueue.main.async {
completion(.failure(jsonError))
}
}
}.resume()
}
func fillFields(_ input: AstronomicalTimes) -> Void {
//formats output fields into Date() or String (HH:mm) format
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" //Your date format
times.sunrise = dateFormatter.date(from: input.sunrise) ?? Date()
times.sunset = dateFormatter.date(from: input.sunset) ?? Date()
times.solarNoon = dateFormatter.date(from: input.solarNoon) ?? Date()
times.dayLength = input.dayLength
times.civilTwilightBegin = dateFormatter.date(from: input.civilTwilightBegin) ?? Date()
times.civilTwilightEnd = dateFormatter.date(from: input.civilTwilightEnd) ?? Date()
times.nauticalTwilightBegin = dateFormatter.date(from: input.nauticalTwilightBegin) ?? Date()
times.nauticalTwilightEnd = dateFormatter.date(from: input.nauticalTwilightEnd) ?? Date()
times.astronomicalTwilightBegin = dateFormatter.date(from: input.astronomicalTwilightBegin) ?? Date()
times.astronomicalTwilightEnd = dateFormatter.date(from: input.astronomicalTwilightEnd) ?? Date()
let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm"
strings.sunrise = timeFormatter.string(from: times.sunrise)
strings.sunset = timeFormatter.string(from: times.sunset)
strings.solarNoon = timeFormatter.string(from: times.solarNoon)
strings.dayLength = input.dayLength
strings.civilTwilightBegin = timeFormatter.string(from: times.civilTwilightBegin)
strings.civilTwilightEnd = timeFormatter.string(from: times.civilTwilightEnd)
strings.nauticalTwilightBegin = timeFormatter.string(from: times.nauticalTwilightBegin)
strings.nauticalTwilightEnd = timeFormatter.string(from: times.nauticalTwilightEnd)
strings.astronomicalTwilightBegin = timeFormatter.string(from: times.astronomicalTwilightBegin)
strings.astronomicalTwilightEnd = timeFormatter.string(from: times.astronomicalTwilightEnd)
}
}
I then call this from a function within viewController:
func getAstronomicalTimes(date: Date, latitude: Float, longitude: Float) -> Void {
let astronomicalTimes = AstronomicalTimes(date: date, latitude: latitude, longitude: longitude)
astronomicalTimes.start { result in
switch result {
case .success(let astronomicalTimesResponse):
print("astronomical times response ", astronomicalTimesResponse)
print("label", astronomicalTimes.strings.astronomicalTwilightBegin)
self.lblAstDawn.text = astronomicalTimes.strings.astronomicalTwilightBegin
case .failure(let error):
print(error)
}
}
}
This function is called within viewDidLoad():
getAstronomicalTimes(date: savedDate, latitude: currentSelection.station.lat, longitude: currentSelection.station.long)
However, the getAstronomicalTimes(date:latitude:longitude) does not update the lblAstDawn.text as I had hoped for.
Any clues as to where I am getting this wrong?
You need to supply a completion handler to your AstronomicalTimes request, so it can tell your view controller when the data has been retrieved, and the view controller can then update the various fields.
Thus:
enum AstronomicalTimesError: Error {
case invalidResponse(Data?, URLResponse?)
}
class AstronomicalTimes {
let date: Date
let latitude: Float // generally we use Double, but for your purposes, this might be adequate
let longitude: Float
init (date: Date, latitude: Float, longitude: Float) {
self.date = date
self.latitude = latitude
self.longitude = longitude
}
func start(completion: #escaping (Result<AstronomicalTimesResponse, Error>) -> Void) {
var components = URLComponents(string: "https://api.sunrise-sunset.org/json")!
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX") // just in case your end user isn't using Gregorian calendar
dateFormatter.dateFormat = "yyyy-MM-dd"
components.queryItems = [
URLQueryItem(name: "lat", value: "\(latitude)"),
URLQueryItem(name: "lng", value: "\(longitude)"),
URLQueryItem(name: "date", value: dateFormatter.string(from: date)),
URLQueryItem(name: "formatted", value: "0")
]
let session = URLSession.shared
let url = components.url!
let request = URLRequest(url: url)
session.dataTask(with: request) { data, response, error in
if let error = error {
DispatchQueue.main.async {
completion(.failure(error))
}
return
}
guard
let responseData = data,
let httpResponse = response as? HTTPURLResponse,
200 ..< 300 ~= httpResponse.statusCode
else {
DispatchQueue.main.async {
completion(.failure(AstronomicalTimesError.invalidResponse(data, response)))
}
return
}
do {
let astronomicalTimesResponse = try JSONDecoder().decode(AstronomicalTimesResponse.self, from: responseData)
DispatchQueue.main.async {
completion(.success(astronomicalTimesResponse))
}
} catch let jsonError {
DispatchQueue.main.async {
completion(.failure(jsonError))
}
}
}.resume()
}
}
Then your viewDidLoad might do something like:
override viewDidLoad() {
super.viewDidLoad()
let astronomicalTimes = AstronomicalTimes(date: someDate, latitude: someLatitude, longitude: someLongitude)
astronomicalTimes.start { result in
switch result {
case .success(let astronomicalTimesResponse):
// populate your fields here
case .failure(let error):
print(error)
}
}
}

How can I decode response data if data is nested in Swift

I've got a problem with decoding response data. Here is my request function
#IBAction func onGetCities(_ sender: UIButton) {
guard let url = URL(string: "http://somelink.com/city-list") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
print(JSON(data))
guard let data = data else { return }
do{
let cities = try JSONDecoder().decode([City].self, from: data)
print(cities)
}catch{
}
}
task.resume()
}
And City struct
struct City: Decodable {
let id: Int
let city: String
}
Here is the response data, I want to decode "items"
{
"offset": 0,
"limit": 10,
"items": [
{id: 0, name: "City name"},
{id: 1, name: "City name1"},
.....
]
}
You need to have nested structures that mirror your nested JSON:
struct City: Decodable {
let id: Int
let city: String
}
struct ResponseObject: Decodable {
let items: [City]
let offset: Int
let limit: Int
}
And then:
do {
let result = try JSONDecoder().decode(ResponseObject.self, from: data)
print(result)
let cities = result.items
print(cities)
} catch {
print(error)
}
Note, in your original example, you included a JSON key, updated_at which was the date represented in number of seconds since 1970 (standard UNIX representation). So, to decode that:
struct City: Decodable {
let id: Int
let city: String
}
struct ResponseObject: Decodable {
let items: [City]
let offset: Int
let limit: Int
let code: Int
let updatedAt: Date
enum CodingKeys: String, CodingKey {
case items, offset, limit, code
case updatedAt = "updated_at"
}
}
And then:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
do {
let result = try decoder.decode(ResponseObject.self, from: data)
print(result)
let cities = result.items
print(cities)
} catch {
print(error)
}

Definition of Global Variable Resetting

I have a class designed to take the temperature data from an API for a specific date and add it to a dictionary. The URL for the API is stored in a global variable called baseURL. It is defined at the beginning as an empty string, but is later changed. My class is below:
import UIKit
import Foundation
typealias ServiceResponse = (JSON, NSError?) -> Void
class WeatherManager: NSObject {
var baseURL: String = ""
var data: String = ""
static let sharedInstance = WeatherManager()
func getRandomUser(onCompletion: (JSON) -> Void) {
println("Starting getRandomUser")
let route = self.baseURL
println(self.baseURL)
makeHTTPGetRequest(route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
func makeHTTPGetRequest(path: String, onCompletion: ServiceResponse) {
let request = NSMutableURLRequest(URL: NSURL(string: path)!)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
let json:JSON = JSON(data: data)
onCompletion(json, error)
if error != nil {
println("No Error")
} else {
println("Error")
}
})
task.resume()
}
func addData() {
WeatherManager.sharedInstance.getRandomUser { json in
var jsonData = json["response"]["version"]
self.data = "\(jsonData)"
dispatch_async(dispatch_get_main_queue(),{
let alert = UIAlertView()
alert.title = "Weather Data Update"
if self.data != "null" {
println("Value:\(self.data)")
alert.message = "The weather data was updated successfully."
alert.addButtonWithTitle("OK")
alert.show()
} else {
println("Error Reading Data")
println(self.data)
alert.message = "HealthTrendFinder encountered an error while updating data."
alert.addButtonWithTitle("OK")
alert.show()
}
})
}
}
func updateWeatherHistory() {
println(self.baseURL)
let calendar: NSCalendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
println("Weather Updating...")
// This sets the start date to midnight of the current date if no start date has been set.
if StorageManager.getValue(StorageManager.StorageKeys.WeatherStartDate) == nil {
let startDate: NSDate = calendar.startOfDayForDate(NSDate())
StorageManager.setValue(startDate, forKey: StorageManager.StorageKeys.WeatherStartDate)
}
// This adds a data array if it hasn't been created yet.
if StorageManager.getValue(StorageManager.StorageKeys.WeatherData) == nil {
StorageManager.setValue([:], forKey: StorageManager.StorageKeys.WeatherData)
}
var weatherData: [NSDate: NSObject] = StorageManager.getValue(StorageManager.StorageKeys.WeatherData)! as! [NSDate : NSObject]
let startMidnight: NSDate = StorageManager.getValue(StorageManager.StorageKeys.WeatherStartDate) as! NSDate
let currentMidnight: NSDate = calendar.startOfDayForDate(NSDate())
let daysFromStartDate: Int = calendar.components(NSCalendarUnit.CalendarUnitDay, fromDate: startMidnight, toDate: currentMidnight, options: nil).day
println("Starting Loop")
for i: Int in 0..<daysFromStartDate {
let dateToBeExamined: NSDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: i, toDate: startMidnight, options: nil)!
if weatherData[dateToBeExamined] == nil {
let calendarUnits: NSCalendarUnit = .CalendarUnitDay | .CalendarUnitMonth | .CalendarUnitYear
let components = NSCalendar.currentCalendar().components(calendarUnits, fromDate: dateToBeExamined)
var month: String
var day: String
if components.month < 10 {
month = "0\(components.month)"
} else {
month = "\(components.month)"
}
if components.day < 10 {
day = "0\(components.day)"
} else {
day = "\(components.day)"
}
var dateString = "\(components.year)\(month)\(day)"
self.baseURL = "http://api.wunderground.com/api/91e65f0fbb35f122/history_\(dateString)/q/OR/Portland.json"
println(self.baseURL)
var get: () = WeatherManager.sharedInstance.addData()
println(get)
weatherData[dateToBeExamined] = self.data
// There is no data for the NSDate dateForInspection. You need to pull data and add it to the dictionary.
} else {
// Data exists for the specified date, so you don't need to do anything.
}
}
println("Loop has finished or been skipped")
}
}
The problem is, baseURL reverts to an empty string when getRandomUser is executed, after baseURL is set to the URL. Why is this happening, and how do I fix it?
Your code is unnecessarily complex, making it hard to diagnose the problem without more information. But here is a suggestion:
Try making it impossible to instantiate more than one instance of your WeatherManager singleton:
class WeatherManager {
private static let _sharedInstance = WeatherManager()
private init() { super.init() }
static func sharedInstance() -> WeatherManager {
return _sharedInstance
}
}
When you are working from outside WeatherManager, you access it by calling:
let wm = WeatherManager.sharedInstane()
Then, when you are working inside WeatherManager, make sure that all your references are to self - i.e., self.baseURL = ... or self.updateWeatherHistory(), instead of WeatherManager.sharedInstance.baseURL = ..., etc.
Though your code is complicated, I think what is going on is you actually have two instances of WeatherManager in play. You are setting the value of baseURL on one, but not the other. If you want it to be a singleton, you need to make it impossible to create more than one.