Swift - completion handler not waiting - swift

My problem is straighforward. I am fetching some data from site with TRON and I need to wait until data is successfully fetched than continue with execution. But my problem is that main thread is not waiting until completion block is completed.
I am doing this as Unit Test, so in setUp() function
Meteorites.Service.sharedInstance.fetchData(completion: { (meteorites, err) in
if err == nil {
self.meteorites = meteorites
}
})
than in test function I am using meteorites array, but I get EXC error (object is nil)
What I am doing wrong? Can someone explain me? Thanks
func fetchData(completion: #escaping ([Meteor]?, APIError<Service.JSONError>?) -> ()) {
print("fetching")
let request: APIRequest<Meteorites, JSONError> = tron.request("")
request.perform(withSuccess: { (meteorites) in
completion(meteorites.meteorites, nil)
}) { (err) in
completion(nil, err)
}
}
In case Meteorites class
class Meteorites: JSONDecodable {
var meteorites: [Meteor]
required init(json: JSON) throws {
print("fetched")
var meteorites = [Meteor]()
for meteorJson in json.array! {
let dateString = meteorJson["year"]
if dateString != JSON.null {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" //Your date format
dateFormatter.timeZone = TimeZone(abbreviation: "GMT+0:00")!
let date = dateFormatter.date(from: dateString.stringValue)
if let compareDate = dateFormatter.date(from: "2011-01-01T00:00:00.000") {
if date! >= compareDate {
let meteor = Meteor()
meteor.date = date!
meteor.name = meteorJson["name"].stringValue
meteor.fall = meteorJson["fall"].stringValue
meteor.id = meteorJson["id"].intValue
meteor.reclong = meteorJson["reclong"].doubleValue
meteor.reclat = meteorJson["reclat"].doubleValue
meteor.mass = meteorJson["mass"].intValue
meteor.nametype = meteorJson["nametype"].stringValue
meteor.recclass = meteorJson["recclass"].stringValue
meteor.lastUpdate = NSDate()
meteorites.append(meteor)
}
}
}
}
self.meteorites = meteorites
}
}

Related

Swift Variable not Changing

Below is my function which is not changing the return Value. How do I get the function to return the modified returnVal?
func RecieveActivitySummary() -> HKActivitySummary {
var returnVal = HKActivitySummary()
self.makeQuery() { (summary) in
returnVal = summary //attempting to alter returnVal
print(returnVal) //prints out returnVal object that I want with data (seemingly modified)
}
print(returnVal) //prints out unmodified returnVal
return returnVal //then goes on to return an empty returnVal object
}
Here is my makeQuery() function if it helps address the problem:
func makeQuery(completion: #escaping (_ summary : HKActivitySummary) -> ()) {
let calendar = NSCalendar.current
let endDate = Date()
guard let startDate = calendar.date(byAdding: .day, value: 0, to: endDate) else {
fatalError("error")
}
let units: Set<Calendar.Component> = [.day, .month, .year, .era]
var startDatecomps = calendar.dateComponents(units, from: startDate)
startDatecomps.calendar = calendar
var endDatecomps = calendar.dateComponents(units, from: endDate)
endDatecomps.calendar = calendar
let summariesWithinRange = HKQuery.predicate(forActivitySummariesBetweenStart: startDatecomps, end: endDatecomps)
let query = HKActivitySummaryQuery(predicate: summariesWithinRange) {
(sample, results, error) -> Void in
if let results = results {
var summary = results[0]
completion(summary)
}
}
healthstore.execute(query)
}

how to update TIME ZONE for using it in my calendar correctly?

