I'm trying to created an fun facts app. It displays a random string from an array every time, then that fact is deleted from the array. For me code, when the app is first launched it gets new array of facts and it saves the data when the app is closed and uses the array from the previous launch every time after the initial launch. My problem is I get an error "Thread 1: signal SIGABRT" when I try to remove a string from my array on my 4th last line. Please tell me what corrections I need to make. I am fairly new to programming. I appreciate all the help I get. Thanks for your time
import Foundation
let userDefaults = NSUserDefaults.standardUserDefaults()
func isAppAlreadyLaunchedOnce()->Bool{
let defaults = NSUserDefaults.standardUserDefaults()
if let isAppAlreadyLaunchedOnce = defaults.stringForKey("isAppAlreadyLaunchedOnce"){
println("App already launched")
return true
}
else{
defaults.setBool(true, forKey: "isAppAlreadyLaunchedOnce")
println("App launched first time")
return false
}
}
struct newFactBook {
let factsArray : NSMutableArray = [
"Ants stretch when they wake up in the morning.",
"Ostriches can run faster than horses.",
"Olympic gold medals are actually made mostly of silver.",
"You are born with 300 bones; by the time you are an adult you will have 206.",
"It takes about 8 minutes for light from the Sun to reach Earth.",
"Some bamboo plants can grow almost a meter in just one day.",
"The state of Florida is bigger than England.",
"Some penguins can leap 2-3 meters out of the water.",
"On average, it takes 66 days to form a new habit.",
"Mammoths still walked the earth when the Great Pyramid was being built."]
}
var checkLaunch = isAppAlreadyLaunchedOnce()
var oldFunFactsArray : NSMutableArray = []
if(checkLaunch == false){
oldFunFactsArray = newFactBook().factsArray
}
else if (checkLaunch == true){
oldFunFactsArray = userDefaults.objectForKey("key") as! NSMutableArray
}
func randomFacts1() -> (String, Int){
var unsignedArrayCount = UInt32(oldFunFactsArray.count)
var unsignedRandomNumber = arc4random_uniform(unsignedArrayCount)
var randomNumber = Int(unsignedRandomNumber)
return (oldFunFactsArray[randomNumber] as! String, randomNumber)
}
oldFunFactsArray.removeObjectAtIndex(randomFacts1().1) //this gives me the error
//oldFunFactsArray.removeValueAtIndex(randomFacts1().1, fromPropertyWithKey: "key") //this gives me the same error
//oldFunFactsArray.removeAtIndex(randomFacts1().1) //This gives me the error "NSMutableArray does not have a member named 'removeAtIndex'
userDefaults.setObject(oldFunFactsArray, forKey:"key")
userDefaults.synchronize()
println(oldFunFactsArray)
You will have to create a mutableCopy of the facts array to removeObjects from it.
if(checkLaunch == false){
oldFunFactsArray = newFactBook().factsArray.mutableCopy
}
Related
I am comparing whether a user should get a win or loss in their score column. I can't get the code to store the win/loss and then add 1 on top of it every time the if statement runs. Here is my if statement, followed by where i call it in the view did load. Cant seem to figure where I'm going wrong. thanks for the help!
viewDidLoad {
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
winsLabel.text = "\(dailyWinsDefault)"
print(dailyWinsDefault)
let dailyLossDefault = UserDefaults.standard.integer(forKey: "dailyLossDefault")
lossLabel.text = "\(dailyLossDefault)"
print(dailyLossDefault)
}
showWinLossVC.callbackForWinLoss = { result in
if result > 0.0 {
self.dailyWins += 1
UserDefaults.standard.set(self.dailyWins, forKey: "dailyWinsDefault")
self.winsLabel.text = String(self.dailyWins)
print(self.dailyWins)
}
else if result < 0.0 {
self.dailyLosses += 1
UserDefaults.standard.set(self.dailyLosses, forKey: "dailyLossDefault")
self.lossLabel.text = "\(self.dailyLosses)"
Couple of mistakes
Mistake 1:
Seems like you are setting integer in UserDefault for key dailyWinsDefault using statement
UserDefaults.standard.set(self.dailyWins, forKey: "dailyWinsDefault")
And you expect it to return you String when you retrieve it with
if let dailyWinsDefault = UserDefaults.standard.string(forKey: dailyWinsDefault)
Why will it not return nil ?
use
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
Mistake 2:
In your statement
if let dailyWinsDefault = UserDefaults.standard.string(forKey: dailyWinsDefault)
dont you think dailyWinsDefault should be in double quote as its a key and supposed to be string ?
try
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
EDIT:
As OP has updated code and now facing a issue with his updated code and requested for help updating my answer.
Couple of issues again
Mistake 1:
In entire ViewDidLoad method u never assigned the value retried from user defaults to property dailyWins and dailyLoses
viewDidLoad {
let dailyWinsDefault = UserDefaults.standard.integer(forKey: "dailyWinsDefault")
self.dailyWins = dailyWinsDefault //this is missing
winsLabel.text = "(dailyWinsDefault)"
print(dailyWinsDefault)
let dailyLossDefault = UserDefaults.standard.integer(forKey: "dailyLossDefault")
self.dailyLosses = dailyLossDefault //this is missing
lossLabel.text = "\(dailyLossDefault)"
print(dailyLossDefault)
}
Mistake 2:
Though not specific to your problem, you should always call super.viewDidLoad in your viewDidLoad unless u have a very firm reason for not doing so
That should help :)
I am fetching JSON data from my server. I am using Alamofire in my code to fetch data from server.
Here's how I am decoding it:
Alamofire.request(URL_GET_DATA, method: .post).responseJSON { response in
//getting json
if let json = response.result.value {
print(json)
let newsJson : NSArray = json as! NSArray
self.parseData(data: ((newsJson[0] as AnyObject).value(forKey: "message") as? String)!)
}
}
func parseData(data : String){
//traversing through all elements of the array
let newsArray: NSArray = data as! NSArray
for i in 0..<newsArray.count{
//adding hero values to the hero list
self.newsList.append(Objectnews(
id : (newsArray[i] as AnyObject).value(forKey: "id") as? String,
heading: (newsArray[i] as AnyObject).value(forKey: "heading") as? String,
description : (newsArray[i] as AnyObject).value(forKey: "description") as? String,
time_stamp : (newsArray[i] as AnyObject).value(forKey: "time_stamp") as? String,
type : (newsArray[i] as AnyObject).value(forKey: "type") as? String,
imageurl: (newsArray[i] as AnyObject).value(forKey: "imageurl") as? String
))
}
print("reloading table")
//displaying data in tableview
self.tableView.reloadData()
}
Here's the log for same. It contains the response from server and the error it is throwing:
{
error = 0;
message = (
{
description = "September 5th marks Teacher\U2019s Day in India. It is celebrated on the birthday of our second President, Sarvepalli Radhakrishnan. It is a day when we pay respect to our teachers and appreciate their contribution to the society and our individual lives and plan special surprises, have a chance to thank them for their selfless work in making the leaders for tomorrow. N.S.S. unit celebrated Teacher's day by gifting a hand made card to each Teaching and Non-Teaching Staff of P.G.D.A.V. College. We will also like to put forward a special thanks to the creative department team of N.S.S. for their terrific job in making those beautiful hand made cards. Happy Teachers' Day to All!";
heading = "Teacher's Day Celebration";
id = 16;
imageurl = "http://check.png";
"time_stamp" = "05/09/2017 22:58";
type = News;
},
{
description = "Incessant rains and floods have brought a havoc in one of India's most populous state, Bihar due to which roads have been closed, houses destroyed and crores affected. Rahat - an initiative of Goonj NGO steps in such situations helping all those in need and this time NSS PGDAV is doing their share of help by donating Via GOONJ and is organising a Collection Drive for the flood survivors. Your donation will provide shelter, food and comfort to families. Below are some of the things you can donate to help the people:-\n\n-Tarpaulins\n-Old flex and ropes\n-Mosquito nets\n-Dry ration\n-Clothing\n-Good quality blankets \n-Wooden toiletries\n-Utensils \n-Bucket\n-Torch and batteries\n-Umbrella\n-Slippers\n-Candles\n-Solar lights etc\n\nGiving is not just about making a donation. It's about making a difference! So step forward and #DONATE.\n\nDate: 4th-9th September 2017.\nVenue- Lobby/Office Area.\nTime: 10AM-1PM.\n\nFor queries contact:\nEkta- 7503894368\nJhanvi- 7838401571";
heading = "Collection Drive for Flood Affected Areas";
id = 15;
imageurl = "http://check.png";
"time_stamp" = "02/09/2017 21:34";
type = News;
},
{
description = "\"Beauty isn't about having a pretty face, it is about having a pretty soul, a pretty heart and a pretty mind.\"\n\nN.S.S. P.G.D.A.V. is honoured and takes a great pride in telling that MS. LAXMI AGARWAL will be speaking at the orientation program of N.S.S. which is set to be held on 26th August from 12:00 P.M. onwards. Ms. Laxmi Agarwal has inspired this country by her courage and has been the recipient of International Women of Courage Award by Michelle Obama. She is an epitome of bravery.\n\nWe could not have been more proud and excited to have her encourage the students and motivate them to overcome barriers and achieve what they aim for. We are sure she will fill in positive vibes around the program and will inspire us all to do what we need to do, to achieve what we dream for, and to inspire others for the same.";
heading = "NSS Orientation ";
id = 14;
imageurl = "http://check.png";
"time_stamp" = "25/08/2017 16:47";
type = News;
},
{
description = "Welcome Fresher's,\nHere is the last step for final selection to become a member of N.S.S. Team.\nIt's the Personal Interview. So following is the list of students who have to appear on August 2, 2017\n\n\n\n\n\n\nDAY 1 - AUGUST 2, 2017\U00a0\nSLOT 1- 10:00 A.M. - 11:00 A.M.\n\n\nAmit\U00a0\nAditya Sapra\U00a0\nAman Gupta\U00a0\nAadesh Sachdeva\U00a0\nAkshay Gaba\U00a0\nAkansha Sharma\U00a0\nAnjali Kataria\U00a0\nAakash\U00a0\nAbhinav Kumar Malviya\U00a0\nAnkita Rai\U00a0\nAstha Kanojia\U00a0\nAnita Mandal\U00a0\nAnchal\U00a0\nArif\U00a0\nAaftab Ahmad\U00a0\nAshish\U00a0\nAashirya Mittal\U00a0\nAnshul Saini\U00a0\nAnanya Singh\U00a0\nAbhay Goyal\U00a0\nAnshu Kumari Gupta\U00a0\nAjay Kumar\U00a0\nAaftab\U00a0\nAnjali Bhatt\U00a0\nAditi Singh\U00a0\nAnirudh Ahlawat\U00a0\nAshutosh\U00a0\nAnuradha\U00a0\nAmiza\U00a0\nAsmat Khan\U00a0\nAyushi Chadha\U00a0\nAmbujakshi Bhardwaj\U00a0\nAnubhav jain\U00a0\nAshutosh Verma\U00a0\nAbhishek Anand\U00a0\nAbhiraj\U00a0\nAkansha\U00a0\nAbhishek ranjan\U00a0\nAyesha Chauhan\U00a0\nAkansha\U00a0\n\n\nSLOT 2 - 11:00 A.M. - 12:00 P.M.\n\n\nAnkit\U00a0\nAmar Jeet Verma\U00a0\nAnshika Adlakha\U00a0\nAnkur Verma\U00a0\nAnkita\U00a0\nAditya Raj Chaudhary\U00a0\nBhavay Virmani\U00a0\nBunty Kumar\U00a0\nBhawna\U00a0\nBronika Paul\U00a0\nBarkat Ali\U00a0\nBrahmadutt\U00a0\nBadal Sharma\U00a0\nBhavnish Sharma\U00a0\nChandra Prakash Bhambhu\U00a0\nChanchal\U00a0\nChetna Garg\U00a0\nChetan Verma\U00a0\nChirag Sethi\U00a0\nChetan\U00a0\nDeependra Pal Yadav\U00a0\nDivya Agnihotri\U00a0\nDeepak Sahni\U00a0\nDrishya Wahil\U00a0\nDiksha paut\U00a0\nDevendra Yadav\U00a0\nDivya Chabra\U00a0\nDeep Singh\U00a0\nDeepankar Vaid\U00a0\nDrishti Goyal\U00a0\nEkta Shankar\U00a0\nFarzan Iqram\U00a0\nGaurav Bhatt\U00a0\nGarima Behl\U00a0\nGaurav Saini\U00a0\nGarvish Nanda\U00a0\nGaurav Kumar\U00a0\nGarima Payla\U00a0\nGauri Dhamija\U00a0\nharshit Singh\U00a0\n\n\nSLOT 3 - 12:00 P.M. - 1:00 P.M.\n\n\U00a0\U00a0\nHimanshi Sharma\U00a0\nHarsh Gupta\U00a0\nHarshit Girdhar\U00a0\nHarsh Bansal\U00a0\nHarshita Chauhan\U00a0\nHeena\U00a0\nHeena\U00a0\nIrfan Khan\U00a0\nIshu Rai\U00a0\nJaspreet kaur\U00a0\nJyoti\U00a0\nJaisny\U00a0\nJyoti Nishad\U00a0\nKrishan Kumar\U00a0\nKrishna\U00a0\nKetu\U00a0\nKrishika Arora\U00a0\nKhushboo Yadav\U00a0\nKanan Makker\U00a0\nKriti Kapoor\U00a0\nKanika Yadav\U00a0\nKiran Pachori\U00a0\nKunal Nagar\U00a0\nKhushboo Bansal\U00a0\nKunal Sharma\U00a0\nKajal\U00a0\nLalit Mohan\U00a0\nLovely Mathur\U00a0\nMitali Baisoya\U00a0\nmanisha\U00a0\nMiti Dangwal\U00a0\nMukul Puri\U00a0\nMansingh\U00a0\nManoj Meena\U00a0\nMohammad faisal\U00a0\nM. Danish Ameer\U00a0\nMohit Jain\U00a0\nMantu Babu\U00a0";
heading = "NSS Interview Day 1";
id = 13;
imageurl = "http://check.png";
"time_stamp" = "01/08/2017 22:30";
type = News;
},
{
description = "Time and tide waits for none, we all learnt this proverb in our primary school but yesterday I experienced something contradictory to my learning. I saw the time stopping and taking a moment to absorb what was happening around? And guess what was it, it was you, the participants, the audience of Sevaarth whose overwhelming response took the event to a whole new level. You made the mighty time get adrenaline rush. \nThis year we brought in many technical advancement to our NSS unit. We launched our mobile app, website. We facilitated user with many problem solving features. We had more number of volunteers than previous year. Our reach set a new record in the history of all NSS Unit with a growth rate of more than 17,000%. As Sevaarth '17 ended in a great way, we are much more inspired by the work of every people who put their day and night in making sure you get what we have to offer. We are humbled by the response of people. \nWe owe a great deal to our sponsers - Little App, PaisaWapas, DCOP, DSB, Internsala, Nestle, SelfDrives.in, CareerLauncher, Student Stories, The Education Tree, DU Beat, Drama Cafe. Thank you to all.\nWe have a aim. We have people who craves great passion. And, these two thing is advancing us to a new level each and every day. Every day, NSS puts a positive value in this society. We are here to make a better world for each and every person out there. No matter what's your race, nationality, religion, we will make sure that you get what you deserve. In the coming years, NSS Unit of P.G.D.A.V College will get into different areas of society from education to better health care by acting as a bridge by them. \nSevaarth is an opportunity to showcase the talent of people who choose stay away from limelight in front of people. Sevaarth seemed to have touched its highest point with all the fun, the immense hard work, representing our culture from a different perspective. \nWe promise to bring you a more vibrant version of Sevaarth next year. Adios! ";
heading = "Thank you";
id = 12;
imageurl = "http://check.png";
"time_stamp" = "22/01/2017 00:22";
type = News;
}
);
}
Could not cast value of type '__NSDictionaryI' (0x106456288) to 'NSArray' (0x106455e28).
(lldb)
How can I properly decode it based on the error in the log? I am using Swift.
Follow this step :
Creat a class called APIManager
Create Delegates method like this
//MARK : Custom Protocol Methods protocol
apiManagerDelegate:NSObjectProtocol {
func apiSuccessResponse(_ response: Dictionary<String, AnyObject>)
func APIFailureResponse(_ msgError: String)
}
//MARK : Custom Extension Methods
extension apiManagerDelegate {
func apiSuccessResponse(_ response: Dictionary<String, AnyObject>){
// leaving this empty
}
func APIFailureResponse(_ msgError: String){
// leaving this empty
} }
In Your View Controller , Conforms this Delegate Methods .
Defined method like this to consume web service in API Manager
func signUpAPI(firstName : String, lastName:String, email:String, password:String, confirmPwd:String, mobile:String, age:String, gender:String, signUp_type:Int) {
//Payload Dict
let payloadDict = [
"first_name" : firstName,
"last_name" : lastName,
"email" : email,
....
] as [String : Any]
//Showing activity indicator
---progressbar goes here
//Call Web API using Alamofire library
AlamoFireSharedManagerInit()
Alamofire.request(HCConstants.URL.HC_BASEURL + HCConstants.URL.SIGNUP, method: .post, parameters: payloadDict, encoding: JSONEncoding.default, headers: nil).responseJSON { response in
do
{
//Checking For Error
if let error = response.result.error {
//Call failure delegate method
print(error)
//Stop AcitivityIndicator
self.hideHud()
self.delegate?.APIFailureResponse("Something goes wrong , Try after some time!!")
return
}
//Store Response
let responseValue = try JSONSerialization.jsonObject(with: response.data!, options: JSONSerialization.ReadingOptions()) as! Dictionary<String, AnyObject>
print(responseValue)
//Check Success Flag
print(responseValue["success"] as! Bool)
if let mSuccessFlag = responseValue["success"] as? Bool {
//Failure message
if !mSuccessFlag {
self.delegate?.APIFailureResponse(responseValue["message"] as! String? ?? "Something goes wrong , Please try after some time")
}
//Call success delegate method
else {
self.delegate?.apiSuccessResponse(responseValue)
}
}
//Stop AcitivityIndicator
self.hideHud()
} catch {print("Sign up API fired exception")}
}
}
Above method parse the data and send it to the success or failure method to your viewcontroller and from there you need to populate the data and just render it .
Isn't it super easy ?
I have an array of strings. I would like to display 3 unique items from this array randomly. Then every 5 seconds, one of the items gets replaced with another unique item (my idea here is adding an animation with a delay).
I can display the 3 strings, however sometimes they repeat, and the timer is not updating the factLabel label.
Here's my progress:
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func randomFact() -> String {
let arrayCount = model.cancunFacts.count
let randomIndex = Int(arc4random_uniform(UInt32(arrayCount)))
return model.cancunFacts[randomIndex]
}
// Display the facts
func updateUI() {
Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(randomFact), userInfo: nil, repeats: true)
factLabel.text = randomFact() + " " + randomFact() + " " + randomFact()
}
How do I get the text to always update randomly, without the 3 facts repeating?
Create an array of indexes. Remove a random index from the array, use it to index into your strings. When the array of indexes is empty, refill it.
Here is some sample code that will generate random, non-repeating strings:
var randomStrings = ["Traitor", "Lord Dampnut", "Cheeto-In-Chief",
"F***face Von Clownstick", "Short-Fingered Vulgarian",
"Drumpf", "Der Gropenführer", "Pumpkin in a suit"]
var indexes = [Int]()
func randomString() -> String {
if indexes.isEmpty {
indexes = Array(0...randomStrings.count-1)
}
let index = Int(arc4random_uniform(UInt32(indexes.count)))
let randomIndex = indexes.remove(at: index)
return randomStrings[randomIndex]
}
for i in 1...100 {
print (randomString())
}
(Note that it may still generate repeating strings in the case when the array of indexes is empty and it needs to be refilled. You'd need to add extra logic to prevent that case.)
Version 2:
Here is the same code, modified slightly to avoid repeats when the array of indexes is empty and needs to be refilled:
var randomStrings = ["tiny-fingered", "cheeto-faced", "ferret-wearing", "sh*tgibbon"]
var indexes = [Int]()
var lastIndex: Int?
func randomString() -> String {
if indexes.isEmpty {
indexes = Array(0...randomStrings.count-1)
}
var randomIndex: Int
repeat {
let index = Int(arc4random_uniform(UInt32(indexes.count)))
randomIndex = indexes.remove(at: index)
} while randomIndex == lastIndex
lastIndex = randomIndex
return randomStrings[randomIndex]
}
for i in 1...10000 {
print (randomString())
}
Even though it's using a repeat...while statement, the repeat condition will never fire twice in a row, because you'll never get a repeat except right after refilling the array of indexes.
With that code, if there is a repeat, the selected string will be skipped on that pass through the array. To avoid that, you'd need to adjust the code slightly to not remove a given index from the array until you verify that it is not a repeat.
Version 3:
Version 2, above, will skip an entry if it picks a repeat when it refills the array. I wrote a 3rd version of the code that refills the array, removes the last item it returned so that it can't repeat, and then adds it back to the array once it's picked a random item. This third version will always return every item in the source array before refilling it and will also never repeat an item. Thus it's truly random with no bias:
import UIKit
var randomStrings = ["Traitor", "Lord Dampnut", "Cheeto-In-Chief",
"F***face Von Clownstick", "Short-Fingered Vulgarian",
"Drumpf", "Der Gropenführer", "Pumpkin in a suit"]
var indexes = [Int]()
var lastIndex: Int?
var indexToPutBack: Int?
func randomString() -> String {
//If our array of indexes is empty, fill it.
if indexes.isEmpty {
indexes = Array(0...randomStrings.count-1)
print("") //Print a blank line each time we refill the array so you can see
//If we have returned an item previously, find and remove that index
//From the refilled array
if let lastIndex = lastIndex,
let indexToRemove = indexes.index(of: lastIndex) {
indexes.remove(at: indexToRemove)
indexToPutBack = indexToRemove //Remember the index we removed so we can put it back.
}
}
var randomIndex: Int
let index = Int(arc4random_uniform(UInt32(indexes.count)))
randomIndex = indexes.remove(at: index)
//If we refilled the array and removed an index to avoid repeats, put the removed index back in the array
if indexToPutBack != nil{
indexes.append(indexToPutBack!)
indexToPutBack = nil
}
lastIndex = randomIndex
return randomStrings[randomIndex]
}
for i in 1...30 {
print (randomString())
}
Sample output:
Short-Fingered Vulgarian
F***face Von Clownstick
Pumpkin in a suit
Drumpf
Lord Dampnut
Traitor
Der Gropenführer
Cheeto-In-Chief
Der Gropenführer
Drumpf
Lord Dampnut
Short-Fingered Vulgarian
Cheeto-In-Chief
Pumpkin in a suit
Traitor
F***face Von Clownstick
Short-Fingered Vulgarian
F***face Von Clownstick
Drumpf
Traitor
Cheeto-In-Chief
Lord Dampnut
Pumpkin in a suit
Der Gropenführer
Lord Dampnut
Short-Fingered Vulgarian
Pumpkin in a suit
Cheeto-In-Chief
Der Gropenführer
F***face Von Clownstick
Your timer is calling random fact, which simply returns a fact and doesn't do anything. You should probably have some third method called initializeTimer that does the Timer.scheduledtimer, which you should take out of your updateUI method. That timer should call updateUI. This would fix your labels updating. Also you would call initializeTimer in your viewDidLoad instead of updateUI. As far as preventing the repetition of facts, Duncan C's idea of having a separate array that you remove items from as you set new random facts then fill back up when it's empty seems like a good idea.
It may be easiest to maintain two arrays, usedStrings and unusedStrings, of random strings, like this:
var unusedStrings: [String] = ["king", "philip", "calls", "out", "for", "good", "soup"]
var usedStrings: [String] = []
func randomString() -> String {
if unusedStrings.isEmpty() {
unusedStrings = usedStrings
usedStrings = []
}
let randomIndex = Int(arc4random_uniform(unusedStrings.count))
let randomString = unusedStrings[randomIndex]
usedStrings.append(randomString)
return randomString
}
Here's a struct I've written to convert an NSTimeInterval into a walltime-based dispatch_time_t:
public struct WallTimeKeeper {
public static func walltimeFrom(spec: timespec)->dispatch_time_t {
var mutableSpec = spec
let wallTime = dispatch_walltime(&mutableSpec, 0)
return wallTime
}
public static func timeStructFrom(interval: NSTimeInterval)->timespec {
let nowWholeSecsFloor = floor(interval)
let nowNanosOnly = interval - nowWholeSecsFloor
let nowNanosFloor = floor(nowNanosOnly * Double(NSEC_PER_SEC))
println("walltimekeeper: DEBUG: nowNanosFloor: \(nowNanosFloor)")
var thisStruct = timespec(tv_sec: Int(nowWholeSecsFloor),
tv_nsec: Int(nowNanosFloor))
return thisStruct
}
}
I've been trying to test the accuracy of it in a Playground, but my results are confusing me.
Here's the code in my Playground (with my WallTimeKeeper in the Sources folder):
var stop = false
var callbackInterval: NSTimeInterval?
var intendedTime: NSDate?
var intendedAction: ()->() = {}
func testDispatchingIn(thisManySeconds: NSTimeInterval){
intendedTime = NSDate(timeIntervalSinceNow: thisManySeconds)
intendedAction = stopAndGetDate
dispatchActionAtDate()
loopUntilAfterIntendedTime()
let success = trueIfActionFiredPunctually() //always returns false
}
func dispatchActionAtDate(){
let timeToAct = dateAsDispatch(intendedTime!)
let now = dateAsDispatch(NSDate())
/*****************
NOTE: if you run this code in a Playground, comparing the above two
values will show that WallTimeKeeper is returning times the
correct number of seconds apart.
******************/
dispatch_after(timeToAct, dispatch_get_main_queue(), intendedAction)
}
func loopUntilAfterIntendedTime() {
let afterIntendedTime = intendedTime!.dateByAddingTimeInterval(1)
while stop == false && intendedTime?.timeIntervalSinceNow > 0 {
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode,
beforeDate: afterIntendedTime)
}
}
func trueIfActionFiredPunctually()->Bool{
let intendedInterval = intendedTime?.timeIntervalSinceReferenceDate
let difference = intendedInterval! - callbackInterval!
let trueIfHappenedWithinOneSecondOfIntendedTime = abs(difference) < 1
return trueIfHappenedWithinOneSecondOfIntendedTime
}
func dateAsDispatch(date: NSDate)->dispatch_time_t{
let intendedAsInterval = date.timeIntervalSinceReferenceDate
let intendedAsStruct = WallTimeKeeper.timeStructFrom(intendedAsInterval)
let intendedAsDispatch = WallTimeKeeper.walltimeFrom(intendedAsStruct)
return intendedAsDispatch
}
func stopAndGetDate() {
callbackInterval = NSDate().timeIntervalSinceReferenceDate
stop = true
}
testDispatchingIn(3)
...so not only doestrueIfActionFiredPunctually() always returns false, but the difference value--intended to measure the difference between the time the callback fired and the time it was supposed to fire--which in a successful result should be really close to 0, and certainly under 1--instead comes out to be almost exactly the same as the amount of time the callback was supposed to wait to fire.
In summary: an amount of time to wait is defined, and an action is set to fire after that amount of time. When the action fires, it creates a timestamp of the moment it fired. When the timestamp is compared to the value it should be, instead of getting close to zero, we get close to the amount of time we were supposed to wait.
In other words, it appears as if the action passed to dispatch_after is firing immediately, which it absolutely shouldn't!
Is this something wrong with Playgrounds or wrong with my code?
EDIT:
It's the code. Running the same code inside a live app gives the same result. What am I doing wrong?
I figured it out. It's a head-smacker. I'll leave it up in case anyone is having the same problem.
I was using NSDate().timeIntervalSinceReferenceDate to set my walltimes.
Walltimes require NSDate().timeIntervalSince1970!
The dispatch_after tasks all fired instantly because they thought they were scheduled for over forty years ago!
Changing everything to NSDate().timeIntervalSince1970 makes it work perfectly.
Moral: don't use walltimes unless you're sure your reference date is 1970!
I have this code, where i would call this "checkingfunction" function. I am not using any threading in my app, I would love to use if it benefits the performance of my app.
The "checkingfunction", takes more time than i expected. It takes more than 30 seconds to complete the execution. I cant wait that long in my app. That is not good, in middle of the game.
Somebody help me out here to rewrite the function, so that i can execute it in a faster way. Some functional programming way, if possible.
func returnCharactersFromAFourLetterString(inputString : String) -> (First : Character,Second : Character, Third : Character, Fourth : Character)
{
return (inputString[advance(inputString.startIndex, 0)],inputString[advance(inputString.startIndex, 1)],inputString[advance(inputString.startIndex, 2)],inputString[advance(inputString.startIndex, 3)])
}
func checkingWords(userEnteredWord : String)
{
var tupleFourLetters = self.returnCharactersFromAFourLetterString(userEnteredWord)
var firstLetter = String(tupleFourLetters.First)
var secondLetter = String(tupleFourLetters.Second)
var thirdLetter = String(tupleFourLetters.Third)
var fourthLetter = String(tupleFourLetters.Fourth)
var mainArrayOfWords : [String] = [] // This array contains around 0.2 million words
var userEnteredTheseWords : [String] = [] // This array contains less than 10 elements
// Check for FirstLetter
for index in 0..<array.count // Array of Letters as Strings , count = 200
{
var input = array[index]
var firstWord = "\(input)\(secondLetter)\(thirdLetter)\(fourthLetter)"
var secondWord = "\(firstLetter)\(input)\(thirdLetter)\(fourthLetter)"
var thirdWord = "\(firstLetter)\(secondLetter)\(input)\(fourthLetter)"
var fourthWord = "\(firstLetter)\(secondLetter)\(thirdLetter)\(input)"
if !contains(userEnteredTheseWords, firstWord) && !contains(userEnteredTheseWords, secondWord) && !contains(userEnteredTheseWords, thirdWord) && !contains(userEnteredTheseWords, fourthWord)
{
if contains(mainArrayOfWords, firstWord )
{
self.delegate?.wordMatchedFromDictionary(firstWord)
return
}
else if contains(mainArrayOfWords, secondWord)
{
self.delegate?.wordMatchedFromDictionary(secondWord)
return
}
else if contains(mainArrayOfWords, thirdWord)
{
self.delegate?.wordMatchedFromDictionary(thirdWord)
return
}
else if contains(mainArrayOfWords, fourthWord)
{
self.delegate?.wordMatchedFromDictionary(fourthWord)
return
}
}
if index == array.count - 1
{
self.delegate?.wordMatchedFromDictionary("NoWord")
}
}
}
Input of this function is a four letter word, Inside this function i am changing each letter by looping through that 200 letters, and checking in the mainArray that, whether any of these changed words exists in mainArray. If exists, then return me that word, otherwise just return NoWord. So totally, we can see that we are checking that contains(mainArray, word) thing around 800 times, i think this is the line which consumes more time, cause mainArray contains 0.2 million words.
Use dictionaries to look up things.
When you measure times, especially with Swift code, measure a release build, not a debug build. On the other hand, measure on the slowest device capable of running your code.