More needed refactoring steps

This commit is contained in:
Raimund Wege committed 2016-07-05 15:09:18 +02:00
1 parent 00509c120b
commit 0bc7e2b115
28 files changed
+635 -432

No files matched your search

@@ -295,7 +295,7 @@
7F8B8D411A373B0200C34B88 /* SpeedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F8B8D401A373B0200C34B88 /* SpeedViewController.swift */; };
7F8B8D431A373D4400C34B88 /* CourseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F8B8D421A373D4400C34B88 /* CourseViewController.swift */; };
7F8CC2081A10D47A00C56D7F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F8CC2071A10D47A00C56D7F /* SettingsViewController.swift */; };
7F950A911A35E967003C8C5C /* LeaderBoardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F950A901A35E967003C8C5C /* LeaderBoardViewController.swift */; };
7F950A911A35E967003C8C5C /* LeaderboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F950A901A35E967003C8C5C /* LeaderboardViewController.swift */; };
7F99DE3C1A126554009B3619 /* RegattaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F99DE3B1A126554009B3619 /* RegattaViewController.swift */; };
7FA2220B1A25E55400FD364B /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F83F17519F1574700C53328 /* CoreLocation.framework */; };
7FA6551A19F6661900A1B92D /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA6551919F6661900A1B92D /* CoreDataManager.swift */; };
@@ -312,16 +312,16 @@
C852BAAB1CF736680093B0C5 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C852BAAA1CF736680093B0C5 /* Preferences.swift */; };
C8603D511C52892A002B2F28 /* Acknowledgements.markdown in Resources */ = {isa = PBXBuildFile; fileRef = C8603D491C528832002B2F28 /* Acknowledgements.markdown */; };
C8739D8E1D23D53C00A89F3C /* SendGPSFixesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739D8D1D23D53C00A89F3C /* SendGPSFixesController.swift */; };
C8739DAF1D2539A900A89F3C /* CheckIn+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DA51D2539A900A89F3C /* CheckIn+CoreDataProperties.swift */; };
C8739DB01D2539A900A89F3C /* CheckIn.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DA61D2539A900A89F3C /* CheckIn.swift */; };
C8739DB31D2539A900A89F3C /* Event+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DA91D2539A900A89F3C /* Event+CoreDataProperties.swift */; };
C8739DB41D2539A900A89F3C /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DAA1D2539A900A89F3C /* Event.swift */; };
C8739DB51D2539A900A89F3C /* Competitor+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DAB1D2539A900A89F3C /* Competitor+CoreDataProperties.swift */; };
C8739DB61D2539A900A89F3C /* Competitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DAC1D2539A900A89F3C /* Competitor.swift */; };
C8739DB71D2539A900A89F3C /* GPSFix+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DAD1D2539A900A89F3C /* GPSFix+CoreDataProperties.swift */; };
C8739DB81D2539A900A89F3C /* GPSFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DAE1D2539A900A89F3C /* GPSFix.swift */; };
C8739DBB1D25409700A89F3C /* Leaderboard+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DB91D25409700A89F3C /* Leaderboard+CoreDataProperties.swift */; };
C8739DBC1D25409700A89F3C /* Leaderboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DBA1D25409700A89F3C /* Leaderboard.swift */; };
C8739E041D2AA2A500A89F3C /* Regatta+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DFA1D2AA2A500A89F3C /* Regatta+CoreDataProperties.swift */; };
C8739E051D2AA2A500A89F3C /* Regatta.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DFB1D2AA2A500A89F3C /* Regatta.swift */; };
C8739E061D2AA2A500A89F3C /* GPSFix+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DFC1D2AA2A500A89F3C /* GPSFix+CoreDataProperties.swift */; };
C8739E071D2AA2A500A89F3C /* GPSFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DFD1D2AA2A500A89F3C /* GPSFix.swift */; };
C8739E081D2AA2A500A89F3C /* Leaderboard+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DFE1D2AA2A500A89F3C /* Leaderboard+CoreDataProperties.swift */; };
C8739E091D2AA2A500A89F3C /* Leaderboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739DFF1D2AA2A500A89F3C /* Leaderboard.swift */; };
C8739E0A1D2AA2A500A89F3C /* Competitor+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739E001D2AA2A500A89F3C /* Competitor+CoreDataProperties.swift */; };
C8739E0B1D2AA2A500A89F3C /* Competitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739E011D2AA2A500A89F3C /* Competitor.swift */; };
C8739E0C1D2AA2A500A89F3C /* Event+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739E021D2AA2A500A89F3C /* Event+CoreDataProperties.swift */; };
C8739E0D1D2AA2A500A89F3C /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8739E031D2AA2A500A89F3C /* Event.swift */; };
C89C5FC51D06A289002AAAD2 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89C5FC41D06A289002AAAD2 /* GradientView.swift */; };
C89C5FCA1D06CECD002AAAD2 /* StatusTrackingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89C5FC91D06CECD002AAAD2 /* StatusTrackingTableViewCell.swift */; };
C89C5FCC1D06CF0C002AAAD2 /* GPSAccuracyTrackingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89C5FCB1D06CF0C002AAAD2 /* GPSAccuracyTrackingTableViewCell.swift */; };
@@ -683,7 +683,7 @@
7F8B8D401A373B0200C34B88 /* SpeedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpeedViewController.swift; sourceTree = "<group>"; };
7F8B8D421A373D4400C34B88 /* CourseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseViewController.swift; sourceTree = "<group>"; };
7F8CC2071A10D47A00C56D7F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
7F950A901A35E967003C8C5C /* LeaderBoardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeaderBoardViewController.swift; sourceTree = "<group>"; };
7F950A901A35E967003C8C5C /* LeaderboardViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeaderboardViewController.swift; sourceTree = "<group>"; };
7F99DE3B1A126554009B3619 /* RegattaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegattaViewController.swift; sourceTree = "<group>"; };
7FA6551919F6661900A1B92D /* CoreDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = CoreDataManager.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
7FAFA7941A126B5700D230F1 /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
@@ -702,16 +702,16 @@
C8739D891D21276B00A89F3C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Main.strings; sourceTree = "<group>"; };
C8739D8D1D23D53C00A89F3C /* SendGPSFixesController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendGPSFixesController.swift; sourceTree = "<group>"; };
C8739D9A1D25317100A89F3C /* CoreData 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "CoreData 2.xcdatamodel"; sourceTree = "<group>"; };
C8739DA51D2539A900A89F3C /* CheckIn+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CheckIn+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DA61D2539A900A89F3C /* CheckIn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckIn.swift; sourceTree = "<group>"; };
C8739DA91D2539A900A89F3C /* Event+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Event+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DAA1D2539A900A89F3C /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
C8739DAB1D2539A900A89F3C /* Competitor+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Competitor+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DAC1D2539A900A89F3C /* Competitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Competitor.swift; sourceTree = "<group>"; };
C8739DAD1D2539A900A89F3C /* GPSFix+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GPSFix+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DAE1D2539A900A89F3C /* GPSFix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GPSFix.swift; sourceTree = "<group>"; };
C8739DB91D25409700A89F3C /* Leaderboard+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Leaderboard+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DBA1D25409700A89F3C /* Leaderboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Leaderboard.swift; sourceTree = "<group>"; };
C8739DFA1D2AA2A500A89F3C /* Regatta+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Regatta+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DFB1D2AA2A500A89F3C /* Regatta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regatta.swift; sourceTree = "<group>"; };
C8739DFC1D2AA2A500A89F3C /* GPSFix+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GPSFix+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DFD1D2AA2A500A89F3C /* GPSFix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GPSFix.swift; sourceTree = "<group>"; };
C8739DFE1D2AA2A500A89F3C /* Leaderboard+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Leaderboard+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739DFF1D2AA2A500A89F3C /* Leaderboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Leaderboard.swift; sourceTree = "<group>"; };
C8739E001D2AA2A500A89F3C /* Competitor+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Competitor+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739E011D2AA2A500A89F3C /* Competitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Competitor.swift; sourceTree = "<group>"; };
C8739E021D2AA2A500A89F3C /* Event+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Event+CoreDataProperties.swift"; sourceTree = "<group>"; };
C8739E031D2AA2A500A89F3C /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
C89C5FC41D06A289002AAAD2 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; };
C89C5FC91D06CECD002AAAD2 /* StatusTrackingTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusTrackingTableViewCell.swift; sourceTree = "<group>"; };
C89C5FCB1D06CF0C002AAAD2 /* GPSAccuracyTrackingTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GPSAccuracyTrackingTableViewCell.swift; sourceTree = "<group>"; };
@@ -763,16 +763,16 @@
children = (
7FA6551919F6661900A1B92D /* CoreDataManager.swift */,
7F44095919F65B5B0021BF22 /* CoreData.xcdatamodeld */,
C8739DA51D2539A900A89F3C /* CheckIn+CoreDataProperties.swift */,
C8739DA61D2539A900A89F3C /* CheckIn.swift */,
C8739DA91D2539A900A89F3C /* Event+CoreDataProperties.swift */,
C8739DAA1D2539A900A89F3C /* Event.swift */,
C8739DAB1D2539A900A89F3C /* Competitor+CoreDataProperties.swift */,
C8739DAC1D2539A900A89F3C /* Competitor.swift */,
C8739DAD1D2539A900A89F3C /* GPSFix+CoreDataProperties.swift */,
C8739DAE1D2539A900A89F3C /* GPSFix.swift */,
C8739DB91D25409700A89F3C /* Leaderboard+CoreDataProperties.swift */,
C8739DBA1D25409700A89F3C /* Leaderboard.swift */,
C8739DFA1D2AA2A500A89F3C /* Regatta+CoreDataProperties.swift */,
C8739DFB1D2AA2A500A89F3C /* Regatta.swift */,
C8739DFC1D2AA2A500A89F3C /* GPSFix+CoreDataProperties.swift */,
C8739DFD1D2AA2A500A89F3C /* GPSFix.swift */,
C8739DFE1D2AA2A500A89F3C /* Leaderboard+CoreDataProperties.swift */,
C8739DFF1D2AA2A500A89F3C /* Leaderboard.swift */,
C8739E001D2AA2A500A89F3C /* Competitor+CoreDataProperties.swift */,
C8739E011D2AA2A500A89F3C /* Competitor.swift */,
C8739E021D2AA2A500A89F3C /* Event+CoreDataProperties.swift */,
C8739E031D2AA2A500A89F3C /* Event.swift */,
);
name = CoreData;
sourceTree = "<group>";
@@ -1267,7 +1267,7 @@
isa = PBXGroup;
children = (
7F99DE3B1A126554009B3619 /* RegattaViewController.swift */,
7F950A901A35E967003C8C5C /* LeaderBoardViewController.swift */,
7F950A901A35E967003C8C5C /* LeaderboardViewController.swift */,
C89C5FC61D06CE62002AAAD2 /* Tracking */,
);
name = Regatta;
@@ -1845,16 +1845,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C8739DB61D2539A900A89F3C /* Competitor.swift in Sources */,
7F1875B31A10F1B800C25BE0 /* TrackingViewController.swift in Sources */,
C8739E081D2AA2A500A89F3C /* Leaderboard+CoreDataProperties.swift in Sources */,
7F83F15819F154F000C53328 /* HomeViewController.swift in Sources */,
C8739DAF1D2539A900A89F3C /* CheckIn+CoreDataProperties.swift in Sources */,
7F8B8D431A373D4400C34B88 /* CourseViewController.swift in Sources */,
C8739DB81D2539A900A89F3C /* GPSFix.swift in Sources */,
7FAFA7951A126B5700D230F1 /* AboutViewController.swift in Sources */,
C8739E0A1D2AA2A500A89F3C /* Competitor+CoreDataProperties.swift in Sources */,
C8739E0C1D2AA2A500A89F3C /* Event+CoreDataProperties.swift in Sources */,
C8739E071D2AA2A500A89F3C /* GPSFix.swift in Sources */,
7F44095B19F65B5B0021BF22 /* CoreData.xcdatamodeld in Sources */,
C89C5FD01D06CF46002AAAD2 /* ModeTrackingTableViewCell.swift in Sources */,
C8739DB71D2539A900A89F3C /* GPSFix+CoreDataProperties.swift in Sources */,
7F1B66F91A0CDCF600904A41 /* BatteryManager.swift in Sources */,
7FA6551A19F6661900A1B92D /* CoreDataManager.swift in Sources */,
C852BAA21CF5E1EA0093B0C5 /* RequestManager.swift in Sources */,
@@ -1863,26 +1863,26 @@
7F23F0881A1A3DEF00F03F1C /* CheckInController.swift in Sources */,
7F2C605A19F160E400C32D93 /* LocationManager.swift in Sources */,
C89C5FCC1D06CF0C002AAAD2 /* GPSAccuracyTrackingTableViewCell.swift in Sources */,
C8739E041D2AA2A500A89F3C /* Regatta+CoreDataProperties.swift in Sources */,
C89C5FCA1D06CECD002AAAD2 /* StatusTrackingTableViewCell.swift in Sources */,
C8739E051D2AA2A500A89F3C /* Regatta.swift in Sources */,
7F8B8D3F1A37356C00C34B88 /* TrackingPageViewController.swift in Sources */,
7F99DE3C1A126554009B3619 /* RegattaViewController.swift in Sources */,
7F8CC2081A10D47A00C56D7F /* SettingsViewController.swift in Sources */,
C8739E0D1D2AA2A500A89F3C /* Event.swift in Sources */,
7F8B8D411A373B0200C34B88 /* SpeedViewController.swift in Sources */,
C8739DB51D2539A900A89F3C /* Competitor+CoreDataProperties.swift in Sources */,
C8739DBB1D25409700A89F3C /* Leaderboard+CoreDataProperties.swift in Sources */,
9E95C3D61C723AB30016632B /* Constants.swift in Sources */,
C8739DB41D2539A900A89F3C /* Event.swift in Sources */,
7FD9D3031A1236080058FEAD /* Extensions.swift in Sources */,
C8739E091D2AA2A500A89F3C /* Leaderboard.swift in Sources */,
C89C5FC51D06A289002AAAD2 /* GradientView.swift in Sources */,
C8739DBC1D25409700A89F3C /* Leaderboard.swift in Sources */,
C8739DB01D2539A900A89F3C /* CheckIn.swift in Sources */,
C852BAAB1CF736680093B0C5 /* Preferences.swift in Sources */,
C8739DB31D2539A900A89F3C /* Event+CoreDataProperties.swift in Sources */,
7F0658FD19F7D6700077FF95 /* ScanViewController.swift in Sources */,
C8739E061D2AA2A500A89F3C /* GPSFix+CoreDataProperties.swift in Sources */,
7F193A8F1A39ACF500D71773 /* TimerViewController.swift in Sources */,
7F83F15619F154F000C53328 /* AppDelegate.swift in Sources */,
7F950A911A35E967003C8C5C /* LeaderBoardViewController.swift in Sources */,
7F950A911A35E967003C8C5C /* LeaderboardViewController.swift in Sources */,
C89C5FCE1D06CF26002AAAD2 /* CachedFixesTrackingTableViewCell.swift in Sources */,
C8739E0B1D2AA2A500A89F3C /* Competitor.swift in Sources */,
7F0658FF19F7EE910077FF95 /* CheckInData.swift in Sources */,
7F193A911A39E54600D71773 /* GPSAccuracyView.swift in Sources */,
);
@@ -36,7 +36,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
Preferences.lastCheckInURLString = urlStringForDeeplink(url)
Preferences.newCheckInURL = urlStringForDeeplink(url)
return true
}
@@ -68,11 +68,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private func setupNavigationBarApperance() {
let tintColor = UIColor(hex: 0x009de0)
let font = UIFont(name: "OpenSans-Bold", size: CGFloat(17.0)) ?? UIFont.systemFontOfSize(17.0)
UINavigationBar.appearance().tintColor = tintColor
UINavigationBar.appearance().titleTextAttributes =
[NSForegroundColorAttributeName: tintColor,
NSFontAttributeName: UIFont(name: "OpenSans-Bold", size: CGFloat(17.0))!
]
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: tintColor, NSFontAttributeName: font]
}
private func setupPageControlApperance() {
@@ -89,6 +87,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// FIXME: - Two prefixes are not allowed
// Deeplink needs another structure (if possible) because of allowed characters in URL
// https:// -> http//
urlString = urlString.hasPrefix(appPrefix) ? urlString.substringFromIndex(appPrefix.endIndex) : urlString
let httpPrefix : String = "http//"
urlString = urlString.hasPrefix(httpPrefix) ? "http://" + urlString.substringFromIndex(httpPrefix.endIndex) : urlString
@@ -190,6 +190,9 @@
<accessibility key="accessibilityConfiguration" label="You can find your QR code on your registration confirmation"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="14"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="200" width="260" height="16.5"/>
</variation>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -384,6 +387,9 @@
<rect key="frame" x="526" y="8" width="54" height="22"/>
<fontDescription key="fontDescription" name="OpenSans-Bold" family="Open Sans" pointSize="16"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="224.5" y="9.5" width="55.5" height="19"/>
</variation>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="DE.png" translatesAutoresizingMaskIntoConstraints="NO" id="szG-Mg-huO">
<rect key="frame" x="494" y="7" width="24" height="24"/>
@@ -391,11 +397,17 @@
<constraint firstAttribute="width" constant="24" id="Ina-uK-atY"/>
<constraint firstAttribute="height" constant="24" id="TVX-WF-dOi"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="192.5" y="7" width="24" height="24"/>
</variation>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Max Mustermann" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="oJS-dM-2B6" userLabel="Name Label">
<rect key="frame" x="20" y="8" width="142" height="22"/>
<fontDescription key="fontDescription" name="OpenSans-Bold" family="Open Sans" pointSize="16"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="9.5" width="125.5" height="19"/>
</variation>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.59999999999999998" colorSpace="calibratedRGB"/>
@@ -468,6 +480,9 @@
<color key="value" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="50" y="133.5" width="200" height="33"/>
</variation>
<connections>
<action selector="teamImageAddButtonTapped:" destination="G3a-SV-Maf" eventType="touchUpInside" id="cs9-h3-kj2"/>
</connections>
@@ -533,12 +548,18 @@
<rect key="frame" x="8" y="0.0" width="59" height="29"/>
<fontDescription key="fontDescription" name="OpenSans-Semibold" family="Open Sans" pointSize="21"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="8" y="0.0" width="55.5" height="24.5"/>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Days" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="l8F-Sg-cUb" userLabel="Days Description Label">
<rect key="frame" x="8" y="28" width="59" height="22"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="16"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="8" y="31" width="55.5" height="19"/>
</variation>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="joW-cG-ai8" userLabel="Separator View">
<rect key="frame" x="75" y="10" width="1" height="30"/>
@@ -547,6 +568,9 @@
<constraint firstAttribute="height" constant="30" id="Jj2-tX-SU9"/>
<constraint firstAttribute="width" constant="1" id="sJJ-vK-Ocb"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="71.5" y="10" width="1" height="30"/>
</variation>
</view>
</subviews>
<constraints>
@@ -559,6 +583,9 @@
<constraint firstItem="sGD-0U-P4b" firstAttribute="top" secondItem="pjH-rV-MGI" secondAttribute="top" id="row-df-nQb"/>
<constraint firstAttribute="bottom" secondItem="l8F-Sg-cUb" secondAttribute="bottom" id="zUW-Uk-XQr"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="0.0" y="0.0" width="73" height="50"/>
</variation>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5is-0I-Kv3" userLabel="Hours View">
<rect key="frame" x="76" y="0.0" width="76.5" height="50"/>
@@ -567,12 +594,18 @@
<rect key="frame" x="8" y="0.0" width="60.5" height="29"/>
<fontDescription key="fontDescription" name="OpenSans-Semibold" family="Open Sans" pointSize="21"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="8" y="0.0" width="57" height="24.5"/>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hours" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c2J-Uj-KEg" userLabel="Hours Description Label">
<rect key="frame" x="8" y="28" width="60.5" height="22"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="16"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="8" y="31" width="57" height="19"/>
</variation>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JBv-xm-KUq" userLabel="Separator View">
<rect key="frame" x="75" y="10" width="1" height="30"/>
@@ -581,6 +614,9 @@
<constraint firstAttribute="width" constant="1" id="Gck-dw-et7"/>
<constraint firstAttribute="height" constant="30" id="gVK-VO-Bq4"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="71.5" y="10" width="1" height="30"/>
</variation>
</view>
</subviews>
<constraints>
@@ -593,6 +629,9 @@
<constraint firstItem="gaG-Iv-Xyq" firstAttribute="top" secondItem="5is-0I-Kv3" secondAttribute="top" id="ryA-23-kxC"/>
<constraint firstAttribute="trailing" secondItem="JBv-xm-KUq" secondAttribute="trailing" id="z4K-rX-taw"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="72.5" y="0.0" width="73" height="50"/>
</variation>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tvc-eS-Us6" userLabel="Minutes View">
<rect key="frame" x="152" y="0.0" width="76.5" height="50"/>
@@ -601,12 +640,18 @@
<rect key="frame" x="8" y="0.0" width="60.5" height="29"/>
<fontDescription key="fontDescription" name="OpenSans-Semibold" family="Open Sans" pointSize="21"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="8" y="0.0" width="57" height="24.5"/>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Minutes" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EES-a9-qxv" userLabel="Minutes Description Label">
<rect key="frame" x="8" y="28" width="60.5" height="22"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="16"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="8" y="31" width="57" height="19"/>
</variation>
</label>
</subviews>
<constraints>
@@ -617,6 +662,9 @@
<constraint firstAttribute="bottom" secondItem="EES-a9-qxv" secondAttribute="bottom" id="jdu-Od-J49"/>
<constraint firstItem="Txm-uf-MP4" firstAttribute="top" secondItem="tvc-eS-Us6" secondAttribute="top" id="vpc-a8-Iuw"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="145" y="0.0" width="73" height="50"/>
</variation>
</view>
</subviews>
<constraints>
@@ -642,6 +690,9 @@
<constraint firstAttribute="bottom" secondItem="5is-0I-Kv3" secondAttribute="bottom" id="ngY-tF-5EI"/>
<constraint firstItem="tvc-eS-Us6" firstAttribute="top" secondItem="6rV-hv-c3D" secondAttribute="top" id="wUO-8b-mHS"/>
</constraints>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="41.5" y="38" width="218.5" height="50"/>
</variation>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Lzv-E6-gsr">
<rect key="frame" x="20" y="96" width="276" height="48"/>
@@ -948,6 +999,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="16" width="49" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="35.5" width="49" height="20"/>
</variation>
@@ -956,6 +1010,9 @@
<rect key="frame" x="444" y="14" width="136" height="24"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="448" y="16" width="132" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="648" y="35.5" width="132" height="20"/>
</variation>
@@ -985,6 +1042,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="16" width="44" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="96" width="44" height="20"/>
</variation>
@@ -993,6 +1053,9 @@
<rect key="frame" x="527.5" y="14" width="52.5" height="23.5"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="231" y="16" width="49" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="731" y="96" width="49" height="20"/>
</variation>
@@ -1022,6 +1085,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="16" width="104" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="64" width="104" height="24"/>
</variation>
@@ -1030,6 +1096,9 @@
<rect key="frame" x="570" y="14" width="10" height="24"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="270" y="16" width="10" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="770" y="64" width="10" height="24"/>
</variation>
@@ -1059,6 +1128,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="16" width="111" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="8" width="111" height="20"/>
</variation>
@@ -1068,6 +1140,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="501" y="16" width="59" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="705" y="8" width="59" height="20"/>
</variation>
@@ -1179,6 +1254,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="8" width="106" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="0.0" width="106" height="24"/>
</variation>
@@ -1188,6 +1266,9 @@
<fontDescription key="fontDescription" name="OpenSans-Light" family="Open Sans" pointSize="72"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="117" width="560" height="84"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="358" width="760" height="84"/>
</variation>
@@ -1228,12 +1309,18 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="8" width="49.5" height="20"/>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0.0 kn" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="l5u-i3-029">
<rect key="frame" x="203" y="210" width="195.5" height="98.5"/>
<fontDescription key="fontDescription" name="OpenSans-Light" family="Open Sans" pointSize="72"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="202" y="117" width="197.5" height="84"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="301.5" y="358" width="197.5" height="84"/>
</variation>
@@ -1271,6 +1358,9 @@
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="8" width="38.5" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="20" y="0.0" width="38.5" height="20"/>
</variation>
@@ -1280,6 +1370,9 @@
<fontDescription key="fontDescription" name="OpenSans-Light" family="Open Sans" pointSize="72"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="256" y="117" width="89" height="84"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="355.5" y="358" width="89" height="84"/>
</variation>
@@ -1532,12 +1625,18 @@ Cg
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="13"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="256" width="43" height="15.5"/>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1.0.0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jsi-LA-7jU">
<rect key="frame" x="20" y="202" width="30" height="18"/>
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="13"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="20" y="279.5" width="29" height="15.5"/>
</variation>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -1582,21 +1681,21 @@ Cg
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="HxP-XV-1as"/>
<exclude reference="Htc-F9-k7d"/>
<exclude reference="i64-7l-JjE"/>
<exclude reference="rI9-vl-bNT"/>
<exclude reference="HxP-XV-1as"/>
</mask>
</variation>
<variation key="heightClass=compact">
<mask key="constraints">
<exclude reference="YBh-Y6-NK3"/>
<include reference="HxP-XV-1as"/>
<exclude reference="08e-KJ-Dpa"/>
<exclude reference="Bht-kB-G6p"/>
<include reference="Htc-F9-k7d"/>
<include reference="i64-7l-JjE"/>
<include reference="rI9-vl-bNT"/>
<include reference="HxP-XV-1as"/>
</mask>
</variation>
</view>
@@ -1681,6 +1780,9 @@ Cg
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="14" y="12" width="111.5" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="15" y="10" width="111.5" height="24"/>
</variation>
@@ -1710,6 +1812,9 @@ Cg
<fontDescription key="fontDescription" name="OpenSans-Bold" family="Open Sans" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="14" y="2" width="127" height="20"/>
</variation>
<variation key="heightClass=regular-widthClass=regular" misplaced="YES">
<rect key="frame" x="15" y="10" width="111.5" height="24"/>
</variation>
@@ -1719,6 +1824,9 @@ Cg
<fontDescription key="fontDescription" name="OpenSans" family="Open Sans" pointSize="13"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="heightClass=compact" misplaced="YES">
<rect key="frame" x="14" y="26" width="32" height="15.5"/>
</variation>
</label>
</subviews>
<constraints>
@@ -1791,7 +1899,7 @@ Cg
<!--Leader Board-->
<scene sceneID="BcW-Cr-BGq">
<objects>
<viewController id="86Y-sl-Zhj" customClass="LeaderBoardViewController" customModule="SAPTracker" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="86Y-sl-Zhj" customClass="LeaderboardViewController" customModule="SAPTracker" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Dlt-1X-fN2"/>
<viewControllerLayoutGuide type="bottom" id="Ehu-33-1iX"/>
@@ -1825,7 +1933,7 @@ Cg
<navigationItem key="navigationItem" title="Leader Board" id="8qz-J9-1KL">
<barButtonItem key="rightBarButtonItem" systemItem="done" id="NSL-iA-JLn">
<connections>
<action selector="done:" destination="86Y-sl-Zhj" id="BdJ-Hv-zJb"/>
<action selector="doneButtonTapped:" destination="86Y-sl-Zhj" id="Xm9-iN-s5q"/>
</connections>
</barButtonItem>
</navigationItem>
@@ -1868,8 +1976,8 @@ Cg
<image name="settings" width="22" height="22"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="QMa-LQ-D8c"/>
<segue reference="HaH-Jc-FNR"/>
<segue reference="tzB-PL-31j"/>
<segue reference="Irs-ey-Uhs"/>
</inferredMetricsTieBreakers>
<color key="tintColor" red="0.0" green="0.61568627450980395" blue="0.8784313725490196" alpha="1" colorSpace="calibratedRGB"/>
</document>
@@ -10,7 +10,7 @@ import UIKit
class CachedFixesTrackingTableViewCell: UITableViewCell {
var checkIn: CheckIn?
var regatta: Regatta?
@IBOutlet weak var cachedFixesLabel: UILabel!
@@ -27,7 +27,7 @@ class CachedFixesTrackingTableViewCell: UITableViewCell {
// MARK: - Setups
private func setupCachedFixesLabel() {
self.cachedFixesLabel.text = String(format: "%d", checkIn?.gpsFixes?.count ?? 0)
self.cachedFixesLabel.text = String(format: "%d", regatta?.gpsFixes?.count ?? 0)
}
// MARK: - Notifications
@@ -1,17 +0,0 @@
//
// CheckIn.swift
// SAPTracker
//
// Created by Raimund Wege on 30.06.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
import Foundation
import CoreData
@objc(CheckIn)
class CheckIn: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
}
@@ -42,9 +42,10 @@ class CheckInController : NSObject {
static let Name = "name"
}
private let checkInData: CheckInData!
private let delegate: CheckInControllerDelegate!
private let requestManager: RequestManager!
var delegate: CheckInControllerDelegate?
private var checkInData = CheckInData()
private var requestManager = RequestManager()
// Responses
private var eventDictionary: [String: AnyObject]?
@@ -52,20 +53,28 @@ class CheckInController : NSObject {
private var competitorDictionary: [String: AnyObject]?
private var teamImageURL: String?
init(checkInData: CheckInData, delegate: CheckInControllerDelegate) {
// MARK: - Initialize
private func initialize(checkInData: CheckInData) {
self.checkInData = checkInData
self.delegate = delegate
setupRequestManager()
}
// MARK: - Setups
private func setupRequestManager() {
requestManager = RequestManager(baseURLString: checkInData.serverURL)
super.init()
}
// MARK: - Start check-in
func startCheckIn() {
func startCheckIn(checkInData: CheckInData) {
initialize(checkInData)
checkInDidStart()
requestManager.getEvent(checkInData.eventID,
success: { (operation, responseObject) -> Void in self.eventSucceed(responseObject) },
failure: { (operation, error) -> Void in self.eventFailed(error) })
failure: { (operation, error) -> Void in self.eventFailed(error) }
)
}
// MARK: - Event
@@ -74,7 +83,8 @@ class CheckInController : NSObject {
eventDictionary = eventResponseObject as? [String: AnyObject]
requestManager.getLeaderboard(checkInData.leaderboardName,
success: { (operation, responseObject) -> Void in self.leaderboardSucceed(responseObject) },
failure: { (operation, error) -> Void in self.leaderboardFailed(error) })
failure: { (operation, error) -> Void in self.leaderboardFailed(error) }
)
}
private func eventFailed(error: AnyObject) {
@@ -94,7 +104,8 @@ class CheckInController : NSObject {
leaderboardDictionary = leaderboardResponseObject as? [String: AnyObject]
requestManager.getCompetitor(checkInData.competitorID,
success: { (operation, responseObject) in self.competitorSucceed(responseObject) },
failure: { (operation, error) in self.competitorFailed(error) })
failure: { (operation, error) in self.competitorFailed(error) }
)
}
private func leaderboardFailed(error: AnyObject) {
@@ -112,7 +123,7 @@ class CheckInController : NSObject {
private func competitorSucceed(competitorResponseObject: AnyObject) {
competitorDictionary = competitorResponseObject as? [String: AnyObject]
requestManager.getTeamImageURL(competitorID(), result: { (imageURL) in self.teamImageURLResult(imageURL) })
requestManager.getTeamImageURL(competitorID, result: { (imageURL) in self.teamImageURLResult(imageURL) })
}
private func competitorFailed(error: AnyObject) {
@@ -131,9 +142,9 @@ class CheckInController : NSObject {
private func teamImageURLResult(imageURL: String?) {
teamImageURL = imageURL
let alertTitle = String(format:NSLocalizedString("Hello %@. Welcome to %@. You are registered as %@.", comment: ""),
competitorName(),
leaderboardName(),
competitorSailID())
competitorName,
leaderboardName,
competitorSailID)
let alertController = UIAlertController(title: alertTitle, message: nil, preferredStyle: .Alert)
let okTitle = NSLocalizedString("OK", comment: "")
let okAction = UIAlertAction(title: okTitle, style: .Default) { (action) in
@@ -145,76 +156,67 @@ class CheckInController : NSObject {
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.showCheckInAlert(alertController)
showCheckInAlert(alertController)
}
// MARK: - Check-in on server
private func checkInOnServer() {
SVProgressHUD.show()
requestManager.postCheckIn(leaderboardName(),
competitorID: competitorID(),
requestManager.postCheckIn(leaderboardName,
competitorID: competitorID,
success: { (operation, responseObject) -> Void in self.checkInOnServerSucceed() },
failure: { (operation, error) -> Void in self.checkInOnServerFailed(error) })
failure: { (operation, error) -> Void in self.checkInOnServerFailed(error) }
)
}
private func checkInOnServerSucceed() {
// Check database if check-in already exist
if let checkIn = CoreDataManager.sharedManager.fetchCheckIn(checkInData.eventID,
leaderboardName: checkInData.leaderboardName,
competitorID: checkInData.competitorID) {
// Delete old check-in
CoreDataManager.sharedManager.deleteCheckIn(checkIn)
CoreDataManager.sharedManager.saveContext()
}
// Regatta
let regatta = CoreDataManager.sharedManager.fetchRegatta(checkInData.eventID,
leaderboardName: checkInData.leaderboardName,
competitorID: checkInData.competitorID
) ?? CoreDataManager.sharedManager.newRegatta()
regatta.serverURL = checkInData.serverURL
regatta.teamImageURL = teamImageURL
regatta.teamImageRetry = false
// Create new check-in
let checkIn = CoreDataManager.sharedManager.newCheckIn()
checkIn.competitorID = checkInData!.competitorID
checkIn.eventID = checkInData.eventID
checkIn.lastSyncDate = NSDate().timeIntervalSince1970
checkIn.leaderboardName = checkInData!.leaderboardName
checkIn.serverURL = checkInData!.serverURL
checkIn.teamImageURL = teamImageURL
checkIn.teamImageRetry = false
// Event
let event = regatta.event ?? CoreDataManager.sharedManager.newEvent(regatta)
event.endDate = (eventEndDate / 1000)
event.eventID = eventID
event.name = eventName
event.startDate = (eventStartDate / 1000)
// Create new event
let event = CoreDataManager.sharedManager.newEvent(checkIn)
event.endDate = (eventEndDate() / 1000)
event.eventID = eventID()
event.name = eventName()
event.startDate = (eventStartDate() / 1000)
// Leaderboard
let leaderboard = regatta.leaderboard ?? CoreDataManager.sharedManager.newLeaderboard(regatta)
leaderboard.name = leaderboardName
// Create new leaderboard
let leaderboard = CoreDataManager.sharedManager.newLeaderBoard(checkIn)
leaderboard.name = leaderboardName()
// Competitor
let competitor = regatta.competitor ?? CoreDataManager.sharedManager.newCompetitor(regatta)
competitor.boatClassName = competitorBoatClassName
competitor.competitorID = competitorID
competitor.countryCode = competitorCountryCode
competitor.name = competitorName
competitor.nationality = competitorNationality
competitor.sailID = competitorSailID
// Create new competitor
let competitor = CoreDataManager.sharedManager.newCompetitor(checkIn)
competitor.boatClassName = competitorBoatClassName()
competitor.competitorID = competitorID()
competitor.countryCode = competitorCountryCode()
competitor.name = competitorName()
competitor.nationality = competitorNationality()
competitor.sailID = competitorSailID()
// Save objects
// Save
CoreDataManager.sharedManager.saveContext()
// Check-in completed
self.checkInDidEnd(withSuccess: true)
checkInDidEnd(withSuccess: true)
}
private func checkInOnServerFailed(error: AnyObject) {
let alertTitle = String(format:NSLocalizedString("Couldn't check-in to %@", comment: ""), checkInData!.leaderboardName)
let alertTitle = String(format:NSLocalizedString("Couldn't check-in to %@", comment: ""), checkInData.leaderboardName)
let alertController = UIAlertController(title: alertTitle, message: error.localizedDescription, preferredStyle: .Alert)
let cancelTitle = NSLocalizedString("Cancel", comment: "")
let cancelAction = UIAlertAction(title: cancelTitle, style: .Cancel) { (action) in
self.checkInDidEnd(withSuccess: false)
}
alertController.addAction(cancelAction)
self.showCheckInAlert(alertController)
showCheckInAlert(alertController)
}
// MARK: - Controller
@@ -222,51 +224,52 @@ class CheckInController : NSObject {
private func checkInDidStart() {
dispatch_async(dispatch_get_main_queue(), {
SVProgressHUD.show()
self.delegate.checkInDidStart?(self)
self.delegate?.checkInDidStart?(self)
})
}
private func checkInDidEnd(withSuccess succeed: Bool) {
dispatch_async(dispatch_get_main_queue(), {
SVProgressHUD.popActivity()
self.delegate.checkInDidEnd?(self, withSuccess: succeed)
self.delegate?.checkInDidEnd?(self, withSuccess: succeed)
})
}
private func showCheckInAlert(alertController: UIAlertController) {
dispatch_async(dispatch_get_main_queue(), {
SVProgressHUD.popActivity()
self.delegate.showCheckInAlert(self, alertController: alertController)
self.delegate?.showCheckInAlert(self, alertController: alertController)
})
}
// MARK: - Event
private func eventID() -> String! { return stringFromEvent(forKey: EventKeys.ID) }
private func eventName() -> String! { return stringFromEvent(forKey: EventKeys.Name) }
private func eventStartDate() -> Double! { return doubleFromEvent(forKey: EventKeys.StartDate) }
private func eventEndDate() -> Double! { return doubleFromEvent(forKey: EventKeys.EndDate) }
private func doubleFromEvent(forKey key: String) -> Double { return doubleFromDictionary(eventDictionary, forKey: key) }
private func stringFromEvent(forKey key: String) -> String { return stringFromDictionary(eventDictionary, forKey: key) }
var eventID: String { get { return stringFromEvent(forKey: EventKeys.ID) } }
var eventName: String { get { return stringFromEvent(forKey: EventKeys.Name) } }
var eventStartDate: Double { get { return doubleFromEvent(forKey: EventKeys.StartDate) } }
var eventEndDate: Double { get { return doubleFromEvent(forKey: EventKeys.EndDate) } }
// MARK: - Leaderboard
private func leaderboardName() -> String! { return stringFromLeaderboard(forKey: LeaderboardKeys.Name) }
private func stringFromLeaderboard(forKey key: String) -> String { return stringFromDictionary(leaderboardDictionary, forKey: key) }
var leaderboardName: String { get { return stringFromLeaderboard(forKey: LeaderboardKeys.Name) } }
// MARK: - Competitor
private func competitorBoatClassName() -> String! { return stringFromCompetitor(forKey: CompetitorKeys.BoatClassName) }
private func competitorCountryCode() -> String! { return stringFromCompetitor(forKey: CompetitorKeys.CountryCode) }
private func competitorID() -> String { return stringFromCompetitor(forKey: CompetitorKeys.ID) }
private func competitorName() -> String! { return stringFromCompetitor(forKey: CompetitorKeys.Name) }
private func competitorNationality() -> String! { return stringFromCompetitor(forKey: CompetitorKeys.Nationality) }
private func competitorSailID() -> String! { return stringFromCompetitor(forKey: CompetitorKeys.SailID) }
private func stringFromCompetitor(forKey key: String) -> String { return stringFromDictionary(competitorDictionary, forKey: key) }
var competitorBoatClassName: String { get { return stringFromCompetitor(forKey: CompetitorKeys.BoatClassName) } }
var competitorCountryCode: String { get { return stringFromCompetitor(forKey: CompetitorKeys.CountryCode) } }
var competitorID: String { get { return stringFromCompetitor(forKey: CompetitorKeys.ID) } }
var competitorName: String { get { return stringFromCompetitor(forKey: CompetitorKeys.Name) } }
var competitorNationality: String { get { return stringFromCompetitor(forKey: CompetitorKeys.Nationality) } }
var competitorSailID: String { get { return stringFromCompetitor(forKey: CompetitorKeys.SailID) } }
// MARK: - Helper
private func doubleFromDictionary(dictionary: [String: AnyObject]?, forKey key: String!) -> Double {
private func doubleFromEvent(forKey key: String) -> Double { return doubleFromDictionary(eventDictionary, forKey: key) }
private func stringFromEvent(forKey key: String) -> String { return stringFromDictionary(eventDictionary, forKey: key) }
private func stringFromLeaderboard(forKey key: String) -> String { return stringFromDictionary(leaderboardDictionary, forKey: key) }
private func stringFromCompetitor(forKey key: String) -> String { return stringFromDictionary(competitorDictionary, forKey: key) }
private func doubleFromDictionary(dictionary: [String: AnyObject]?, forKey key: String) -> Double {
if let value = dictionary?[key] as? Double {
return value
} else {
@@ -274,7 +277,7 @@ class CheckInController : NSObject {
}
}
private func stringFromDictionary(dictionary: [String: AnyObject]?, forKey key: String!) -> String {
private func stringFromDictionary(dictionary: [String: AnyObject]?, forKey key: String) -> String {
if let value = dictionary?[key] as? String {
return value
} else {
@@ -16,17 +16,16 @@ public class CheckInData: NSObject {
static let CompetitorID = "competitor_id"
}
public let serverURL: String!
public let eventID: String!
public let leaderboardName: String!
public let competitorID: String!
public let serverURL: String
public let eventID: String
public let leaderboardName: String
public let competitorID: String
init?(serverURL: String!,
eventID: String!,
leaderboardName: String!,
competitorID: String!)
init(serverURL: String = "",
eventID: String = "",
leaderboardName: String = "",
competitorID: String = "")
{
guard serverURL != nil && eventID != nil && leaderboardName != nil && competitorID != nil else { return nil }
self.serverURL = serverURL
self.eventID = eventID
self.leaderboardName = leaderboardName
@@ -34,19 +33,21 @@ public class CheckInData: NSObject {
super.init()
}
convenience init?(url: NSURL!) {
guard url != nil && url.host != nil && url.query != nil else { return nil }
convenience init?(url: NSURL) {
guard let host = url.host else { return nil }
guard let components = NSURLComponents(string: url.absoluteString) else { return nil }
// Set server URL
let serverURL = url.scheme + "://" + host + (url.port != nil ? ":" + url.port!.stringValue : "")
// In query component replace '+' occurrences with '%20' as a workaround for bug 3664
components.percentEncodedQuery = components.percentEncodedQuery?.stringByReplacingOccurrencesOfString("+", withString: "%20")
let queryItems = components.queryItems
// Set server URL and check-in items
let serverURL = url.scheme + "://" + url.host! + (url.port == nil ? "" : ":" + url.port!.stringValue)
let eventID = CheckInData.queryItemValue(queryItems, itemName: CheckInData.ItemNames.EventID)
let leaderboardName = CheckInData.queryItemValue(queryItems, itemName: CheckInData.ItemNames.LeaderboardName)
let competitorID = CheckInData.queryItemValue(queryItems, itemName: CheckInData.ItemNames.CompetitorID)
// Get check-in items
guard let eventID = CheckInData.queryItemValue(queryItems, itemName: CheckInData.ItemNames.EventID) else { return nil }
guard let leaderboardName = CheckInData.queryItemValue(queryItems, itemName: CheckInData.ItemNames.LeaderboardName) else { return nil }
guard let competitorID = CheckInData.queryItemValue(queryItems, itemName: CheckInData.ItemNames.CompetitorID) else { return nil }
// Init with values
self.init(serverURL: serverURL,
@@ -55,8 +56,10 @@ public class CheckInData: NSObject {
competitorID: competitorID)
}
convenience init?(urlString: String!) {
convenience init?(urlString: String) {
guard let url = NSURL(string: urlString) else { return nil }
// Init with URL
self.init(url: url)
}
@@ -2,7 +2,7 @@
// Competitor+CoreDataProperties.swift
// SAPTracker
//
// Created by Raimund Wege on 01.07.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
@@ -14,12 +14,12 @@ import CoreData
extension Competitor {
@NSManaged var boatClassName: String?
@NSManaged var competitorID: String?
@NSManaged var countryCode: String?
@NSManaged var name: String?
@NSManaged var nationality: String?
@NSManaged var sailID: String?
@NSManaged var checkIn: CheckIn?
@NSManaged var boatClassName: String
@NSManaged var competitorID: String
@NSManaged var countryCode: String
@NSManaged var name: String
@NSManaged var nationality: String
@NSManaged var sailID: String
@NSManaged var regatta: Regatta
}
@@ -2,7 +2,7 @@
// Competitor.swift
// SAPTracker
//
// Created by Raimund Wege on 30.06.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
@@ -1,34 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10171" systemVersion="15E65" minimumToolsVersion="Xcode 4.3">
<entity name="CheckIn" representedClassName="CheckIn" syncable="YES">
<attribute name="competitorID" attributeType="String" syncable="YES"/>
<attribute name="eventID" attributeType="String" syncable="YES"/>
<attribute name="lastSyncDate" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="leaderboardName" attributeType="String" syncable="YES"/>
<attribute name="serverURL" attributeType="String" syncable="YES"/>
<attribute name="teamImageData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="teamImageRetry" optional="YES" attributeType="Boolean" syncable="YES"/>
<attribute name="teamImageURL" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="competitor" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Competitor" inverseName="checkIn" inverseEntity="Competitor" syncable="YES"/>
<relationship name="event" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Event" inverseName="checkIn" inverseEntity="Event" syncable="YES"/>
<relationship name="gpsFixes" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="GPSFix" inverseName="checkIn" inverseEntity="GPSFix" syncable="YES"/>
<relationship name="leaderboard" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Leaderboard" inverseName="checkIn" inverseEntity="Leaderboard" syncable="YES"/>
</entity>
<entity name="Competitor" representedClassName="Competitor" syncable="YES">
<attribute name="boatClassName" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="boatClassName" attributeType="String" syncable="YES"/>
<attribute name="competitorID" attributeType="String" indexed="YES" syncable="YES"/>
<attribute name="countryCode" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="nationality" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="sailID" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="checkIn" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CheckIn" inverseName="competitor" inverseEntity="CheckIn" syncable="YES"/>
<attribute name="countryCode" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="nationality" attributeType="String" syncable="YES"/>
<attribute name="sailID" attributeType="String" syncable="YES"/>
<relationship name="regatta" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Regatta" inverseName="competitor" inverseEntity="Regatta" syncable="YES"/>
</entity>
<entity name="Event" representedClassName="Event" syncable="YES">
<attribute name="endDate" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="endDate" attributeType="Date" syncable="YES"/>
<attribute name="eventID" attributeType="String" indexed="YES" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="startDate" optional="YES" attributeType="Date" syncable="YES"/>
<relationship name="checkIn" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CheckIn" inverseName="event" inverseEntity="CheckIn" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="startDate" attributeType="Date" syncable="YES"/>
<relationship name="regatta" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Regatta" inverseName="event" inverseEntity="Regatta" syncable="YES"/>
</entity>
<entity name="GPSFix" representedClassName="GPSFix" syncable="YES">
<attribute name="course" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
@@ -36,17 +22,27 @@
<attribute name="longitude" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
<attribute name="speed" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
<attribute name="timestamp" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
<relationship name="checkIn" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CheckIn" inverseName="gpsFixes" inverseEntity="CheckIn" syncable="YES"/>
<relationship name="regatta" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Regatta" inverseName="gpsFixes" inverseEntity="Regatta" syncable="YES"/>
</entity>
<entity name="Leaderboard" representedClassName="Leaderboard" syncable="YES">
<attribute name="name" attributeType="String" indexed="YES" syncable="YES"/>
<relationship name="checkIn" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="CheckIn" inverseName="leaderboard" inverseEntity="CheckIn" syncable="YES"/>
<relationship name="regatta" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Regatta" inverseName="leaderboard" inverseEntity="Regatta" syncable="YES"/>
</entity>
<entity name="Regatta" representedClassName="Regatta" syncable="YES">
<attribute name="serverURL" attributeType="String" syncable="YES"/>
<attribute name="teamImageData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="teamImageRetry" attributeType="Boolean" syncable="YES"/>
<attribute name="teamImageURL" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="competitor" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Competitor" inverseName="regatta" inverseEntity="Competitor" syncable="YES"/>
<relationship name="event" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Event" inverseName="regatta" inverseEntity="Event" syncable="YES"/>
<relationship name="gpsFixes" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="GPSFix" inverseName="regatta" inverseEntity="GPSFix" syncable="YES"/>
<relationship name="leaderboard" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Leaderboard" inverseName="regatta" inverseEntity="Leaderboard" syncable="YES"/>
</entity>
<elements>
<element name="CheckIn" positionX="-36" positionY="90" width="128" height="225"/>
<element name="Regatta" positionX="-36" positionY="90" width="128" height="165"/>
<element name="Competitor" positionX="-9" positionY="81" width="128" height="150"/>
<element name="Event" positionX="-63" positionY="27" width="128" height="120"/>
<element name="GPSFix" positionX="-63" positionY="-18" width="128" height="133"/>
<element name="GPSFix" positionX="-63" positionY="-18" width="128" height="135"/>
<element name="Leaderboard" positionX="-36" positionY="72" width="128" height="75"/>
</elements>
</model>
@@ -20,13 +20,13 @@ public class CoreDataManager: NSObject {
// MARK: - Fetch
func fetchCheckIn(eventID: String,
func fetchRegatta(eventID: String,
leaderboardName: String,
competitorID: String) -> CheckIn?
competitorID: String) -> Regatta?
{
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("CheckIn", inManagedObjectContext: managedObjectContext!)
fetchRequest.predicate = NSPredicate(format: "eventID = %@ AND leaderboardName = %@ AND competitorID = %@",
fetchRequest.entity = NSEntityDescription.entityForName("Regatta", inManagedObjectContext: managedObjectContext!)
fetchRequest.predicate = NSPredicate(format: "event.eventID = %@ AND leaderboard.name = %@ AND competitor.competitorID = %@",
eventID,
leaderboardName,
competitorID)
@@ -35,7 +35,7 @@ public class CoreDataManager: NSObject {
if results.count == 0 {
return nil
} else {
return results[0] as? CheckIn
return results[0] as? Regatta
}
} catch {
print(error)
@@ -43,46 +43,46 @@ public class CoreDataManager: NSObject {
return nil
}
func checkInFetchedResultsController() -> NSFetchedResultsController {
let fetchRequest = NSFetchRequest(entityName: "CheckIn")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "leaderboardName", ascending: true)]
func regattaFetchedResultsController() -> NSFetchedResultsController {
let fetchRequest = NSFetchRequest(entityName: "Regatta")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "leaderboard.name", ascending: true)]
return NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext!, sectionNameKeyPath: nil, cacheName: nil)
}
// MARK: - Insert
func newCheckIn() -> CheckIn {
return NSEntityDescription.insertNewObjectForEntityForName("CheckIn", inManagedObjectContext: managedObjectContext!) as! CheckIn
func newRegatta() -> Regatta {
return NSEntityDescription.insertNewObjectForEntityForName("Regatta", inManagedObjectContext: managedObjectContext!) as! Regatta
}
func newEvent(checkIn: CheckIn) -> Event {
func newEvent(regatta: Regatta) -> Event {
let event = NSEntityDescription.insertNewObjectForEntityForName("Event", inManagedObjectContext: managedObjectContext!) as! Event
event.checkIn = checkIn
event.regatta = regatta
return event
}
func newLeaderBoard(checkIn: CheckIn) -> Leaderboard {
let leaderBoard = NSEntityDescription.insertNewObjectForEntityForName("Leaderboard", inManagedObjectContext: managedObjectContext!) as! Leaderboard
leaderBoard.checkIn = checkIn
return leaderBoard
func newLeaderboard(regatta: Regatta) -> Leaderboard {
let leaderboard = NSEntityDescription.insertNewObjectForEntityForName("Leaderboard", inManagedObjectContext: managedObjectContext!) as! Leaderboard
leaderboard.regatta = regatta
return leaderboard
}
func newCompetitor(checkIn: CheckIn) -> Competitor {
func newCompetitor(regatta: Regatta) -> Competitor {
let competitor = NSEntityDescription.insertNewObjectForEntityForName("Competitor", inManagedObjectContext: managedObjectContext!) as! Competitor
competitor.checkIn = checkIn
competitor.regatta = regatta
return competitor
}
func newGPSFix(checkIn: CheckIn) -> GPSFix {
func newGPSFix(regatta: Regatta) -> GPSFix {
let gpsFix = NSEntityDescription.insertNewObjectForEntityForName("GPSFix", inManagedObjectContext: managedObjectContext!) as! GPSFix
gpsFix.checkIn = checkIn
gpsFix.regatta = regatta
return gpsFix
}
// MARK: - Delete
func deleteCheckIn(checkIn: CheckIn) {
managedObjectContext!.deleteObject(checkIn)
func deleteRegatta(regatta: Regatta) {
managedObjectContext!.deleteObject(regatta)
}
func deleteGPSFixes(gpsFixes: [GPSFix]) {
@@ -2,7 +2,7 @@
// Event+CoreDataProperties.swift
// SAPTracker
//
// Created by Raimund Wege on 01.07.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
@@ -15,9 +15,9 @@ import CoreData
extension Event {
@NSManaged var endDate: NSTimeInterval
@NSManaged var eventID: String?
@NSManaged var name: String?
@NSManaged var eventID: String
@NSManaged var name: String
@NSManaged var startDate: NSTimeInterval
@NSManaged var checkIn: CheckIn?
@NSManaged var regatta: Regatta
}
@@ -2,7 +2,7 @@
// Event.swift
// SAPTracker
//
// Created by Raimund Wege on 30.06.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
@@ -2,7 +2,7 @@
// GPSFix+CoreDataProperties.swift
// SAPTracker
//
// Created by Raimund Wege on 01.07.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
@@ -19,6 +19,6 @@ extension GPSFix {
@NSManaged var longitude: Double
@NSManaged var speed: Double
@NSManaged var timestamp: Double
@NSManaged var checkIn: CheckIn?
@NSManaged var regatta: Regatta
}
@@ -2,7 +2,7 @@
// GPSFix.swift
// SAPTracker
//
// Created by Raimund Wege on 30.06.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
@@ -17,24 +17,31 @@ class HomeViewController: UIViewController {
@IBOutlet weak var noCodeButton: UIButton!
@IBOutlet weak var infoCodeLabel: UILabel!
private var fetchedResultsController: NSFetchedResultsController?
override func viewDidLoad() {
super.viewDidLoad()
setups()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
reviews()
subscribeForNewCheckInURLNotifications()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
unsubscribeFromNewCheckInURLNotifications()
}
// MARK: - Setups
private func setups() {
setupButtons()
setupLanguage()
setupNavigationBar()
setupTableViewDataSource()
subscribeForNotifications()
checkEULA(NSNotification.init(name: "", object: nil))
}
deinit {
self.unsubscribeForNotifications()
}
// MARK: - Setups
private func setupButtons() {
scanCodeButton.setBackgroundImage(Images.BlueHighlighted, forState: .Highlighted)
noCodeButton.setBackgroundImage(Images.GrayHighlighted, forState: .Highlighted)
@@ -53,45 +60,66 @@ class HomeViewController: UIViewController {
}
private func setupTableViewDataSource() {
fetchedResultsController = CoreDataManager.sharedManager.checkInFetchedResultsController()
fetchedResultsController!.delegate = self
do {
try fetchedResultsController!.performFetch()
try fetchedResultsController.performFetch()
} catch {
print(error)
}
}
// MARK: - Reviews
func reviews() {
if !Preferences.termsAccepted {
reviewEULA()
} else {
reviewNewCheckIn()
}
}
func reviewEULA() {
let alertTitle = NSLocalizedString("EULA_title", comment: "")
let alertMessage = NSLocalizedString("EULA_content", comment: "")
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .Alert)
let viewTitle = NSLocalizedString("EULA_view", comment: "")
let viewAction = UIAlertAction(title: viewTitle, style: .Cancel, handler: { action in
UIApplication.sharedApplication().openURL(URLs.EULA)
self.reviewEULA() // Reopen alert
})
let confirmTitle = NSLocalizedString("EULA_confirm", comment: "")
let confirmAction = UIAlertAction(title: confirmTitle, style: .Default, handler: { action in
Preferences.termsAccepted = true
self.reviewNewCheckIn()
})
alertController.addAction(viewAction)
alertController.addAction(confirmAction)
presentViewController(alertController, animated: true, completion: nil)
}
func reviewNewCheckIn() {
guard Preferences.termsAccepted else { return }
guard let urlString = Preferences.newCheckInURL else { return }
guard let checkInData = CheckInData(urlString: urlString) else { return }
checkInController.startCheckIn(checkInData)
}
// MARK: - Notifications
private func subscribeForNotifications() {
private func subscribeForNewCheckInURLNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(HomeViewController.checkEULA(_:)),
name: UIApplicationWillEnterForegroundNotification,
selector: #selector(HomeViewController.newCheckInURLNotification(_:)),
name: Preferences.NotificationType.NewCheckInURLChanged,
object: nil)
}
private func unsubscribeForNotifications() {
private func unsubscribeFromNewCheckInURLNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func checkEULA(notification: NSNotification) {
if !Preferences.acceptedTerms {
let alertTitle = NSLocalizedString("EULA_title", comment: "")
let alertMessage = NSLocalizedString("EULA_content", comment: "")
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .Alert)
let viewTitle = NSLocalizedString("EULA_view", comment: "")
let viewAction = UIAlertAction(title: viewTitle, style: .Cancel, handler: { action in
UIApplication.sharedApplication().openURL(URLs.EULA)
})
let confirmTitle = NSLocalizedString("EULA_confirm", comment: "")
let confirmAction = UIAlertAction(title: confirmTitle, style: .Default, handler: { action in
Preferences.acceptedTerms = true
})
alertController.addAction(viewAction)
alertController.addAction(confirmAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
func newCheckInURLNotification(notification: NSNotification) {
dispatch_async(dispatch_get_main_queue(), {
self.reviewNewCheckIn()
})
}
// MARK: - Actions
@@ -148,13 +176,28 @@ class HomeViewController: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.destinationViewController.isKindOfClass(RegattaViewController)) {
let regattaVC = segue.destinationViewController as! RegattaViewController
let indexPath = self.tableView.indexPathForSelectedRow
regattaVC.checkIn = self.fetchedResultsController?.objectAtIndexPath(indexPath!) as? CheckIn
self.tableView.deselectRowAtIndexPath(indexPath!, animated: true)
guard let regattaVC = segue.destinationViewController as? RegattaViewController else { return }
guard let indexPath = tableView.indexPathForSelectedRow else { return }
guard let regatta = fetchedResultsController.objectAtIndexPath(indexPath) as? Regatta else { return }
regattaVC.regatta = regatta
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
}
// MARK: - Properties
lazy var checkInController: CheckInController = {
let checkInController = CheckInController()
checkInController.delegate = self
return checkInController
}()
lazy var fetchedResultsController: NSFetchedResultsController = {
let fetchedResultsController = CoreDataManager.sharedManager.regattaFetchedResultsController()
fetchedResultsController.delegate = self
return fetchedResultsController
}()
}
// MARK: - UITableViewDataSource
@@ -162,18 +205,18 @@ class HomeViewController: UIViewController {
extension HomeViewController: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController!.sections![section].numberOfObjects
return fetchedResultsController.sections?[section].numberOfObjects ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Regatta") as UITableViewCell!
let cell = tableView.dequeueReusableCellWithIdentifier("Regatta") ?? UITableViewCell()
self.configureCell(cell, atIndexPath: indexPath)
return cell
}
func configureCell(cell: UITableViewCell, atIndexPath indexPath: NSIndexPath) {
let checkIn = fetchedResultsController!.objectAtIndexPath(indexPath) as! CheckIn
cell.textLabel?.text = checkIn.leaderboardName
guard let regatta = fetchedResultsController.objectAtIndexPath(indexPath) as? Regatta else { return }
cell.textLabel?.text = regatta.leaderboard.name
}
}
@@ -224,3 +267,17 @@ extension HomeViewController: NSFetchedResultsControllerDelegate {
}
}
// MARK: - CheckInControllerDelegate
extension HomeViewController: CheckInControllerDelegate {
func showCheckInAlert(checkInController: CheckInController, alertController: UIAlertController) {
presentViewController(alertController, animated: true, completion: nil)
}
func checkInDidEnd(checkInController: CheckInController, withSuccess succeed: Bool) {
Preferences.newCheckInURL = nil
}
}
@@ -1,43 +0,0 @@
//
// LeaderBoardViewController.swift
// SAPTracker
//
// Created by computing on 08/12/14.
// Copyright (c) 2014 com.sap.sailing. All rights reserved.
//
import Foundation
class LeaderBoardViewController: UIViewController, UIWebViewDelegate, UIAlertViewDelegate {
weak var checkIn: CheckIn?
@IBOutlet weak var webView: UIWebView!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let serverUrl = self.checkIn?.serverURL
let leaderboardName = self.checkIn?.leaderboardName?.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url = "\(serverUrl)/gwt/Leaderboard.html?name=\(leaderboardName)&showRaceDetails=false&embedded=true&hideToolbar=true"
webView!.loadRequest(NSURLRequest(URL: NSURL(string: url)!))
}
@IBAction func done(sender: AnyObject) {
presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func webViewDidFinishLoad(webView: UIWebView) {
activityIndicator.stopAnimating()
}
func wwebView(webView: UIWebView, didFailLoadWithError error: NSError) {
activityIndicator.hidden = true
let alertView = UIAlertView(title: NSLocalizedString("Couldn't load about view", comment: ""), message: nil, delegate: nil, cancelButtonTitle: NSLocalizedString("Cancel", comment: ""))
alertView.show()
}
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
@@ -2,7 +2,7 @@
// Leaderboard+CoreDataProperties.swift
// SAPTracker
//
// Created by Raimund Wege on 01.07.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
@@ -14,7 +14,7 @@ import CoreData
extension Leaderboard {
@NSManaged var name: String?
@NSManaged var checkIn: CheckIn?
@NSManaged var name: String
@NSManaged var regatta: Regatta
}
@@ -2,7 +2,7 @@
// Leaderboard.swift
// SAPTracker
//
// Created by Raimund Wege on 30.06.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
@@ -12,6 +12,8 @@ import CoreData
@objc(Leaderboard)
class Leaderboard: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
func nameWithQueryAllowedCharacters() -> String? {
return name.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())
}
}
@@ -0,0 +1,59 @@
//
// LeaderboardViewController.swift
// SAPTracker
//
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
import Foundation
class LeaderboardViewController: UIViewController {
weak var regatta: Regatta?
@IBOutlet weak var webView: UIWebView!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
setupWebView()
}
// MARK: - Setups
private func setupWebView() {
guard let url = regatta?.leaderboardURL() else { return }
webView.loadRequest(NSURLRequest(URL: url))
}
// MARK: - Actions
@IBAction func doneButtonTapped(sender: AnyObject) {
presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
// MARK: - UIWebViewDelegate
extension LeaderboardViewController: UIWebViewDelegate {
func webViewDidFinishLoad(webView: UIWebView) {
activityIndicator.stopAnimating()
}
func webView(webView: UIWebView, didFailLoadWithError error: NSError?) {
activityIndicator.stopAnimating()
// FIXME: - Translation
let alertTitle = NSLocalizedString("Couldn't load leaderboard view", comment: "")
let alertController = UIAlertController(title: alertTitle, message: nil, preferredStyle: .Alert)
let okTitle = NSLocalizedString("OK", comment: "")
let okAction = UIAlertAction(title: okTitle, style: .Default) { (action) in
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(okAction)
presentViewController(alertController, animated: true, completion: nil)
}
}
@@ -10,35 +10,43 @@ import UIKit
class Preferences: NSObject {
struct NotificationType {
static let NewCheckInURLChanged = "NewCheckInURLStringChanged"
}
struct PreferenceKey {
static let BatterySaving = "BatterySaving"
static let LastCheckInURLString = "lastCheckInURLString"
static let AcceptedTerms = "acceptedTerms"
static let NewCheckInURL = "NewCheckInURL"
static let TermsAccepted = "TermsAccepted"
static let UUID = "udid"
}
private static let preferences = NSUserDefaults.standardUserDefaults()
// MARK: - LastCheckInData
// MARK: - NewCheckInURL
class var lastCheckInURLString: String? {
class var newCheckInURL: String? {
get {
return preferences.stringForKey(PreferenceKey.LastCheckInURLString)
return preferences.stringForKey(PreferenceKey.NewCheckInURL)
}
set(value) {
preferences.setObject(value, forKey: PreferenceKey.LastCheckInURLString)
preferences.setObject(value, forKey: PreferenceKey.NewCheckInURL)
preferences.synchronize()
// Send notification
let notification = NSNotification(name: NotificationType.NewCheckInURLChanged, object: self)
NSNotificationQueue.defaultQueue().enqueueNotification(notification, postingStyle: NSPostingStyle.PostASAP)
}
}
// MARK: - AcceptedTerms
// MARK: - TermsAccepted
class var acceptedTerms: Bool {
class var termsAccepted: Bool {
get {
return preferences.boolForKey(PreferenceKey.AcceptedTerms)
return preferences.boolForKey(PreferenceKey.TermsAccepted)
}
set(value) {
preferences.setBool(value, forKey:PreferenceKey.AcceptedTerms)
preferences.setBool(value, forKey:PreferenceKey.TermsAccepted)
preferences.synchronize()
}
}
@@ -1,8 +1,8 @@
//
// CheckIn+CoreDataProperties.swift
// Regatta+CoreDataProperties.swift
// SAPTracker
//
// Created by Raimund Wege on 01.07.16.
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu
@@ -12,19 +12,15 @@
import Foundation
import CoreData
extension CheckIn {
extension Regatta {
@NSManaged var competitorID: String?
@NSManaged var eventID: String?
@NSManaged var teamImageURL: String?
@NSManaged var lastSyncDate: NSTimeInterval
@NSManaged var leaderboardName: String?
@NSManaged var serverURL: String?
@NSManaged var serverURL: String
@NSManaged var teamImageData: NSData?
@NSManaged var teamImageRetry: Bool
@NSManaged var competitor: Competitor?
@NSManaged var event: Event?
@NSManaged var teamImageURL: String?
@NSManaged var competitor: Competitor
@NSManaged var event: Event
@NSManaged var gpsFixes: NSSet?
@NSManaged var leaderboard: Leaderboard?
@NSManaged var leaderboard: Leaderboard
}
@@ -0,0 +1,24 @@
//
// Regatta.swift
// SAPTracker
//
// Created by Raimund Wege on 04.07.16.
// Copyright © 2016 com.sap.sailing. All rights reserved.
//
import Foundation
import CoreData
@objc(Regatta)
class Regatta: NSManagedObject {
func eventURL() -> NSURL? {
return NSURL(string: "\(serverURL)/gwt/Home.html?navigationTab=Regattas#EventPlace:eventId=\(event.eventID)")
}
func leaderboardURL() -> NSURL? {
guard let name = leaderboard.nameWithQueryAllowedCharacters() else { return nil }
return NSURL(string: "\(serverURL)/gwt/Leaderboard.html?name=\(name)&showRaceDetails=false&embedded=true&hideToolbar=true")
}
}
@@ -37,17 +37,18 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
@IBOutlet weak var startTrackingButton: UIButton!
@IBOutlet weak var announcementsLabel: UILabel!
var checkIn: CheckIn!
var regatta: Regatta!
var requestManager: RequestManager!
var countdownTimer: NSTimer?
override func viewDidLoad() {
super.viewDidLoad()
requestManager = RequestManager(baseURLString: checkIn!.serverURL)
requestManager = RequestManager(baseURLString: regatta.serverURL)
setupButtons()
setupCompetitor()
setupCountdownTimer()
setupNavigationBar()
setupImageSourceTypes()
setupTeamImage()
}
@@ -61,9 +62,9 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
}
private func setupCompetitor() {
competitorNameLabel.text = checkIn?.competitor?.name ?? ""
competitorFlagImageView.image = UIImage(named: checkIn?.competitor?.countryCode ?? "")
competitorSailLabel.text = checkIn?.competitor?.sailID ?? ""
competitorNameLabel.text = regatta.competitor.name
competitorFlagImageView.image = UIImage(named: regatta.competitor.countryCode)
competitorSailLabel.text = regatta.competitor.sailID
}
private func setupCountdownTimer() {
@@ -87,11 +88,15 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
}
}
private func setupNavigationBar() {
navigationItem.title = regatta.leaderboard.name
}
private func setupTeamImage() {
if checkIn.teamImageData != nil {
setupTeamImageWithImageData(checkIn.teamImageData)
} else if checkIn.teamImageURL != nil {
setupTeamImageWithURL(checkIn.teamImageURL)
if regatta.teamImageData != nil {
setupTeamImageWithImageData(regatta.teamImageData)
} else if regatta.teamImageURL != nil {
setupTeamImageWithURL(regatta.teamImageURL)
} else {
setupTeamImageFinished()
}
@@ -115,8 +120,8 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
private func setupTeamImageWithURLSuccess(image: UIImage) {
teamImageView.image = image
checkIn.teamImageData = UIImageJPEGRepresentation(image, 0.8)
checkIn.teamImageRetry = false
regatta.teamImageData = UIImageJPEGRepresentation(image, 0.8)
regatta.teamImageRetry = false
CoreDataManager.sharedManager.saveContext()
setupTeamImageFinished()
}
@@ -126,9 +131,9 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
}
private func setupTeamImageFinished() {
if checkIn.teamImageData != nil {
if regatta.teamImageData != nil {
teamImageAddButton.hidden = true
teamImageEditButton.hidden = checkIn.teamImageRetry
teamImageEditButton.hidden = regatta.teamImageRetry
teamImageRetryButton.hidden = !teamImageEditButton.hidden
} else {
teamImageAddButton.hidden = false
@@ -140,23 +145,21 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
// MARK: - Refresh
func refreshCountdown() {
if let event = checkIn.event {
if event.startDate - NSDate().timeIntervalSince1970 > 0 {
regattaStartLabel.text = NSLocalizedString("Regatta will start in", comment: "")
let duration = event.startDate - NSDate().timeIntervalSince1970
let days = Int(duration / (60 * 60 * 24))
let hours = Int(duration / (60 * 60)) - (days * 24)
let minutes = Int(duration / 60) - (days * 24 * 60) - (hours * 60)
countdownDaysLabel.text = String(format: "%02d", days)
countdownHoursLabel.text = String(format: "%02d", hours)
countdownMinutesLabel.text = String(format: "%02d", minutes)
countdownView.hidden = false
countdownViewHeight.constant = 60
} else {
regattaStartLabel.text = NSLocalizedString("Regatta in progress", comment: "")
countdownView.hidden = true
countdownViewHeight.constant = 0
}
if regatta.event.startDate - NSDate().timeIntervalSince1970 > 0 {
regattaStartLabel.text = NSLocalizedString("Regatta will start in", comment: "")
let duration = regatta.event.startDate - NSDate().timeIntervalSince1970
let days = Int(duration / (60 * 60 * 24))
let hours = Int(duration / (60 * 60)) - (days * 24)
let minutes = Int(duration / 60) - (days * 24 * 60) - (hours * 60)
countdownDaysLabel.text = String(format: "%02d", days)
countdownHoursLabel.text = String(format: "%02d", hours)
countdownMinutesLabel.text = String(format: "%02d", minutes)
countdownView.hidden = false
countdownViewHeight.constant = 60
} else {
regattaStartLabel.text = NSLocalizedString("Regatta in progress", comment: "")
countdownView.hidden = true
countdownViewHeight.constant = 0
}
}
@@ -171,14 +174,12 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
}
@IBAction func teamImageRetryButtonTapped(sender: AnyObject) {
postTeamImageData(checkIn.teamImageData)
postTeamImageData(regatta.teamImageData)
}
@IBAction func eventButtonTapped(sender: UIButton) {
let serverURL = checkIn.serverURL!
let eventID = checkIn.eventID!
let urlString = "\(serverURL)/gwt/Home.html?navigationTab=Regattas#EventPlace:eventId=\(eventID)"
let url = NSURL(string: urlString)!
// FIXME: - Alert?
guard let url = regatta.eventURL() else { return }
UIApplication.sharedApplication().openURL(url)
}
@@ -259,12 +260,12 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
}
func preformCheckOut() {
requestManager.postCheckOut(checkIn.leaderboardName,
competitorId: checkIn.competitorID,
requestManager.postCheckOut(regatta.leaderboard.name,
competitorId: regatta.competitor.competitorID,
success: { (operation, responseObject) in },
failure: { (operation, error) in
})
CoreDataManager.sharedManager.deleteCheckIn(self.checkIn!)
CoreDataManager.sharedManager.deleteRegatta(regatta!)
CoreDataManager.sharedManager.saveContext()
navigationController!.popViewControllerAnimated(true)
}
@@ -275,7 +276,7 @@ class RegattaViewController : UIViewController, UINavigationControllerDelegate {
if (segue.identifier == "Tracking") {
let nc = segue.destinationViewController as! UINavigationController
let vc = nc.viewControllers[0] as! TrackingViewController
vc.checkIn = checkIn
vc.regatta = regatta
}
}
@@ -300,14 +301,14 @@ extension RegattaViewController: UIImagePickerControllerDelegate {
teamImageAddButton.hidden = true
teamImageEditButton.hidden = false
teamImageRetryButton.hidden = true
checkIn.teamImageData = UIImageJPEGRepresentation(image, 0.8)
regatta.teamImageData = UIImageJPEGRepresentation(image, 0.8)
CoreDataManager.sharedManager.saveContext()
postTeamImageData(checkIn.teamImageData)
postTeamImageData(regatta.teamImageData)
}
private func postTeamImageData(imageData: NSData!) {
requestManager.postTeamImageData(imageData,
competitorId: checkIn.competitorID,
competitorId: regatta.competitor.competitorID,
success: { (responseObject) in self.postTeamImageSuccess(responseObject) },
failure: { (error) in self.postTeamImageFailure(error) })
}
@@ -317,8 +318,8 @@ extension RegattaViewController: UIImagePickerControllerDelegate {
// Save image URL and upload success
let teamImageDictionary = responseObject as! [String: AnyObject]
let teamImageURL = (teamImageDictionary[TeamImageKeys.TeamImageURL]) as! String
checkIn.teamImageURL = teamImageURL
checkIn.teamImageRetry = false
regatta.teamImageURL = teamImageURL
regatta.teamImageRetry = false
CoreDataManager.sharedManager.saveContext()
// Setup team image
@@ -328,7 +329,7 @@ extension RegattaViewController: UIImagePickerControllerDelegate {
private func postTeamImageFailure(error: AnyObject) {
// Save image upload failure
checkIn.teamImageRetry = true
regatta.teamImageRetry = true
CoreDataManager.sharedManager.saveContext()
// Show alert
@@ -36,11 +36,11 @@ class RequestManager: NSObject {
static let IOS = "iOS"
}
let baseURLString: String!
let manager: AFHTTPRequestOperationManager!
let sessionManager: AFURLSessionManager!
let baseURLString: String
let manager: AFHTTPRequestOperationManager
let sessionManager: AFURLSessionManager
init(baseURLString: String!) {
init(baseURLString: String = "") {
self.baseURLString = baseURLString
manager = AFHTTPRequestOperationManager(baseURL: NSURL(string: baseURLString))
manager.requestSerializer = AFJSONRequestSerializer() as AFHTTPRequestSerializer
@@ -54,7 +54,8 @@ class RequestManager: NSObject {
func getEvent(eventID: String!,
success: (AFHTTPRequestOperation!, AnyObject!) -> Void,
failure: (AFHTTPRequestOperation!, AnyObject!) -> Void) {
failure: (AFHTTPRequestOperation!, AnyObject!) -> Void)
{
let encodedEventID = eventID.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLPathAllowedCharacterSet()) ?? ""
let urlString = "\(basePathString)/events/\(encodedEventID)"
manager.GET(urlString, parameters: nil, success: success, failure: failure)
@@ -14,7 +14,7 @@ class ScanViewController: UIViewController {
@IBOutlet weak var previewView: UIView!
@IBOutlet weak var targetImageView: UIImageView!
private var checkInController: CheckInController?
private let checkInController = CheckInController()
private var session: AVCaptureSession!
private var output: AVCaptureMetadataOutput!
@@ -22,36 +22,41 @@ class ScanViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setupSession()
setupCheckInController()
setupSession()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.startScanning()
startScanning()
}
// MARK: - Setups
private func setupCheckInController() {
checkInController.delegate = self
}
private func setupSession() {
self.session = AVCaptureSession()
self.output = AVCaptureMetadataOutput()
session = AVCaptureSession()
output = AVCaptureMetadataOutput()
do {
let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
let input = try AVCaptureDeviceInput(device: device);
self.output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
self.session.canSetSessionPreset(AVCaptureSessionPresetHigh)
output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
session.canSetSessionPreset(AVCaptureSessionPresetHigh)
if session.canAddInput(input) {
session.addInput(input)
}
if session.canAddOutput(output) {
session.addOutput(output)
}
self.output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
self.previewLayer.frame = previewView.bounds
self.previewLayer.position = CGPointMake(CGRectGetMidX(previewView.bounds), CGRectGetMidY(previewView.bounds))
self.previewView.layer.addSublayer(previewLayer)
output.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
previewLayer.frame = previewView.bounds
previewLayer.position = CGPointMake(CGRectGetMidX(previewView.bounds), CGRectGetMidY(previewView.bounds))
previewView.layer.addSublayer(previewLayer)
} catch {
print(error)
}
@@ -75,14 +80,14 @@ class ScanViewController: UIViewController {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.session.startRunning()
})
self.targetImageView.image = UIImage(named: "scan_white")
targetImageView.image = UIImage(named: "scan_white")
}
private func stopScanning() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
self.session.stopRunning()
})
self.targetImageView.image = UIImage(named: "scan_green")
targetImageView.image = UIImage(named: "scan_green")
}
}
@@ -97,9 +102,9 @@ extension ScanViewController: CheckInControllerDelegate {
func checkInDidEnd(checkInController: CheckInController, withSuccess succeed: Bool) {
if succeed {
self.navigationController?.popViewControllerAnimated(true)
navigationController?.popViewControllerAnimated(true)
} else {
self.startScanning()
startScanning()
}
}
@@ -116,9 +121,8 @@ extension ScanViewController: AVCaptureMetadataOutputObjectsDelegate {
if metadataObjects.count > 0 {
let metadataObject: AVMetadataMachineReadableCodeObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if let checkInData = CheckInData(urlString: metadataObject.stringValue) {
self.stopScanning()
checkInController = CheckInController(checkInData: checkInData, delegate: self)
checkInController!.startCheckIn()
stopScanning()
checkInController.startCheckIn(checkInData)
} else {
let alertTitle = NSLocalizedString("Incorrect QR Code", comment: "")
let alertController = UIAlertController(title: alertTitle, message: "", preferredStyle: .Alert)
@@ -15,14 +15,14 @@ class SendGPSFixesController: NSObject {
static let BatterySaving: NSTimeInterval = 30
}
private let checkIn: CheckIn!
private let regatta: Regatta!
private let requestManager: RequestManager!
private var isRunning: Bool = false
init(checkIn: CheckIn) {
self.checkIn = checkIn
requestManager = RequestManager(baseURLString: checkIn.serverURL)
init(regatta: Regatta) {
self.regatta = regatta
requestManager = RequestManager(baseURLString: regatta.serverURL)
super.init()
}
@@ -47,7 +47,7 @@ class SendGPSFixesController: NSObject {
dispatch_async(dispatch_get_main_queue(), {
if let userInfo = notification.userInfo {
if userInfo[LocationManager.UserInfo.IsValid] as? Bool ?? false {
let gpsFix = CoreDataManager.sharedManager.newGPSFix(self.checkIn)
let gpsFix = CoreDataManager.sharedManager.newGPSFix(self.regatta)
gpsFix.course = userInfo[LocationManager.UserInfo.Course] as! Double
gpsFix.latitude = userInfo[LocationManager.UserInfo.Latitude] as! Double
gpsFix.longitude = userInfo[LocationManager.UserInfo.Longitude] as! Double
@@ -72,10 +72,8 @@ class SendGPSFixesController: NSObject {
func sendGPSFixes() {
var gpsFixes = [GPSFix]()
if checkIn.gpsFixes != nil {
checkIn.gpsFixes!.forEach { (gpsFix) in
gpsFixes.append(gpsFix as! GPSFix)
}
if regatta.gpsFixes != nil {
regatta.gpsFixes!.forEach { (gpsFix) in gpsFixes.append(gpsFix as! GPSFix) }
}
if gpsFixes.count > 0 {
log("\(gpsFixes.count) GPS fixes will be sent")
@@ -10,7 +10,7 @@ import Foundation
class TrackingViewController : UIViewController {
var checkIn: CheckIn?
var regatta: Regatta?
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableViewHeight: NSLayoutConstraint!
@@ -18,8 +18,7 @@ class TrackingViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupButtons()
setupNavigationBarTitle()
setups()
}
override func updateViewConstraints() {
@@ -29,12 +28,17 @@ class TrackingViewController : UIViewController {
// MARK: - Setups
private func setups() {
setupButtons()
setupNavigationBar()
}
private func setupButtons() {
stopTrackingButton.setBackgroundImage(Images.RedHighlighted, forState: .Highlighted)
}
private func setupNavigationBarTitle() {
navigationItem.title = checkIn?.leaderboardName
private func setupNavigationBar() {
navigationItem.title = regatta?.leaderboard.name
}
// MARK: - Actions
@@ -85,7 +89,7 @@ extension TrackingViewController: UITableViewDataSource {
let cell = tableView.dequeueReusableCellWithIdentifier(TrackingViewController.Rows[indexPath.row]) ?? UITableViewCell()
if (cell.isKindOfClass(CachedFixesTrackingTableViewCell)) {
let cachedFixesCell = cell as! CachedFixesTrackingTableViewCell
cachedFixesCell.checkIn = checkIn
cachedFixesCell.regatta = regatta
}
return cell
}