Issue here is that while from API I get date in this form
Foundation.Date 2021-07-20 00:00:00 UTC
I successfully add hours, with I get start hour from API in form of "08:30:00" with gives me the value for start (but same issue about the end date) this way:
Foundation.Date 2021-07-20 08:30:00 UTC
but, when I get to calendar, the events are set to 10:30 AM, how could I correct the TIME ZONE?
I tested things like
let calendar = Calendar()
calendar.timezone = .current
but did not work
example from response
{
"data": {
"appointments": [
{
"date": "2021-07-20T00:00:00.000Z",
"time": {
"from": "08:30:00",
"to": "09:30:00"
}
}
]
}
}
my code
func onSynchronize(detailInfo: AppointmentDetail) {
let appointment = detailInfo
let fakeAddress = "fake address"
let fakeLocality = "Alderaan"
let store = EKEventStore()
store.requestAccess(to: .event) {(granted, error) in
if !granted { return }
let event = EKEvent(eventStore: store)
event.title = "my title"
guard let date0 = appointment.date else { return }
guard let hours = appointment.time else { return }
guard let host = appointment.host else { return }
if let componentsFrom = self.timeComponentsFromHourInString(from: hours.from) {
if let start = Calendar.current.date(byAdding: componentsFrom, to: date0, wrappingComponents: false) {
event.startDate = start
event.addAlarm(EKAlarm(absoluteDate: start.dayBefore))
event.addAlarm(EKAlarm(absoluteDate: start.halfHourBefore))
event.endDate = start.hourAfter
}
}
if let componentsTo = self.timeComponentsFromHourInString(from: hours.to) {
if let end = Calendar.current.date(byAdding: componentsTo, to: date0, wrappingComponents: false) {
event.endDate = end
}
}
event.location = fakeAddress.concats(fakeLocality, withSeparator: "-")
event.url = URL(string: BanksUtils.appLocalizeString(forKey: "url"))
if let managerDescription = host.managerDescription,
let manager = BanksUtils.localizeString(forKey: "handledBy").concats(managerDescription.capitalized),
let description = appointment.requestorDescription,
let reason = BanksUtils.localizeString(forKey: "reason").concats(description.capitalized) {
event.notes = manager + "\n" + reason
} else {
if let description = appointment.requestorDescription {
event.notes = BanksUtils.localizeString(forKey: "reason").concats(description.capitalized)
}
}
let structuredLocation = EKStructuredLocation(title: fakeAddress)
structuredLocation.geoLocation = self.location
event.structuredLocation = structuredLocation
event.calendar = store.defaultCalendarForNewEvents
do {
try store.save(event, span: .thisEvent, commit: true)
} catch {
Log.logger.error("Error: \(String(describing: error))")
}
guard let url = URL(string: "calshow:\(event.startDate.timeIntervalSinceReferenceDate)") else { return }
DispatchQueue.main.async {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
///Allows to get HH:MM:SS from an hour in form of a string
func timeComponentsFromHourInString(from string: String) -> DateComponents? {
let comps = string.components(separatedBy: ":")
guard comps.count == 3,
let hr = Int(comps[0]),
let mn = Int(comps[1]),
let sc = Int(comps[2])
else { return nil }
return DateComponents(hour: hr, minute: mn, second: sc)
}

Swift function execution order

I'm trying to load sleep data from healthkit.
after retrieving some data, I converted it into Double type to use chart, and reflected them on setChart function.
But here's some problem.
To successfully show on chart, my function should executed in order.
Ideal order is : retrieve data, convert data -> setchart
but the order is not kept.
I read some articles about dispatchqueue, but still I can't solve the problem.
What should I do to solve this problem? \
override func viewDidLoad() {
super.viewDidLoad()
//선행작업
DispatchQueue.global(qos: .userInteractive).sync {
print("1")
self.retrieveSleepAnalysis()
}
DispatchQueue.global(qos: .default).sync {
print("2")
self.recentSleepStartHourDouble = self.stringTimeToDouble(stringTime: self.recentSleepStartHour)
self.wakeUp = self.wakeupTimeCalcuation(start: self.recentSleepStartHourDouble, end: self.resultToSleepAmount)
}
DispatchQueue.main.async {
// self.recentSleepStartHourDouble = self.stringTimeToDouble(stringTime: self.recentSleepStartHour)
// self.wakeUp = self.wakeupTimeCalcuation(start: self.recentSleepStartHourDouble, end: self.resultToSleepAmount)
self.setChart(days: self.recentSleepDate.reversed(), sleepTimes: self.recentSleepStartHourDouble.reversed(), wakeupTimes:
public func retrieveSleepAnalysis() {
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// print(sleepType)
// Use a sortDescriptor to get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// we create our query with a block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 7, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// something happened
return
}
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
let value = (sample.value == HKCategoryValueSleepAnalysis.inBed.rawValue) ? "InBed" : "Asleep"
print("Healthkit sleep: \(sample.startDate) \(sample.endDate) - value: \(value)")
let sleepHour = Calendar.current.component(.hour, from: sample.startDate)
print("현지시각으로 잠든시간 \(sleepHour)")
if sleepHour < 19 && sleepHour > 12{
print("낮잠")
}
else{
self.resultToSleepAmount.append(CFDateGetTimeIntervalSinceDate(sample.endDate as CFDate, sample.startDate as CFDate)/3600)
let myDateFormatter = DateFormatter()
myDateFormatter.dateFormat = "MM / dd"
myDateFormatter.locale = Locale(identifier: "ko_KR")
let sleepDate = myDateFormatter.string(from: sample.startDate)
self.recentSleepDate.append(sleepDate)
let myHourFormatter = DateFormatter()
myHourFormatter.dateFormat = "HHmm"
myHourFormatter.locale = Locale(identifier: "ko_KR")
let sleepStartHour = myHourFormatter.string(from: sample.startDate)
self.recentSleepStartHour.append(sleepStartHour)
print("잠든 시간은 몇시몇분? \(sleepStartHour)")
let sleepEndHour = myHourFormatter.string(from: sample.endDate)
self.recentSleepEndHour.append(sleepEndHour)
}
}
print("total amount of sleep time : \(self.resultToSleepAmount), 현재 크기는 : \(self.resultToSleepAmount.count)")
}
}
}
// finally, we execute our query
healthStore.execute(query)
}

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)
}
}
}

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.