You can't initialize a standard property with the value of another property (and there's no promise that they'll be initialized in source code order the way you're assuming they will be). You can initialize a lazy
property with the value of another one, but that's way too complicated for this problem. Accessing an object's property requires that the entire object be initialized, and it isn't initialized until all the properties are set.
This kind of logic is best done in an init
. Here's how I would write it myself (with the shuffle extension):
class ViewController {
//There's just one of these in the world, not one per class. And I can't
// imagine it changing (so should be let)
static let allEvents = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT - “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]
// Do you really need both of these? If so, I'd probably actually just keep
// eventIndices and compute events when needed, but if you access it a lot,
// this is fine. But "event1" "event2" etc is bad practice
let eventIndices: [Int]
let events: [String]
init() {
eventIndices = Array(ViewController.allEvents.indices.shuffled().prefix(4))
events = eventIndices.map { ViewController.allEvents[$0] }
}
}
OK, that's how I'd do it, but if we want to keep it in the style you have, we can still do that. You'll still need allEvents
to be static
though.
class ViewController {
static let allEvents: [String] = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT - “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]
static func generateRandomIndex() -> [Int] {
var eventIndex1: Int = 0
var eventIndex2: Int = 0
var eventIndex3: Int = 0
var eventIndex4: Int = 0
while (eventIndex1 == eventIndex2) || (eventIndex2 == eventIndex3) || (eventIndex1 == eventIndex3) || (eventIndex1 == eventIndex4) || (eventIndex2 == eventIndex4) || (eventIndex3 == eventIndex4) {
eventIndex1 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
eventIndex2 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
eventIndex3 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
eventIndex4 = GKRandomSource.sharedRandom().nextInt(upperBound: allEvents.count)
}
return [eventIndex1, eventIndex2, eventIndex3, eventIndex4]
}
let index1: Int
let index2: Int
let index3: Int
let index4: Int
let event1: String
let event2: String
let event3: String
let event4: String
init() {
let eventIndexes = ViewController.generateRandomIndex()
index1 = eventIndexes[0]
index2 = eventIndexes[1]
index3 = eventIndexes[2]
index4 = eventIndexes[3]
event1 = ViewController.allEvents[index1]
event2 = ViewController.allEvents[index2]
event3 = ViewController.allEvents[index3]
event4 = ViewController.allEvents[index4]
}
}
If allEvents
doesn't need to be seen outside this file, you can make it simpler by hoisting it to file scope:
fileprivate let allEvents: [String] = ["SOUTHERN STATES PASS LAWS TO DISENFRANCISE BLACKS", "THEODORE ROOSEVELT ELECTED VICE PRESIDENT", "WRIGHT BROTHERS FIRST FLIGHT", "SAN FRANCISCO EARTHQUAKE", "FORD INTRODUCES MODEL T", "WORLD WAR I BEGINS IN EUROPE", "WORLD WAR I ENDS", "TR DIES – CARNEGIE DIES", "GERMAN MONEY HYPER INFLATES", "TREATY OF VERSAILLES, 19TH AMENDMENT", "HINDENBURG ELECTED PRESIDENT OF GERMANY", "STOCK MARKET CRASHES, DEPRESSION BEGINS", "HITLER’S NAZI PARTY GAINS MAJORITY IN PARLIAMENT", "HITLER BEGINS TO TAKE POWER IN GERMANY", "FRANKLIN ROOSEVELT ELECTED PRESIDENT", "FDR INAUGURATED “100 DAYS”", "HINDENBURG DIES, HITLER APPOINTS HIMSELF PRESIDENT - “ FUEHRER”", "FDR RE-ELECTED", "German airship HINDENBURG BURNS IN NEW JERSEY", "“MUNICH” – British P.M. Chamberlain agrees that Hitler can have Czechoslovakia.", "Germany invades Holland, Belgium and France – Churchill becomes P.M.", "Germany invades Russia", "JAPANESE ATTACK PEARL HARBOR – U.S. DECLARES WAR ON JAPAN", "HITLER DECLARES WAR ON U.S."]
class ViewController {
...
event1 = allEvents[index1]
event2 = allEvents[index2]
event3 = allEvents[index3]
event4 = allEvents[index4]
...
But the key lesson in all this is that you can't access properties in an object before the object is initialized.